From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/tdi/acd/acddefs.h | 100 + private/ntos/tdi/acd/api.c | 1556 +++++++++ private/ntos/tdi/acd/debug.h | 74 + private/ntos/tdi/acd/makefile | 6 + private/ntos/tdi/acd/mem.c | 289 ++ private/ntos/tdi/acd/mem.h | 41 + private/ntos/tdi/acd/ntdisp.c | 411 +++ private/ntos/tdi/acd/ntinit.c | 223 ++ private/ntos/tdi/acd/rasacd.rc | 12 + private/ntos/tdi/acd/request.c | 303 ++ private/ntos/tdi/acd/request.h | 31 + private/ntos/tdi/acd/sources | 46 + private/ntos/tdi/acd/table.c | 286 ++ private/ntos/tdi/acd/table.h | 77 + private/ntos/tdi/acd/timer.c | 145 + private/ntos/tdi/dirs | 32 + private/ntos/tdi/irda/dirs | 10 + private/ntos/tdi/irda/driver/irda.c | 132 + private/ntos/tdi/irda/driver/irndis.c | 706 ++++ private/ntos/tdi/irda/driver/makefile | 7 + private/ntos/tdi/irda/driver/sources | 15 + private/ntos/tdi/irda/inc/af_irda.h | 100 + private/ntos/tdi/irda/inc/decdirda.h | 174 + private/ntos/tdi/irda/inc/irda.h | 857 +++++ private/ntos/tdi/irda/inc/irdalink.h | 34 + private/ntos/tdi/irda/inc/irerr.h | 110 + private/ntos/tdi/irda/inc/irlap.h | 66 + private/ntos/tdi/irda/inc/irlaplog.h | 44 + private/ntos/tdi/irda/inc/irlmp.h | 60 + private/ntos/tdi/irda/inc/irmac.h | 32 + private/ntos/tdi/irda/inc/oscfg.h | 62 + private/ntos/tdi/irda/inc/tmp.h | 31 + private/ntos/tdi/irda/irlap/irlap.c | 4519 ++++++++++++++++++++++++ private/ntos/tdi/irda/irlap/irlapio.c | 900 +++++ private/ntos/tdi/irda/irlap/irlapio.h | 32 + private/ntos/tdi/irda/irlap/irlaplog.c | 265 ++ private/ntos/tdi/irda/irlap/irlapp.h | 299 ++ private/ntos/tdi/irda/irlap/makefile | 6 + private/ntos/tdi/irda/irlap/sources | 23 + private/ntos/tdi/isn/dirs | 34 + private/ntos/tdi/isn/flt/debug.c | 5 + private/ntos/tdi/isn/flt/debug.h | 42 + private/ntos/tdi/isn/flt/dirs | 22 + private/ntos/tdi/isn/flt/driver.c | 443 +++ private/ntos/tdi/isn/flt/filter.c | 856 +++++ private/ntos/tdi/isn/flt/filter.h | 288 ++ private/ntos/tdi/isn/flt/flt.mak | 497 +++ private/ntos/tdi/isn/flt/fwdbind.c | 161 + private/ntos/tdi/isn/flt/fwdbind.h | 69 + private/ntos/tdi/isn/flt/mp/makefile | 6 + private/ntos/tdi/isn/flt/mp/sources | 29 + private/ntos/tdi/isn/flt/nwlnkflt.rc | 12 + private/ntos/tdi/isn/flt/precomp.h | 50 + private/ntos/tdi/isn/flt/sources.inc | 51 + private/ntos/tdi/isn/flt/up/makefile | 6 + private/ntos/tdi/isn/flt/up/sources | 31 + private/ntos/tdi/isn/fwd/ddreqs.c | 220 ++ private/ntos/tdi/isn/fwd/ddreqs.h | 149 + private/ntos/tdi/isn/fwd/debug.c | 7 + private/ntos/tdi/isn/fwd/debug.h | 69 + private/ntos/tdi/isn/fwd/dirs | 22 + private/ntos/tdi/isn/fwd/driver.c | 1157 ++++++ private/ntos/tdi/isn/fwd/driver.h | 102 + private/ntos/tdi/isn/fwd/filterif.c | 211 ++ private/ntos/tdi/isn/fwd/filterif.h | 126 + private/ntos/tdi/isn/fwd/fwd.mak | 2427 +++++++++++++ private/ntos/tdi/isn/fwd/fwddefs.h | 125 + private/ntos/tdi/isn/fwd/ipxbind.c | 462 +++ private/ntos/tdi/isn/fwd/ipxbind.h | 75 + private/ntos/tdi/isn/fwd/lineind.c | 323 ++ private/ntos/tdi/isn/fwd/lineind.h | 116 + private/ntos/tdi/isn/fwd/mp/makefile | 6 + private/ntos/tdi/isn/fwd/mp/sources | 29 + private/ntos/tdi/isn/fwd/netbios.c | 311 ++ private/ntos/tdi/isn/fwd/netbios.h | 134 + private/ntos/tdi/isn/fwd/nwlnkfwd.rc | 12 + private/ntos/tdi/isn/fwd/packets.c | 528 +++ private/ntos/tdi/isn/fwd/packets.h | 464 +++ private/ntos/tdi/isn/fwd/precomp.h | 61 + private/ntos/tdi/isn/fwd/rcvind.c | 825 +++++ private/ntos/tdi/isn/fwd/rcvind.h | 180 + private/ntos/tdi/isn/fwd/registry.c | 273 ++ private/ntos/tdi/isn/fwd/registry.h | 61 + private/ntos/tdi/isn/fwd/rwlock.h | 179 + private/ntos/tdi/isn/fwd/send.c | 1253 +++++++ private/ntos/tdi/isn/fwd/send.h | 220 ++ private/ntos/tdi/isn/fwd/sources.inc | 61 + private/ntos/tdi/isn/fwd/tables.c | 1512 ++++++++ private/ntos/tdi/isn/fwd/tables.h | 708 ++++ private/ntos/tdi/isn/fwd/up/makefile | 6 + private/ntos/tdi/isn/fwd/up/sources | 31 + private/ntos/tdi/isn/inc/bind.h | 843 +++++ private/ntos/tdi/isn/inc/ioctls.h | 155 + private/ntos/tdi/isn/inc/ipxfltif.h | 151 + private/ntos/tdi/isn/inc/isn.h | 41 + private/ntos/tdi/isn/ipx/action.c | 2921 ++++++++++++++++ private/ntos/tdi/isn/ipx/adapter.c | 804 +++++ private/ntos/tdi/isn/ipx/address.c | 1882 ++++++++++ private/ntos/tdi/isn/ipx/config.c | 1715 +++++++++ private/ntos/tdi/isn/ipx/config.h | 132 + private/ntos/tdi/isn/ipx/device.c | 612 ++++ private/ntos/tdi/isn/ipx/dirs | 22 + private/ntos/tdi/isn/ipx/driver.c | 4447 +++++++++++++++++++++++ private/ntos/tdi/isn/ipx/event.c | 143 + private/ntos/tdi/isn/ipx/ind.c | 4417 +++++++++++++++++++++++ private/ntos/tdi/isn/ipx/internal.c | 1342 +++++++ private/ntos/tdi/isn/ipx/ipx.rc | 12 + private/ntos/tdi/isn/ipx/ipxprocs.h | 1675 +++++++++ private/ntos/tdi/isn/ipx/ipxtypes.h | 2144 ++++++++++++ private/ntos/tdi/isn/ipx/isnipx.h | 554 +++ private/ntos/tdi/isn/ipx/loopback.c | 280 ++ private/ntos/tdi/isn/ipx/mac.c | 3940 +++++++++++++++++++++ private/ntos/tdi/isn/ipx/mac.h | 44 + private/ntos/tdi/isn/ipx/mp/makefile | 6 + private/ntos/tdi/isn/ipx/mp/nwlnkipx.prf | 89 + private/ntos/tdi/isn/ipx/mp/sources | 29 + private/ntos/tdi/isn/ipx/ndis.c | 2381 +++++++++++++ private/ntos/tdi/isn/ipx/nwlnkipx.ini | 191 + private/ntos/tdi/isn/ipx/nwlnkipx.rc | 12 + private/ntos/tdi/isn/ipx/packet.c | 1560 +++++++++ private/ntos/tdi/isn/ipx/precomp.h | 45 + private/ntos/tdi/isn/ipx/query.c | 297 ++ private/ntos/tdi/isn/ipx/receive.c | 493 +++ private/ntos/tdi/isn/ipx/rip.c | 2700 ++++++++++++++ private/ntos/tdi/isn/ipx/rt.c | 1311 +++++++ private/ntos/tdi/isn/ipx/send.c | 2364 +++++++++++++ private/ntos/tdi/isn/ipx/sources.inc | 75 + private/ntos/tdi/isn/ipx/up/makefile | 6 + private/ntos/tdi/isn/ipx/up/nwlnkipx.prf | 89 + private/ntos/tdi/isn/ipx/up/sources | 29 + private/ntos/tdi/isn/ipxroute/ipxroute.c | 1822 ++++++++++ private/ntos/tdi/isn/ipxroute/ipxroute.rc | 13 + private/ntos/tdi/isn/ipxroute/ipxrtmsg.mc | 252 ++ private/ntos/tdi/isn/ipxroute/makefile | 6 + private/ntos/tdi/isn/ipxroute/makefile.inc | 13 + private/ntos/tdi/isn/ipxroute/sources | 36 + private/ntos/tdi/isn/ipxroute/utils.h | 55 + private/ntos/tdi/isn/isnext/cteext.c | 155 + private/ntos/tdi/isn/isnext/cteext.h | 33 + private/ntos/tdi/isn/isnext/daytona/makefile | 6 + private/ntos/tdi/isn/isnext/daytona/sources | 45 + private/ntos/tdi/isn/isnext/dirs | 25 + private/ntos/tdi/isn/isnext/ipxext.c | 1989 +++++++++++ private/ntos/tdi/isn/isnext/ipxext.h | 16 + private/ntos/tdi/isn/isnext/isnext.c | 345 ++ private/ntos/tdi/isn/isnext/isnext.def | 33 + private/ntos/tdi/isn/isnext/isnext.h | 306 ++ private/ntos/tdi/isn/isnext/isnext.rc | 12 + private/ntos/tdi/isn/isnext/nbext.c | 873 +++++ private/ntos/tdi/isn/isnext/precomp.h | 41 + private/ntos/tdi/isn/isnext/spxext.c | 1035 ++++++ private/ntos/tdi/isn/isnext/spxext.h | 16 + private/ntos/tdi/isn/isnext/traverse.c | 419 +++ private/ntos/tdi/isn/isnext/traverse.h | 55 + private/ntos/tdi/isn/nb/action.c | 221 ++ private/ntos/tdi/isn/nb/address.c | 2406 +++++++++++++ private/ntos/tdi/isn/nb/autodial.c | 526 +++ private/ntos/tdi/isn/nb/bind.c | 594 ++++ private/ntos/tdi/isn/nb/cache.c | 2746 +++++++++++++++ private/ntos/tdi/isn/nb/config.c | 661 ++++ private/ntos/tdi/isn/nb/config.h | 70 + private/ntos/tdi/isn/nb/connect.c | 3628 +++++++++++++++++++ private/ntos/tdi/isn/nb/datagram.c | 1089 ++++++ private/ntos/tdi/isn/nb/device.c | 461 +++ private/ntos/tdi/isn/nb/dirs | 22 + private/ntos/tdi/isn/nb/driver.c | 1794 ++++++++++ private/ntos/tdi/isn/nb/event.c | 117 + private/ntos/tdi/isn/nb/frame.c | 1096 ++++++ private/ntos/tdi/isn/nb/isnnb.h | 787 +++++ private/ntos/tdi/isn/nb/mp/makefile | 6 + private/ntos/tdi/isn/nb/mp/sources | 29 + private/ntos/tdi/isn/nb/nbcount/makefile | 6 + private/ntos/tdi/isn/nb/nbcount/nbcount.c | 177 + private/ntos/tdi/isn/nb/nbcount/nbcount.rc | 11 + private/ntos/tdi/isn/nb/nbcount/sources | 29 + private/ntos/tdi/isn/nb/nbiprocs.h | 1533 ++++++++ private/ntos/tdi/isn/nb/nbitypes.h | 1511 ++++++++ private/ntos/tdi/isn/nb/nwlnknb.ini | 43 + private/ntos/tdi/isn/nb/nwlnknb.rc | 12 + private/ntos/tdi/isn/nb/packet.c | 1482 ++++++++ private/ntos/tdi/isn/nb/precomp.h | 42 + private/ntos/tdi/isn/nb/query.c | 1817 ++++++++++ private/ntos/tdi/isn/nb/receive.c | 1307 +++++++ private/ntos/tdi/isn/nb/send.c | 2886 +++++++++++++++ private/ntos/tdi/isn/nb/session.c | 2450 +++++++++++++ private/ntos/tdi/isn/nb/sources.inc | 69 + private/ntos/tdi/isn/nb/timer.c | 1233 +++++++ private/ntos/tdi/isn/nb/up/makefile | 6 + private/ntos/tdi/isn/nb/up/sources | 29 + private/ntos/tdi/isn/rip/debug.h | 62 + private/ntos/tdi/isn/rip/dirs | 22 + private/ntos/tdi/isn/rip/driver.c | 1023 ++++++ private/ntos/tdi/isn/rip/driver.h | 150 + private/ntos/tdi/isn/rip/globals.c | 86 + private/ntos/tdi/isn/rip/globals.h | 369 ++ private/ntos/tdi/isn/rip/init.c | 174 + private/ntos/tdi/isn/rip/ipxbind.c | 191 + private/ntos/tdi/isn/rip/lineind.c | 430 +++ private/ntos/tdi/isn/rip/mp/makefile | 6 + private/ntos/tdi/isn/rip/mp/sources | 29 + private/ntos/tdi/isn/rip/nbproc.c | 38 + private/ntos/tdi/isn/rip/netbios.c | 155 + private/ntos/tdi/isn/rip/nicman.c | 640 ++++ private/ntos/tdi/isn/rip/nwlnkrip.rc | 12 + private/ntos/tdi/isn/rip/packet.h | 81 + private/ntos/tdi/isn/rip/rcvind.c | 448 +++ private/ntos/tdi/isn/rip/rcvpkt.c | 833 +++++ private/ntos/tdi/isn/rip/registry.c | 258 ++ private/ntos/tdi/isn/rip/ripaux.c | 455 +++ private/ntos/tdi/isn/rip/ripproc.c | 745 ++++ private/ntos/tdi/isn/rip/ripsend.c | 1176 +++++++ private/ntos/tdi/isn/rip/riptimer.c | 221 ++ private/ntos/tdi/isn/rip/route.c | 179 + private/ntos/tdi/isn/rip/rtdefs.h | 327 ++ private/ntos/tdi/isn/rip/rttest/makefile | 6 + private/ntos/tdi/isn/rip/rttest/nwsap.h | 75 + private/ntos/tdi/isn/rip/rttest/rttest.c | 426 +++ private/ntos/tdi/isn/rip/rttest/sources | 42 + private/ntos/tdi/isn/rip/rttest/utils.h | 55 + private/ntos/tdi/isn/rip/send.c | 524 +++ private/ntos/tdi/isn/rip/sources.inc | 60 + private/ntos/tdi/isn/rip/start.c | 193 + private/ntos/tdi/isn/rip/stubs.c | 38 + private/ntos/tdi/isn/rip/timer.c | 140 + private/ntos/tdi/isn/rip/up/makefile | 6 + private/ntos/tdi/isn/rip/up/sources | 29 + private/ntos/tdi/isn/rip/utils.h | 77 + private/ntos/tdi/isn/sockhelp/makefile | 6 + private/ntos/tdi/isn/sockhelp/sources | 32 + private/ntos/tdi/isn/sockhelp/wshelper.c | 1778 ++++++++++ private/ntos/tdi/isn/sockhelp/wshisn.c | 323 ++ private/ntos/tdi/isn/sockhelp/wshisn.def | 21 + private/ntos/tdi/isn/sockhelp/wshisn.rc | 11 + private/ntos/tdi/isn/sockhelp/wshutil.c | 189 + private/ntos/tdi/isn/spx/dirs | 22 + private/ntos/tdi/isn/spx/globals.c | 87 + private/ntos/tdi/isn/spx/h/fwddecls.h | 28 + private/ntos/tdi/isn/spx/h/globals.h | 67 + private/ntos/tdi/isn/spx/h/isnspx.h | 363 ++ private/ntos/tdi/isn/spx/h/spxaddr.h | 426 +++ private/ntos/tdi/isn/spx/h/spxbind.h | 32 + private/ntos/tdi/isn/spx/h/spxconn.h | 1666 +++++++++ private/ntos/tdi/isn/spx/h/spxdev.h | 204 ++ private/ntos/tdi/isn/spx/h/spxerror.h | 246 ++ private/ntos/tdi/isn/spx/h/spxmem.h | 142 + private/ntos/tdi/isn/spx/h/spxntdef.h | 72 + private/ntos/tdi/isn/spx/h/spxpkt.h | 466 +++ private/ntos/tdi/isn/spx/h/spxquery.h | 54 + private/ntos/tdi/isn/spx/h/spxrecv.h | 91 + private/ntos/tdi/isn/spx/h/spxreg.h | 65 + private/ntos/tdi/isn/spx/h/spxsend.h | 34 + private/ntos/tdi/isn/spx/h/spxtimer.h | 101 + private/ntos/tdi/isn/spx/h/spxutils.h | 178 + private/ntos/tdi/isn/spx/mp/makefile | 6 + private/ntos/tdi/isn/spx/mp/sources | 29 + private/ntos/tdi/isn/spx/nwlnkspx.rc | 12 + private/ntos/tdi/isn/spx/precomp.h | 1 + private/ntos/tdi/isn/spx/sources.inc | 65 + private/ntos/tdi/isn/spx/spxaddr.c | 1729 +++++++++ private/ntos/tdi/isn/spx/spxbind.c | 602 ++++ private/ntos/tdi/isn/spx/spxconn.c | 3851 ++++++++++++++++++++ private/ntos/tdi/isn/spx/spxcpkt.c | 4131 ++++++++++++++++++++++ private/ntos/tdi/isn/spx/spxcutil.c | 1736 +++++++++ private/ntos/tdi/isn/spx/spxdev.c | 242 ++ private/ntos/tdi/isn/spx/spxdrvr.c | 1008 ++++++ private/ntos/tdi/isn/spx/spxerror.c | 316 ++ private/ntos/tdi/isn/spx/spxmem.c | 897 +++++ private/ntos/tdi/isn/spx/spxpkt.c | 1594 +++++++++ private/ntos/tdi/isn/spx/spxquery.c | 259 ++ private/ntos/tdi/isn/spx/spxrecv.c | 2839 +++++++++++++++ private/ntos/tdi/isn/spx/spxreg.c | 400 +++ private/ntos/tdi/isn/spx/spxsend.c | 262 ++ private/ntos/tdi/isn/spx/spxtimer.c | 637 ++++ private/ntos/tdi/isn/spx/spxutils.c | 484 +++ private/ntos/tdi/isn/spx/up/makefile | 6 + private/ntos/tdi/isn/spx/up/sources | 29 + private/ntos/tdi/isnp/dirs | 28 + private/ntos/tdi/isnp/inc/bind.h | 563 +++ private/ntos/tdi/isnp/inc/ioctls.h | 155 + private/ntos/tdi/isnp/inc/isn.h | 41 + private/ntos/tdi/isnp/ipx/action.c | 1802 ++++++++++ private/ntos/tdi/isnp/ipx/adapter.c | 636 ++++ private/ntos/tdi/isnp/ipx/address.c | 1843 ++++++++++ private/ntos/tdi/isnp/ipx/config.c | 1715 +++++++++ private/ntos/tdi/isnp/ipx/config.h | 132 + private/ntos/tdi/isnp/ipx/device.c | 599 ++++ private/ntos/tdi/isnp/ipx/dirs | 22 + private/ntos/tdi/isnp/ipx/driver.c | 4219 ++++++++++++++++++++++ private/ntos/tdi/isnp/ipx/event.c | 143 + private/ntos/tdi/isnp/ipx/ind.c | 4047 +++++++++++++++++++++ private/ntos/tdi/isnp/ipx/internal.c | 1233 +++++++ private/ntos/tdi/isnp/ipx/ipxprocs.h | 1525 ++++++++ private/ntos/tdi/isnp/ipx/ipxtypes.h | 1999 +++++++++++ private/ntos/tdi/isnp/ipx/isnipx.h | 531 +++ private/ntos/tdi/isnp/ipx/loopback.c | 280 ++ private/ntos/tdi/isnp/ipx/mac.c | 3793 ++++++++++++++++++++ private/ntos/tdi/isnp/ipx/mac.h | 44 + private/ntos/tdi/isnp/ipx/mp/makefile | 6 + private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf | 89 + private/ntos/tdi/isnp/ipx/mp/sources | 29 + private/ntos/tdi/isnp/ipx/ndis.c | 2204 ++++++++++++ private/ntos/tdi/isnp/ipx/nwlnkipx.ini | 191 + private/ntos/tdi/isnp/ipx/nwlnkipx.rc | 12 + private/ntos/tdi/isnp/ipx/packet.c | 1560 +++++++++ private/ntos/tdi/isnp/ipx/precomp.h | 44 + private/ntos/tdi/isnp/ipx/query.c | 297 ++ private/ntos/tdi/isnp/ipx/receive.c | 466 +++ private/ntos/tdi/isnp/ipx/rip.c | 2655 ++++++++++++++ private/ntos/tdi/isnp/ipx/send.c | 1651 +++++++++ private/ntos/tdi/isnp/ipx/sources.inc | 74 + private/ntos/tdi/isnp/ipx/up/makefile | 6 + private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf | 89 + private/ntos/tdi/isnp/ipx/up/sources | 29 + private/ntos/tdi/isnp/nb/action.c | 221 ++ private/ntos/tdi/isnp/nb/address.c | 2406 +++++++++++++ private/ntos/tdi/isnp/nb/autodial.c | 526 +++ private/ntos/tdi/isnp/nb/bind.c | 593 ++++ private/ntos/tdi/isnp/nb/cache.c | 2746 +++++++++++++++ private/ntos/tdi/isnp/nb/config.c | 661 ++++ private/ntos/tdi/isnp/nb/config.h | 70 + private/ntos/tdi/isnp/nb/connect.c | 3628 +++++++++++++++++++ private/ntos/tdi/isnp/nb/datagram.c | 1089 ++++++ private/ntos/tdi/isnp/nb/device.c | 461 +++ private/ntos/tdi/isnp/nb/dirs | 22 + private/ntos/tdi/isnp/nb/driver.c | 1794 ++++++++++ private/ntos/tdi/isnp/nb/event.c | 117 + private/ntos/tdi/isnp/nb/frame.c | 1095 ++++++ private/ntos/tdi/isnp/nb/isnnb.h | 787 +++++ private/ntos/tdi/isnp/nb/mp/makefile | 6 + private/ntos/tdi/isnp/nb/mp/sources | 29 + private/ntos/tdi/isnp/nb/nbcount/makefile | 6 + private/ntos/tdi/isnp/nb/nbcount/nbcount.c | 177 + private/ntos/tdi/isnp/nb/nbcount/nbcount.rc | 11 + private/ntos/tdi/isnp/nb/nbcount/sources | 29 + private/ntos/tdi/isnp/nb/nbiprocs.h | 1530 ++++++++ private/ntos/tdi/isnp/nb/nbitypes.h | 1511 ++++++++ private/ntos/tdi/isnp/nb/nwlnknb.ini | 43 + private/ntos/tdi/isnp/nb/nwlnknb.rc | 12 + private/ntos/tdi/isnp/nb/packet.c | 1482 ++++++++ private/ntos/tdi/isnp/nb/precomp.h | 42 + private/ntos/tdi/isnp/nb/query.c | 1817 ++++++++++ private/ntos/tdi/isnp/nb/receive.c | 1303 +++++++ private/ntos/tdi/isnp/nb/send.c | 2886 +++++++++++++++ private/ntos/tdi/isnp/nb/session.c | 2450 +++++++++++++ private/ntos/tdi/isnp/nb/sources.inc | 69 + private/ntos/tdi/isnp/nb/timer.c | 1233 +++++++ private/ntos/tdi/isnp/nb/up/makefile | 6 + private/ntos/tdi/isnp/nb/up/sources | 29 + private/ntos/tdi/isnp/spx/dirs | 22 + private/ntos/tdi/isnp/spx/globals.c | 87 + private/ntos/tdi/isnp/spx/h/fwddecls.h | 28 + private/ntos/tdi/isnp/spx/h/globals.h | 67 + private/ntos/tdi/isnp/spx/h/isnspx.h | 363 ++ private/ntos/tdi/isnp/spx/h/spxaddr.h | 426 +++ private/ntos/tdi/isnp/spx/h/spxbind.h | 32 + private/ntos/tdi/isnp/spx/h/spxconn.h | 1666 +++++++++ private/ntos/tdi/isnp/spx/h/spxdev.h | 204 ++ private/ntos/tdi/isnp/spx/h/spxerror.h | 246 ++ private/ntos/tdi/isnp/spx/h/spxmem.h | 142 + private/ntos/tdi/isnp/spx/h/spxntdef.h | 72 + private/ntos/tdi/isnp/spx/h/spxpkt.h | 466 +++ private/ntos/tdi/isnp/spx/h/spxquery.h | 54 + private/ntos/tdi/isnp/spx/h/spxrecv.h | 89 + private/ntos/tdi/isnp/spx/h/spxreg.h | 65 + private/ntos/tdi/isnp/spx/h/spxsend.h | 34 + private/ntos/tdi/isnp/spx/h/spxtimer.h | 101 + private/ntos/tdi/isnp/spx/h/spxutils.h | 178 + private/ntos/tdi/isnp/spx/mp/makefile | 6 + private/ntos/tdi/isnp/spx/mp/sources | 29 + private/ntos/tdi/isnp/spx/nwlnkspx.rc | 12 + private/ntos/tdi/isnp/spx/precomp.h | 1 + private/ntos/tdi/isnp/spx/sources.inc | 65 + private/ntos/tdi/isnp/spx/spxaddr.c | 1729 +++++++++ private/ntos/tdi/isnp/spx/spxbind.c | 600 ++++ private/ntos/tdi/isnp/spx/spxconn.c | 3862 ++++++++++++++++++++ private/ntos/tdi/isnp/spx/spxcpkt.c | 4131 ++++++++++++++++++++++ private/ntos/tdi/isnp/spx/spxcutil.c | 1738 +++++++++ private/ntos/tdi/isnp/spx/spxdev.c | 242 ++ private/ntos/tdi/isnp/spx/spxdrvr.c | 1008 ++++++ private/ntos/tdi/isnp/spx/spxerror.c | 316 ++ private/ntos/tdi/isnp/spx/spxmem.c | 897 +++++ private/ntos/tdi/isnp/spx/spxpkt.c | 1594 +++++++++ private/ntos/tdi/isnp/spx/spxquery.c | 259 ++ private/ntos/tdi/isnp/spx/spxrecv.c | 2837 +++++++++++++++ private/ntos/tdi/isnp/spx/spxreg.c | 400 +++ private/ntos/tdi/isnp/spx/spxsend.c | 262 ++ private/ntos/tdi/isnp/spx/spxtimer.c | 637 ++++ private/ntos/tdi/isnp/spx/spxutils.c | 484 +++ private/ntos/tdi/isnp/spx/up/makefile | 6 + private/ntos/tdi/isnp/spx/up/sources | 29 + private/ntos/tdi/loopback/connect.c | 1593 +++++++++ private/ntos/tdi/loopback/datagram.c | 848 +++++ private/ntos/tdi/loopback/endpoint.c | 373 ++ private/ntos/tdi/loopback/info.c | 183 + private/ntos/tdi/loopback/loopback.c | 1284 +++++++ private/ntos/tdi/loopback/loopback.h | 433 +++ private/ntos/tdi/loopback/loopdbg.h | 58 + private/ntos/tdi/loopback/loopsub.c | 379 ++ private/ntos/tdi/loopback/makefile | 6 + private/ntos/tdi/loopback/sources | 46 + private/ntos/tdi/loopback/transfer.c | 859 +++++ private/ntos/tdi/nbf/action.c | 642 ++++ private/ntos/tdi/nbf/address.c | 3046 ++++++++++++++++ private/ntos/tdi/nbf/autodial.c | 502 +++ private/ntos/tdi/nbf/connect.c | 1700 +++++++++ private/ntos/tdi/nbf/connobj.c | 2413 +++++++++++++ private/ntos/tdi/nbf/devctx.c | 408 +++ private/ntos/tdi/nbf/dlc.c | 3270 +++++++++++++++++ private/ntos/tdi/nbf/event.c | 190 + private/ntos/tdi/nbf/framecon.c | 1087 ++++++ private/ntos/tdi/nbf/framesnd.c | 2504 +++++++++++++ private/ntos/tdi/nbf/iframes.c | 3339 ++++++++++++++++++ private/ntos/tdi/nbf/info.c | 3487 +++++++++++++++++++ private/ntos/tdi/nbf/link.c | 2315 ++++++++++++ private/ntos/tdi/nbf/linktree.c | 537 +++ private/ntos/tdi/nbf/makefile | 6 + private/ntos/tdi/nbf/nbf.h | 166 + private/ntos/tdi/nbf/nbf.rc | 12 + private/ntos/tdi/nbf/nbfcnfg.c | 1155 ++++++ private/ntos/tdi/nbf/nbfcnfg.h | 102 + private/ntos/tdi/nbf/nbfconst.h | 436 +++ private/ntos/tdi/nbf/nbfdebug.c | 646 ++++ private/ntos/tdi/nbf/nbfdrvr.c | 2493 +++++++++++++ private/ntos/tdi/nbf/nbfhdrs.h | 257 ++ private/ntos/tdi/nbf/nbfmac.c | 417 +++ private/ntos/tdi/nbf/nbfmac.h | 766 ++++ private/ntos/tdi/nbf/nbfndis.c | 1956 +++++++++++ private/ntos/tdi/nbf/nbfpnp.c | 210 ++ private/ntos/tdi/nbf/nbfprocs.h | 2322 ++++++++++++ private/ntos/tdi/nbf/nbftypes.h | 2202 ++++++++++++ private/ntos/tdi/nbf/packet.c | 1808 ++++++++++ private/ntos/tdi/nbf/precomp.h | 219 ++ private/ntos/tdi/nbf/rcv.c | 309 ++ private/ntos/tdi/nbf/rcveng.c | 823 +++++ private/ntos/tdi/nbf/request.c | 1376 ++++++++ private/ntos/tdi/nbf/send.c | 503 +++ private/ntos/tdi/nbf/sendeng.c | 3657 +++++++++++++++++++ private/ntos/tdi/nbf/sources | 79 + private/ntos/tdi/nbf/spnlckdb.c | 156 + private/ntos/tdi/nbf/testnbf.c | 1466 ++++++++ private/ntos/tdi/nbf/testtdi.c | 291 ++ private/ntos/tdi/nbf/timer.c | 2762 +++++++++++++++ private/ntos/tdi/nbf/uframes.c | 2974 ++++++++++++++++ private/ntos/tdi/st/address.c | 2247 ++++++++++++ private/ntos/tdi/st/connect.c | 1137 ++++++ private/ntos/tdi/st/connobj.c | 1375 ++++++++ private/ntos/tdi/st/devctx.c | 256 ++ private/ntos/tdi/st/event.c | 185 + private/ntos/tdi/st/framesnd.c | 371 ++ private/ntos/tdi/st/iframes.c | 808 +++++ private/ntos/tdi/st/ind.c | 829 +++++ private/ntos/tdi/st/info.c | 865 +++++ private/ntos/tdi/st/makefile | 6 + private/ntos/tdi/st/oemnxpts.inf | 446 +++ private/ntos/tdi/st/packet.c | 597 ++++ private/ntos/tdi/st/rcv.c | 247 ++ private/ntos/tdi/st/rcveng.c | 383 ++ private/ntos/tdi/st/request.c | 801 +++++ private/ntos/tdi/st/send.c | 385 ++ private/ntos/tdi/st/sendeng.c | 1505 ++++++++ private/ntos/tdi/st/sources | 62 + private/ntos/tdi/st/st.h | 46 + private/ntos/tdi/st/st.rc | 12 + private/ntos/tdi/st/stcnfg.c | 1277 +++++++ private/ntos/tdi/st/stcnfg.h | 57 + private/ntos/tdi/st/stconst.h | 127 + private/ntos/tdi/st/stdrvr.c | 1627 +++++++++ private/ntos/tdi/st/sthdrs.h | 69 + private/ntos/tdi/st/stmac.c | 356 ++ private/ntos/tdi/st/stmac.h | 635 ++++ private/ntos/tdi/st/stndis.c | 986 ++++++ private/ntos/tdi/st/stprocs.h | 923 +++++ private/ntos/tdi/st/sttypes.h | 1103 ++++++ private/ntos/tdi/st/uframes.c | 980 ++++++ private/ntos/tdi/tcpip/dirs | 28 + private/ntos/tdi/tcpip/h/oscfg.h | 72 + private/ntos/tdi/tcpip/h/packoff.h | 37 + private/ntos/tdi/tcpip/h/packon.h | 35 + private/ntos/tdi/tcpip/h/queue.h | 87 + private/ntos/tdi/tcpip/h/tdint.h | 41 + private/ntos/tdi/tcpip/ip/arp.c | 4839 ++++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/arp.h | 21 + private/ntos/tdi/tcpip/ip/arpdef.h | 340 ++ private/ntos/tdi/tcpip/ip/dirs | 22 + private/ntos/tdi/tcpip/ip/icmp.c | 1698 +++++++++ private/ntos/tdi/tcpip/ip/icmp.h | 70 + private/ntos/tdi/tcpip/ip/igmp.c | 799 +++++ private/ntos/tdi/tcpip/ip/igmp.h | 42 + private/ntos/tdi/tcpip/ip/info.c | 609 ++++ private/ntos/tdi/tcpip/ip/info.h | 22 + private/ntos/tdi/tcpip/ip/init.c | 3509 +++++++++++++++++++ private/ntos/tdi/tcpip/ip/ipdef.h | 371 ++ private/ntos/tdi/tcpip/ip/ipinit.h | 155 + private/ntos/tdi/tcpip/ip/iploop.c | 665 ++++ private/ntos/tdi/tcpip/ip/iprcv.c | 1145 ++++++ private/ntos/tdi/tcpip/ip/iproute.c | 4703 +++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/iproute.h | 107 + private/ntos/tdi/tcpip/ip/iprtdef.h | 135 + private/ntos/tdi/tcpip/ip/ipstatus.c | 205 ++ private/ntos/tdi/tcpip/ip/ipxmit.c | 1949 +++++++++++ private/ntos/tdi/tcpip/ip/ipxmit.h | 34 + private/ntos/tdi/tcpip/ip/mp/makefile | 6 + private/ntos/tdi/tcpip/ip/mp/sources | 27 + private/ntos/tdi/tcpip/ip/ntip.c | 3361 ++++++++++++++++++ private/ntos/tdi/tcpip/ip/ntirp.c | 1458 ++++++++ private/ntos/tdi/tcpip/ip/ntreg.c | 672 ++++ private/ntos/tdi/tcpip/ip/sources.inc | 59 + private/ntos/tdi/tcpip/ip/up/makefile | 6 + private/ntos/tdi/tcpip/ip/up/sources | 27 + private/ntos/tdi/tcpip/tcp/addr.c | 2061 +++++++++++ private/ntos/tdi/tcpip/tcp/addr.h | 211 ++ private/ntos/tdi/tcpip/tcp/alpha/sources | 3 + private/ntos/tdi/tcpip/tcp/alpha/xsum.s | 271 ++ private/ntos/tdi/tcpip/tcp/dgram.c | 990 ++++++ private/ntos/tdi/tcpip/tcp/dgram.h | 89 + private/ntos/tdi/tcpip/tcp/dirs | 22 + private/ntos/tdi/tcpip/tcp/i386/sources | 4 + private/ntos/tdi/tcpip/tcp/i386/xsum.asm | 259 ++ private/ntos/tdi/tcpip/tcp/info.c | 917 +++++ private/ntos/tdi/tcpip/tcp/info.h | 51 + private/ntos/tdi/tcpip/tcp/init.c | 597 ++++ private/ntos/tdi/tcpip/tcp/mips/sources | 4 + private/ntos/tdi/tcpip/tcp/mips/xsum.s | 243 ++ private/ntos/tdi/tcpip/tcp/mp/makefile | 6 + private/ntos/tdi/tcpip/tcp/mp/sources | 30 + private/ntos/tdi/tcpip/tcp/mp/tcpip.prf | 297 ++ private/ntos/tdi/tcpip/tcp/ntautodl.c | 258 ++ private/ntos/tdi/tcpip/tcp/ntdisp.c | 4063 +++++++++++++++++++++ private/ntos/tdi/tcpip/tcp/ntinit.c | 1038 ++++++ private/ntos/tdi/tcpip/tcp/ppc/sources | 4 + private/ntos/tdi/tcpip/tcp/ppc/xsum.s | 257 ++ private/ntos/tdi/tcpip/tcp/raw.c | 693 ++++ private/ntos/tdi/tcpip/tcp/raw.h | 34 + private/ntos/tdi/tcpip/tcp/secfltr.c | 1425 ++++++++ private/ntos/tdi/tcpip/tcp/secfltr.h | 61 + private/ntos/tdi/tcpip/tcp/sources.inc | 67 + private/ntos/tdi/tcpip/tcp/tcb.c | 1524 ++++++++ private/ntos/tdi/tcpip/tcp/tcb.h | 67 + private/ntos/tdi/tcpip/tcp/tcp.h | 426 +++ private/ntos/tdi/tcpip/tcp/tcpcfg.h | 86 + private/ntos/tdi/tcpip/tcp/tcpconn.c | 2344 +++++++++++++ private/ntos/tdi/tcpip/tcp/tcpconn.h | 124 + private/ntos/tdi/tcpip/tcp/tcpdeb.c | 169 + private/ntos/tdi/tcpip/tcp/tcpdeb.h | 78 + private/ntos/tdi/tcpip/tcp/tcpdeliv.c | 1971 +++++++++++ private/ntos/tdi/tcpip/tcp/tcpdeliv.h | 44 + private/ntos/tdi/tcpip/tcp/tcpip.def | 8 + private/ntos/tdi/tcpip/tcp/tcpip.rc | 12 + private/ntos/tdi/tcpip/tcp/tcprcv.c | 3397 ++++++++++++++++++ private/ntos/tdi/tcpip/tcp/tcprcv.h | 74 + private/ntos/tdi/tcpip/tcp/tcpsend.c | 2666 ++++++++++++++ private/ntos/tdi/tcpip/tcp/tcpsend.h | 105 + private/ntos/tdi/tcpip/tcp/tlcommon.c | 553 +++ private/ntos/tdi/tcpip/tcp/tlcommon.h | 46 + private/ntos/tdi/tcpip/tcp/udp.c | 673 ++++ private/ntos/tdi/tcpip/tcp/udp.h | 39 + private/ntos/tdi/tcpip/tcp/up/makefile | 6 + private/ntos/tdi/tcpip/tcp/up/sources | 30 + private/ntos/tdi/tcpip/tcp/up/tcpip.prf | 297 ++ private/ntos/tdi/wrapper/cxport.c | 723 ++++ private/ntos/tdi/wrapper/makefile | 6 + private/ntos/tdi/wrapper/sources | 49 + private/ntos/tdi/wrapper/tdi.c | 1737 +++++++++ private/ntos/tdi/wrapper/tdi.def | 44 + private/ntos/tdi/wrapper/tdi.pnp | 44 + private/ntos/tdi/wrapper/tdi.rc | 12 + private/ntos/tdi/wrapper/tdidebug.h | 52 + private/ntos/tdi/wrapper/tdipnp.c | 1029 ++++++ private/ntos/tdi/wrapper/tdipnp.h | 163 + 569 files changed, 376041 insertions(+) create mode 100644 private/ntos/tdi/acd/acddefs.h create mode 100644 private/ntos/tdi/acd/api.c create mode 100644 private/ntos/tdi/acd/debug.h create mode 100644 private/ntos/tdi/acd/makefile create mode 100644 private/ntos/tdi/acd/mem.c create mode 100644 private/ntos/tdi/acd/mem.h create mode 100644 private/ntos/tdi/acd/ntdisp.c create mode 100644 private/ntos/tdi/acd/ntinit.c create mode 100644 private/ntos/tdi/acd/rasacd.rc create mode 100644 private/ntos/tdi/acd/request.c create mode 100644 private/ntos/tdi/acd/request.h create mode 100644 private/ntos/tdi/acd/sources create mode 100644 private/ntos/tdi/acd/table.c create mode 100644 private/ntos/tdi/acd/table.h create mode 100644 private/ntos/tdi/acd/timer.c create mode 100644 private/ntos/tdi/dirs create mode 100644 private/ntos/tdi/irda/dirs create mode 100644 private/ntos/tdi/irda/driver/irda.c create mode 100644 private/ntos/tdi/irda/driver/irndis.c create mode 100644 private/ntos/tdi/irda/driver/makefile create mode 100644 private/ntos/tdi/irda/driver/sources create mode 100644 private/ntos/tdi/irda/inc/af_irda.h create mode 100644 private/ntos/tdi/irda/inc/decdirda.h create mode 100644 private/ntos/tdi/irda/inc/irda.h create mode 100644 private/ntos/tdi/irda/inc/irdalink.h create mode 100644 private/ntos/tdi/irda/inc/irerr.h create mode 100644 private/ntos/tdi/irda/inc/irlap.h create mode 100644 private/ntos/tdi/irda/inc/irlaplog.h create mode 100644 private/ntos/tdi/irda/inc/irlmp.h create mode 100644 private/ntos/tdi/irda/inc/irmac.h create mode 100644 private/ntos/tdi/irda/inc/oscfg.h create mode 100644 private/ntos/tdi/irda/inc/tmp.h create mode 100644 private/ntos/tdi/irda/irlap/irlap.c create mode 100644 private/ntos/tdi/irda/irlap/irlapio.c create mode 100644 private/ntos/tdi/irda/irlap/irlapio.h create mode 100644 private/ntos/tdi/irda/irlap/irlaplog.c create mode 100644 private/ntos/tdi/irda/irlap/irlapp.h create mode 100644 private/ntos/tdi/irda/irlap/makefile create mode 100644 private/ntos/tdi/irda/irlap/sources create mode 100644 private/ntos/tdi/isn/dirs create mode 100644 private/ntos/tdi/isn/flt/debug.c create mode 100644 private/ntos/tdi/isn/flt/debug.h create mode 100644 private/ntos/tdi/isn/flt/dirs create mode 100644 private/ntos/tdi/isn/flt/driver.c create mode 100644 private/ntos/tdi/isn/flt/filter.c create mode 100644 private/ntos/tdi/isn/flt/filter.h create mode 100644 private/ntos/tdi/isn/flt/flt.mak create mode 100644 private/ntos/tdi/isn/flt/fwdbind.c create mode 100644 private/ntos/tdi/isn/flt/fwdbind.h create mode 100644 private/ntos/tdi/isn/flt/mp/makefile create mode 100644 private/ntos/tdi/isn/flt/mp/sources create mode 100644 private/ntos/tdi/isn/flt/nwlnkflt.rc create mode 100644 private/ntos/tdi/isn/flt/precomp.h create mode 100644 private/ntos/tdi/isn/flt/sources.inc create mode 100644 private/ntos/tdi/isn/flt/up/makefile create mode 100644 private/ntos/tdi/isn/flt/up/sources create mode 100644 private/ntos/tdi/isn/fwd/ddreqs.c create mode 100644 private/ntos/tdi/isn/fwd/ddreqs.h create mode 100644 private/ntos/tdi/isn/fwd/debug.c create mode 100644 private/ntos/tdi/isn/fwd/debug.h create mode 100644 private/ntos/tdi/isn/fwd/dirs create mode 100644 private/ntos/tdi/isn/fwd/driver.c create mode 100644 private/ntos/tdi/isn/fwd/driver.h create mode 100644 private/ntos/tdi/isn/fwd/filterif.c create mode 100644 private/ntos/tdi/isn/fwd/filterif.h create mode 100644 private/ntos/tdi/isn/fwd/fwd.mak create mode 100644 private/ntos/tdi/isn/fwd/fwddefs.h create mode 100644 private/ntos/tdi/isn/fwd/ipxbind.c create mode 100644 private/ntos/tdi/isn/fwd/ipxbind.h create mode 100644 private/ntos/tdi/isn/fwd/lineind.c create mode 100644 private/ntos/tdi/isn/fwd/lineind.h create mode 100644 private/ntos/tdi/isn/fwd/mp/makefile create mode 100644 private/ntos/tdi/isn/fwd/mp/sources create mode 100644 private/ntos/tdi/isn/fwd/netbios.c create mode 100644 private/ntos/tdi/isn/fwd/netbios.h create mode 100644 private/ntos/tdi/isn/fwd/nwlnkfwd.rc create mode 100644 private/ntos/tdi/isn/fwd/packets.c create mode 100644 private/ntos/tdi/isn/fwd/packets.h create mode 100644 private/ntos/tdi/isn/fwd/precomp.h create mode 100644 private/ntos/tdi/isn/fwd/rcvind.c create mode 100644 private/ntos/tdi/isn/fwd/rcvind.h create mode 100644 private/ntos/tdi/isn/fwd/registry.c create mode 100644 private/ntos/tdi/isn/fwd/registry.h create mode 100644 private/ntos/tdi/isn/fwd/rwlock.h create mode 100644 private/ntos/tdi/isn/fwd/send.c create mode 100644 private/ntos/tdi/isn/fwd/send.h create mode 100644 private/ntos/tdi/isn/fwd/sources.inc create mode 100644 private/ntos/tdi/isn/fwd/tables.c create mode 100644 private/ntos/tdi/isn/fwd/tables.h create mode 100644 private/ntos/tdi/isn/fwd/up/makefile create mode 100644 private/ntos/tdi/isn/fwd/up/sources create mode 100644 private/ntos/tdi/isn/inc/bind.h create mode 100644 private/ntos/tdi/isn/inc/ioctls.h create mode 100644 private/ntos/tdi/isn/inc/ipxfltif.h create mode 100644 private/ntos/tdi/isn/inc/isn.h create mode 100644 private/ntos/tdi/isn/ipx/action.c create mode 100644 private/ntos/tdi/isn/ipx/adapter.c create mode 100644 private/ntos/tdi/isn/ipx/address.c create mode 100644 private/ntos/tdi/isn/ipx/config.c create mode 100644 private/ntos/tdi/isn/ipx/config.h create mode 100644 private/ntos/tdi/isn/ipx/device.c create mode 100644 private/ntos/tdi/isn/ipx/dirs create mode 100644 private/ntos/tdi/isn/ipx/driver.c create mode 100644 private/ntos/tdi/isn/ipx/event.c create mode 100644 private/ntos/tdi/isn/ipx/ind.c create mode 100644 private/ntos/tdi/isn/ipx/internal.c create mode 100644 private/ntos/tdi/isn/ipx/ipx.rc create mode 100644 private/ntos/tdi/isn/ipx/ipxprocs.h create mode 100644 private/ntos/tdi/isn/ipx/ipxtypes.h create mode 100644 private/ntos/tdi/isn/ipx/isnipx.h create mode 100644 private/ntos/tdi/isn/ipx/loopback.c create mode 100644 private/ntos/tdi/isn/ipx/mac.c create mode 100644 private/ntos/tdi/isn/ipx/mac.h create mode 100644 private/ntos/tdi/isn/ipx/mp/makefile create mode 100644 private/ntos/tdi/isn/ipx/mp/nwlnkipx.prf create mode 100644 private/ntos/tdi/isn/ipx/mp/sources create mode 100644 private/ntos/tdi/isn/ipx/ndis.c create mode 100644 private/ntos/tdi/isn/ipx/nwlnkipx.ini create mode 100644 private/ntos/tdi/isn/ipx/nwlnkipx.rc create mode 100644 private/ntos/tdi/isn/ipx/packet.c create mode 100644 private/ntos/tdi/isn/ipx/precomp.h create mode 100644 private/ntos/tdi/isn/ipx/query.c create mode 100644 private/ntos/tdi/isn/ipx/receive.c create mode 100644 private/ntos/tdi/isn/ipx/rip.c create mode 100644 private/ntos/tdi/isn/ipx/rt.c create mode 100644 private/ntos/tdi/isn/ipx/send.c create mode 100644 private/ntos/tdi/isn/ipx/sources.inc create mode 100644 private/ntos/tdi/isn/ipx/up/makefile create mode 100644 private/ntos/tdi/isn/ipx/up/nwlnkipx.prf create mode 100644 private/ntos/tdi/isn/ipx/up/sources create mode 100644 private/ntos/tdi/isn/ipxroute/ipxroute.c create mode 100644 private/ntos/tdi/isn/ipxroute/ipxroute.rc create mode 100644 private/ntos/tdi/isn/ipxroute/ipxrtmsg.mc create mode 100644 private/ntos/tdi/isn/ipxroute/makefile create mode 100644 private/ntos/tdi/isn/ipxroute/makefile.inc create mode 100644 private/ntos/tdi/isn/ipxroute/sources create mode 100644 private/ntos/tdi/isn/ipxroute/utils.h create mode 100644 private/ntos/tdi/isn/isnext/cteext.c create mode 100644 private/ntos/tdi/isn/isnext/cteext.h create mode 100644 private/ntos/tdi/isn/isnext/daytona/makefile create mode 100644 private/ntos/tdi/isn/isnext/daytona/sources create mode 100644 private/ntos/tdi/isn/isnext/dirs create mode 100644 private/ntos/tdi/isn/isnext/ipxext.c create mode 100644 private/ntos/tdi/isn/isnext/ipxext.h create mode 100644 private/ntos/tdi/isn/isnext/isnext.c create mode 100644 private/ntos/tdi/isn/isnext/isnext.def create mode 100644 private/ntos/tdi/isn/isnext/isnext.h create mode 100644 private/ntos/tdi/isn/isnext/isnext.rc create mode 100644 private/ntos/tdi/isn/isnext/nbext.c create mode 100644 private/ntos/tdi/isn/isnext/precomp.h create mode 100644 private/ntos/tdi/isn/isnext/spxext.c create mode 100644 private/ntos/tdi/isn/isnext/spxext.h create mode 100644 private/ntos/tdi/isn/isnext/traverse.c create mode 100644 private/ntos/tdi/isn/isnext/traverse.h create mode 100644 private/ntos/tdi/isn/nb/action.c create mode 100644 private/ntos/tdi/isn/nb/address.c create mode 100644 private/ntos/tdi/isn/nb/autodial.c create mode 100644 private/ntos/tdi/isn/nb/bind.c create mode 100644 private/ntos/tdi/isn/nb/cache.c create mode 100644 private/ntos/tdi/isn/nb/config.c create mode 100644 private/ntos/tdi/isn/nb/config.h create mode 100644 private/ntos/tdi/isn/nb/connect.c create mode 100644 private/ntos/tdi/isn/nb/datagram.c create mode 100644 private/ntos/tdi/isn/nb/device.c create mode 100644 private/ntos/tdi/isn/nb/dirs create mode 100644 private/ntos/tdi/isn/nb/driver.c create mode 100644 private/ntos/tdi/isn/nb/event.c create mode 100644 private/ntos/tdi/isn/nb/frame.c create mode 100644 private/ntos/tdi/isn/nb/isnnb.h create mode 100644 private/ntos/tdi/isn/nb/mp/makefile create mode 100644 private/ntos/tdi/isn/nb/mp/sources create mode 100644 private/ntos/tdi/isn/nb/nbcount/makefile create mode 100644 private/ntos/tdi/isn/nb/nbcount/nbcount.c create mode 100644 private/ntos/tdi/isn/nb/nbcount/nbcount.rc create mode 100644 private/ntos/tdi/isn/nb/nbcount/sources create mode 100644 private/ntos/tdi/isn/nb/nbiprocs.h create mode 100644 private/ntos/tdi/isn/nb/nbitypes.h create mode 100644 private/ntos/tdi/isn/nb/nwlnknb.ini create mode 100644 private/ntos/tdi/isn/nb/nwlnknb.rc create mode 100644 private/ntos/tdi/isn/nb/packet.c create mode 100644 private/ntos/tdi/isn/nb/precomp.h create mode 100644 private/ntos/tdi/isn/nb/query.c create mode 100644 private/ntos/tdi/isn/nb/receive.c create mode 100644 private/ntos/tdi/isn/nb/send.c create mode 100644 private/ntos/tdi/isn/nb/session.c create mode 100644 private/ntos/tdi/isn/nb/sources.inc create mode 100644 private/ntos/tdi/isn/nb/timer.c create mode 100644 private/ntos/tdi/isn/nb/up/makefile create mode 100644 private/ntos/tdi/isn/nb/up/sources create mode 100644 private/ntos/tdi/isn/rip/debug.h create mode 100644 private/ntos/tdi/isn/rip/dirs create mode 100644 private/ntos/tdi/isn/rip/driver.c create mode 100644 private/ntos/tdi/isn/rip/driver.h create mode 100644 private/ntos/tdi/isn/rip/globals.c create mode 100644 private/ntos/tdi/isn/rip/globals.h create mode 100644 private/ntos/tdi/isn/rip/init.c create mode 100644 private/ntos/tdi/isn/rip/ipxbind.c create mode 100644 private/ntos/tdi/isn/rip/lineind.c create mode 100644 private/ntos/tdi/isn/rip/mp/makefile create mode 100644 private/ntos/tdi/isn/rip/mp/sources create mode 100644 private/ntos/tdi/isn/rip/nbproc.c create mode 100644 private/ntos/tdi/isn/rip/netbios.c create mode 100644 private/ntos/tdi/isn/rip/nicman.c create mode 100644 private/ntos/tdi/isn/rip/nwlnkrip.rc create mode 100644 private/ntos/tdi/isn/rip/packet.h create mode 100644 private/ntos/tdi/isn/rip/rcvind.c create mode 100644 private/ntos/tdi/isn/rip/rcvpkt.c create mode 100644 private/ntos/tdi/isn/rip/registry.c create mode 100644 private/ntos/tdi/isn/rip/ripaux.c create mode 100644 private/ntos/tdi/isn/rip/ripproc.c create mode 100644 private/ntos/tdi/isn/rip/ripsend.c create mode 100644 private/ntos/tdi/isn/rip/riptimer.c create mode 100644 private/ntos/tdi/isn/rip/route.c create mode 100644 private/ntos/tdi/isn/rip/rtdefs.h create mode 100644 private/ntos/tdi/isn/rip/rttest/makefile create mode 100644 private/ntos/tdi/isn/rip/rttest/nwsap.h create mode 100644 private/ntos/tdi/isn/rip/rttest/rttest.c create mode 100644 private/ntos/tdi/isn/rip/rttest/sources create mode 100644 private/ntos/tdi/isn/rip/rttest/utils.h create mode 100644 private/ntos/tdi/isn/rip/send.c create mode 100644 private/ntos/tdi/isn/rip/sources.inc create mode 100644 private/ntos/tdi/isn/rip/start.c create mode 100644 private/ntos/tdi/isn/rip/stubs.c create mode 100644 private/ntos/tdi/isn/rip/timer.c create mode 100644 private/ntos/tdi/isn/rip/up/makefile create mode 100644 private/ntos/tdi/isn/rip/up/sources create mode 100644 private/ntos/tdi/isn/rip/utils.h create mode 100644 private/ntos/tdi/isn/sockhelp/makefile create mode 100644 private/ntos/tdi/isn/sockhelp/sources create mode 100644 private/ntos/tdi/isn/sockhelp/wshelper.c create mode 100644 private/ntos/tdi/isn/sockhelp/wshisn.c create mode 100644 private/ntos/tdi/isn/sockhelp/wshisn.def create mode 100644 private/ntos/tdi/isn/sockhelp/wshisn.rc create mode 100644 private/ntos/tdi/isn/sockhelp/wshutil.c create mode 100644 private/ntos/tdi/isn/spx/dirs create mode 100644 private/ntos/tdi/isn/spx/globals.c create mode 100644 private/ntos/tdi/isn/spx/h/fwddecls.h create mode 100644 private/ntos/tdi/isn/spx/h/globals.h create mode 100644 private/ntos/tdi/isn/spx/h/isnspx.h create mode 100644 private/ntos/tdi/isn/spx/h/spxaddr.h create mode 100644 private/ntos/tdi/isn/spx/h/spxbind.h create mode 100644 private/ntos/tdi/isn/spx/h/spxconn.h create mode 100644 private/ntos/tdi/isn/spx/h/spxdev.h create mode 100644 private/ntos/tdi/isn/spx/h/spxerror.h create mode 100644 private/ntos/tdi/isn/spx/h/spxmem.h create mode 100644 private/ntos/tdi/isn/spx/h/spxntdef.h create mode 100644 private/ntos/tdi/isn/spx/h/spxpkt.h create mode 100644 private/ntos/tdi/isn/spx/h/spxquery.h create mode 100644 private/ntos/tdi/isn/spx/h/spxrecv.h create mode 100644 private/ntos/tdi/isn/spx/h/spxreg.h create mode 100644 private/ntos/tdi/isn/spx/h/spxsend.h create mode 100644 private/ntos/tdi/isn/spx/h/spxtimer.h create mode 100644 private/ntos/tdi/isn/spx/h/spxutils.h create mode 100644 private/ntos/tdi/isn/spx/mp/makefile create mode 100644 private/ntos/tdi/isn/spx/mp/sources create mode 100644 private/ntos/tdi/isn/spx/nwlnkspx.rc create mode 100644 private/ntos/tdi/isn/spx/precomp.h create mode 100644 private/ntos/tdi/isn/spx/sources.inc create mode 100644 private/ntos/tdi/isn/spx/spxaddr.c create mode 100644 private/ntos/tdi/isn/spx/spxbind.c create mode 100644 private/ntos/tdi/isn/spx/spxconn.c create mode 100644 private/ntos/tdi/isn/spx/spxcpkt.c create mode 100644 private/ntos/tdi/isn/spx/spxcutil.c create mode 100644 private/ntos/tdi/isn/spx/spxdev.c create mode 100644 private/ntos/tdi/isn/spx/spxdrvr.c create mode 100644 private/ntos/tdi/isn/spx/spxerror.c create mode 100644 private/ntos/tdi/isn/spx/spxmem.c create mode 100644 private/ntos/tdi/isn/spx/spxpkt.c create mode 100644 private/ntos/tdi/isn/spx/spxquery.c create mode 100644 private/ntos/tdi/isn/spx/spxrecv.c create mode 100644 private/ntos/tdi/isn/spx/spxreg.c create mode 100644 private/ntos/tdi/isn/spx/spxsend.c create mode 100644 private/ntos/tdi/isn/spx/spxtimer.c create mode 100644 private/ntos/tdi/isn/spx/spxutils.c create mode 100644 private/ntos/tdi/isn/spx/up/makefile create mode 100644 private/ntos/tdi/isn/spx/up/sources create mode 100644 private/ntos/tdi/isnp/dirs create mode 100644 private/ntos/tdi/isnp/inc/bind.h create mode 100644 private/ntos/tdi/isnp/inc/ioctls.h create mode 100644 private/ntos/tdi/isnp/inc/isn.h create mode 100644 private/ntos/tdi/isnp/ipx/action.c create mode 100644 private/ntos/tdi/isnp/ipx/adapter.c create mode 100644 private/ntos/tdi/isnp/ipx/address.c create mode 100644 private/ntos/tdi/isnp/ipx/config.c create mode 100644 private/ntos/tdi/isnp/ipx/config.h create mode 100644 private/ntos/tdi/isnp/ipx/device.c create mode 100644 private/ntos/tdi/isnp/ipx/dirs create mode 100644 private/ntos/tdi/isnp/ipx/driver.c create mode 100644 private/ntos/tdi/isnp/ipx/event.c create mode 100644 private/ntos/tdi/isnp/ipx/ind.c create mode 100644 private/ntos/tdi/isnp/ipx/internal.c create mode 100644 private/ntos/tdi/isnp/ipx/ipxprocs.h create mode 100644 private/ntos/tdi/isnp/ipx/ipxtypes.h create mode 100644 private/ntos/tdi/isnp/ipx/isnipx.h create mode 100644 private/ntos/tdi/isnp/ipx/loopback.c create mode 100644 private/ntos/tdi/isnp/ipx/mac.c create mode 100644 private/ntos/tdi/isnp/ipx/mac.h create mode 100644 private/ntos/tdi/isnp/ipx/mp/makefile create mode 100644 private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf create mode 100644 private/ntos/tdi/isnp/ipx/mp/sources create mode 100644 private/ntos/tdi/isnp/ipx/ndis.c create mode 100644 private/ntos/tdi/isnp/ipx/nwlnkipx.ini create mode 100644 private/ntos/tdi/isnp/ipx/nwlnkipx.rc create mode 100644 private/ntos/tdi/isnp/ipx/packet.c create mode 100644 private/ntos/tdi/isnp/ipx/precomp.h create mode 100644 private/ntos/tdi/isnp/ipx/query.c create mode 100644 private/ntos/tdi/isnp/ipx/receive.c create mode 100644 private/ntos/tdi/isnp/ipx/rip.c create mode 100644 private/ntos/tdi/isnp/ipx/send.c create mode 100644 private/ntos/tdi/isnp/ipx/sources.inc create mode 100644 private/ntos/tdi/isnp/ipx/up/makefile create mode 100644 private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf create mode 100644 private/ntos/tdi/isnp/ipx/up/sources create mode 100644 private/ntos/tdi/isnp/nb/action.c create mode 100644 private/ntos/tdi/isnp/nb/address.c create mode 100644 private/ntos/tdi/isnp/nb/autodial.c create mode 100644 private/ntos/tdi/isnp/nb/bind.c create mode 100644 private/ntos/tdi/isnp/nb/cache.c create mode 100644 private/ntos/tdi/isnp/nb/config.c create mode 100644 private/ntos/tdi/isnp/nb/config.h create mode 100644 private/ntos/tdi/isnp/nb/connect.c create mode 100644 private/ntos/tdi/isnp/nb/datagram.c create mode 100644 private/ntos/tdi/isnp/nb/device.c create mode 100644 private/ntos/tdi/isnp/nb/dirs create mode 100644 private/ntos/tdi/isnp/nb/driver.c create mode 100644 private/ntos/tdi/isnp/nb/event.c create mode 100644 private/ntos/tdi/isnp/nb/frame.c create mode 100644 private/ntos/tdi/isnp/nb/isnnb.h create mode 100644 private/ntos/tdi/isnp/nb/mp/makefile create mode 100644 private/ntos/tdi/isnp/nb/mp/sources create mode 100644 private/ntos/tdi/isnp/nb/nbcount/makefile create mode 100644 private/ntos/tdi/isnp/nb/nbcount/nbcount.c create mode 100644 private/ntos/tdi/isnp/nb/nbcount/nbcount.rc create mode 100644 private/ntos/tdi/isnp/nb/nbcount/sources create mode 100644 private/ntos/tdi/isnp/nb/nbiprocs.h create mode 100644 private/ntos/tdi/isnp/nb/nbitypes.h create mode 100644 private/ntos/tdi/isnp/nb/nwlnknb.ini create mode 100644 private/ntos/tdi/isnp/nb/nwlnknb.rc create mode 100644 private/ntos/tdi/isnp/nb/packet.c create mode 100644 private/ntos/tdi/isnp/nb/precomp.h create mode 100644 private/ntos/tdi/isnp/nb/query.c create mode 100644 private/ntos/tdi/isnp/nb/receive.c create mode 100644 private/ntos/tdi/isnp/nb/send.c create mode 100644 private/ntos/tdi/isnp/nb/session.c create mode 100644 private/ntos/tdi/isnp/nb/sources.inc create mode 100644 private/ntos/tdi/isnp/nb/timer.c create mode 100644 private/ntos/tdi/isnp/nb/up/makefile create mode 100644 private/ntos/tdi/isnp/nb/up/sources create mode 100644 private/ntos/tdi/isnp/spx/dirs create mode 100644 private/ntos/tdi/isnp/spx/globals.c create mode 100644 private/ntos/tdi/isnp/spx/h/fwddecls.h create mode 100644 private/ntos/tdi/isnp/spx/h/globals.h create mode 100644 private/ntos/tdi/isnp/spx/h/isnspx.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxaddr.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxbind.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxconn.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxdev.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxerror.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxmem.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxntdef.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxpkt.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxquery.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxrecv.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxreg.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxsend.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxtimer.h create mode 100644 private/ntos/tdi/isnp/spx/h/spxutils.h create mode 100644 private/ntos/tdi/isnp/spx/mp/makefile create mode 100644 private/ntos/tdi/isnp/spx/mp/sources create mode 100644 private/ntos/tdi/isnp/spx/nwlnkspx.rc create mode 100644 private/ntos/tdi/isnp/spx/precomp.h create mode 100644 private/ntos/tdi/isnp/spx/sources.inc create mode 100644 private/ntos/tdi/isnp/spx/spxaddr.c create mode 100644 private/ntos/tdi/isnp/spx/spxbind.c create mode 100644 private/ntos/tdi/isnp/spx/spxconn.c create mode 100644 private/ntos/tdi/isnp/spx/spxcpkt.c create mode 100644 private/ntos/tdi/isnp/spx/spxcutil.c create mode 100644 private/ntos/tdi/isnp/spx/spxdev.c create mode 100644 private/ntos/tdi/isnp/spx/spxdrvr.c create mode 100644 private/ntos/tdi/isnp/spx/spxerror.c create mode 100644 private/ntos/tdi/isnp/spx/spxmem.c create mode 100644 private/ntos/tdi/isnp/spx/spxpkt.c create mode 100644 private/ntos/tdi/isnp/spx/spxquery.c create mode 100644 private/ntos/tdi/isnp/spx/spxrecv.c create mode 100644 private/ntos/tdi/isnp/spx/spxreg.c create mode 100644 private/ntos/tdi/isnp/spx/spxsend.c create mode 100644 private/ntos/tdi/isnp/spx/spxtimer.c create mode 100644 private/ntos/tdi/isnp/spx/spxutils.c create mode 100644 private/ntos/tdi/isnp/spx/up/makefile create mode 100644 private/ntos/tdi/isnp/spx/up/sources create mode 100644 private/ntos/tdi/loopback/connect.c create mode 100644 private/ntos/tdi/loopback/datagram.c create mode 100644 private/ntos/tdi/loopback/endpoint.c create mode 100644 private/ntos/tdi/loopback/info.c create mode 100644 private/ntos/tdi/loopback/loopback.c create mode 100644 private/ntos/tdi/loopback/loopback.h create mode 100644 private/ntos/tdi/loopback/loopdbg.h create mode 100644 private/ntos/tdi/loopback/loopsub.c create mode 100644 private/ntos/tdi/loopback/makefile create mode 100644 private/ntos/tdi/loopback/sources create mode 100644 private/ntos/tdi/loopback/transfer.c create mode 100644 private/ntos/tdi/nbf/action.c create mode 100644 private/ntos/tdi/nbf/address.c create mode 100644 private/ntos/tdi/nbf/autodial.c create mode 100644 private/ntos/tdi/nbf/connect.c create mode 100644 private/ntos/tdi/nbf/connobj.c create mode 100644 private/ntos/tdi/nbf/devctx.c create mode 100644 private/ntos/tdi/nbf/dlc.c create mode 100644 private/ntos/tdi/nbf/event.c create mode 100644 private/ntos/tdi/nbf/framecon.c create mode 100644 private/ntos/tdi/nbf/framesnd.c create mode 100644 private/ntos/tdi/nbf/iframes.c create mode 100644 private/ntos/tdi/nbf/info.c create mode 100644 private/ntos/tdi/nbf/link.c create mode 100644 private/ntos/tdi/nbf/linktree.c create mode 100644 private/ntos/tdi/nbf/makefile create mode 100644 private/ntos/tdi/nbf/nbf.h create mode 100644 private/ntos/tdi/nbf/nbf.rc create mode 100644 private/ntos/tdi/nbf/nbfcnfg.c create mode 100644 private/ntos/tdi/nbf/nbfcnfg.h create mode 100644 private/ntos/tdi/nbf/nbfconst.h create mode 100644 private/ntos/tdi/nbf/nbfdebug.c create mode 100644 private/ntos/tdi/nbf/nbfdrvr.c create mode 100644 private/ntos/tdi/nbf/nbfhdrs.h create mode 100644 private/ntos/tdi/nbf/nbfmac.c create mode 100644 private/ntos/tdi/nbf/nbfmac.h create mode 100644 private/ntos/tdi/nbf/nbfndis.c create mode 100644 private/ntos/tdi/nbf/nbfpnp.c create mode 100644 private/ntos/tdi/nbf/nbfprocs.h create mode 100644 private/ntos/tdi/nbf/nbftypes.h create mode 100644 private/ntos/tdi/nbf/packet.c create mode 100644 private/ntos/tdi/nbf/precomp.h create mode 100644 private/ntos/tdi/nbf/rcv.c create mode 100644 private/ntos/tdi/nbf/rcveng.c create mode 100644 private/ntos/tdi/nbf/request.c create mode 100644 private/ntos/tdi/nbf/send.c create mode 100644 private/ntos/tdi/nbf/sendeng.c create mode 100644 private/ntos/tdi/nbf/sources create mode 100644 private/ntos/tdi/nbf/spnlckdb.c create mode 100644 private/ntos/tdi/nbf/testnbf.c create mode 100644 private/ntos/tdi/nbf/testtdi.c create mode 100644 private/ntos/tdi/nbf/timer.c create mode 100644 private/ntos/tdi/nbf/uframes.c create mode 100644 private/ntos/tdi/st/address.c create mode 100644 private/ntos/tdi/st/connect.c create mode 100644 private/ntos/tdi/st/connobj.c create mode 100644 private/ntos/tdi/st/devctx.c create mode 100644 private/ntos/tdi/st/event.c create mode 100644 private/ntos/tdi/st/framesnd.c create mode 100644 private/ntos/tdi/st/iframes.c create mode 100644 private/ntos/tdi/st/ind.c create mode 100644 private/ntos/tdi/st/info.c create mode 100644 private/ntos/tdi/st/makefile create mode 100644 private/ntos/tdi/st/oemnxpts.inf create mode 100644 private/ntos/tdi/st/packet.c create mode 100644 private/ntos/tdi/st/rcv.c create mode 100644 private/ntos/tdi/st/rcveng.c create mode 100644 private/ntos/tdi/st/request.c create mode 100644 private/ntos/tdi/st/send.c create mode 100644 private/ntos/tdi/st/sendeng.c create mode 100644 private/ntos/tdi/st/sources create mode 100644 private/ntos/tdi/st/st.h create mode 100644 private/ntos/tdi/st/st.rc create mode 100644 private/ntos/tdi/st/stcnfg.c create mode 100644 private/ntos/tdi/st/stcnfg.h create mode 100644 private/ntos/tdi/st/stconst.h create mode 100644 private/ntos/tdi/st/stdrvr.c create mode 100644 private/ntos/tdi/st/sthdrs.h create mode 100644 private/ntos/tdi/st/stmac.c create mode 100644 private/ntos/tdi/st/stmac.h create mode 100644 private/ntos/tdi/st/stndis.c create mode 100644 private/ntos/tdi/st/stprocs.h create mode 100644 private/ntos/tdi/st/sttypes.h create mode 100644 private/ntos/tdi/st/uframes.c create mode 100644 private/ntos/tdi/tcpip/dirs create mode 100644 private/ntos/tdi/tcpip/h/oscfg.h create mode 100644 private/ntos/tdi/tcpip/h/packoff.h create mode 100644 private/ntos/tdi/tcpip/h/packon.h create mode 100644 private/ntos/tdi/tcpip/h/queue.h create mode 100644 private/ntos/tdi/tcpip/h/tdint.h create mode 100644 private/ntos/tdi/tcpip/ip/arp.c create mode 100644 private/ntos/tdi/tcpip/ip/arp.h create mode 100644 private/ntos/tdi/tcpip/ip/arpdef.h create mode 100644 private/ntos/tdi/tcpip/ip/dirs create mode 100644 private/ntos/tdi/tcpip/ip/icmp.c create mode 100644 private/ntos/tdi/tcpip/ip/icmp.h create mode 100644 private/ntos/tdi/tcpip/ip/igmp.c create mode 100644 private/ntos/tdi/tcpip/ip/igmp.h create mode 100644 private/ntos/tdi/tcpip/ip/info.c create mode 100644 private/ntos/tdi/tcpip/ip/info.h create mode 100644 private/ntos/tdi/tcpip/ip/init.c create mode 100644 private/ntos/tdi/tcpip/ip/ipdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipinit.h create mode 100644 private/ntos/tdi/tcpip/ip/iploop.c create mode 100644 private/ntos/tdi/tcpip/ip/iprcv.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.h create mode 100644 private/ntos/tdi/tcpip/ip/iprtdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipstatus.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.h create mode 100644 private/ntos/tdi/tcpip/ip/mp/makefile create mode 100644 private/ntos/tdi/tcpip/ip/mp/sources create mode 100644 private/ntos/tdi/tcpip/ip/ntip.c create mode 100644 private/ntos/tdi/tcpip/ip/ntirp.c create mode 100644 private/ntos/tdi/tcpip/ip/ntreg.c create mode 100644 private/ntos/tdi/tcpip/ip/sources.inc create mode 100644 private/ntos/tdi/tcpip/ip/up/makefile create mode 100644 private/ntos/tdi/tcpip/ip/up/sources create mode 100644 private/ntos/tdi/tcpip/tcp/addr.c create mode 100644 private/ntos/tdi/tcpip/tcp/addr.h create mode 100644 private/ntos/tdi/tcpip/tcp/alpha/sources create mode 100644 private/ntos/tdi/tcpip/tcp/alpha/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/dgram.c create mode 100644 private/ntos/tdi/tcpip/tcp/dgram.h create mode 100644 private/ntos/tdi/tcpip/tcp/dirs create mode 100644 private/ntos/tdi/tcpip/tcp/i386/sources create mode 100644 private/ntos/tdi/tcpip/tcp/i386/xsum.asm create mode 100644 private/ntos/tdi/tcpip/tcp/info.c create mode 100644 private/ntos/tdi/tcpip/tcp/info.h create mode 100644 private/ntos/tdi/tcpip/tcp/init.c create mode 100644 private/ntos/tdi/tcpip/tcp/mips/sources create mode 100644 private/ntos/tdi/tcpip/tcp/mips/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/mp/makefile create mode 100644 private/ntos/tdi/tcpip/tcp/mp/sources create mode 100644 private/ntos/tdi/tcpip/tcp/mp/tcpip.prf create mode 100644 private/ntos/tdi/tcpip/tcp/ntautodl.c create mode 100644 private/ntos/tdi/tcpip/tcp/ntdisp.c create mode 100644 private/ntos/tdi/tcpip/tcp/ntinit.c create mode 100644 private/ntos/tdi/tcpip/tcp/ppc/sources create mode 100644 private/ntos/tdi/tcpip/tcp/ppc/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/raw.c create mode 100644 private/ntos/tdi/tcpip/tcp/raw.h create mode 100644 private/ntos/tdi/tcpip/tcp/secfltr.c create mode 100644 private/ntos/tdi/tcpip/tcp/secfltr.h create mode 100644 private/ntos/tdi/tcpip/tcp/sources.inc create mode 100644 private/ntos/tdi/tcpip/tcp/tcb.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcb.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcp.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpcfg.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpconn.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpconn.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeb.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeb.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeliv.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeliv.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpip.def create mode 100644 private/ntos/tdi/tcpip/tcp/tcpip.rc create mode 100644 private/ntos/tdi/tcpip/tcp/tcprcv.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcprcv.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpsend.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpsend.h create mode 100644 private/ntos/tdi/tcpip/tcp/tlcommon.c create mode 100644 private/ntos/tdi/tcpip/tcp/tlcommon.h create mode 100644 private/ntos/tdi/tcpip/tcp/udp.c create mode 100644 private/ntos/tdi/tcpip/tcp/udp.h create mode 100644 private/ntos/tdi/tcpip/tcp/up/makefile create mode 100644 private/ntos/tdi/tcpip/tcp/up/sources create mode 100644 private/ntos/tdi/tcpip/tcp/up/tcpip.prf create mode 100644 private/ntos/tdi/wrapper/cxport.c create mode 100644 private/ntos/tdi/wrapper/makefile create mode 100644 private/ntos/tdi/wrapper/sources create mode 100644 private/ntos/tdi/wrapper/tdi.c create mode 100644 private/ntos/tdi/wrapper/tdi.def create mode 100644 private/ntos/tdi/wrapper/tdi.pnp create mode 100644 private/ntos/tdi/wrapper/tdi.rc create mode 100644 private/ntos/tdi/wrapper/tdidebug.h create mode 100644 private/ntos/tdi/wrapper/tdipnp.c create mode 100644 private/ntos/tdi/wrapper/tdipnp.h (limited to 'private/ntos/tdi') diff --git a/private/ntos/tdi/acd/acddefs.h b/private/ntos/tdi/acd/acddefs.h new file mode 100644 index 000000000..0563e9926 --- /dev/null +++ b/private/ntos/tdi/acd/acddefs.h @@ -0,0 +1,100 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + acddefs.h + +Abstract: + + Shared internal structure defintions for the Implicit + Connection Driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 23-Jun-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#ifndef _ACDDEFS_ +#define _ACDDEFS_ + +// +// Min macro +// +#define MIN(a, b) (a) < (b) ? (a) : (b) + +// +// List entry structure for the AcdCompletionQueue. +// +typedef struct _ACD_COMPLETION { + LIST_ENTRY ListEntry; + ULONG ulDriverId; // transport driver id + BOOLEAN fCanceled; // TRUE if request was canceled + BOOLEAN fCompleted; // TRUE if request was completed + ACD_NOTIFICATION notif; + ACD_CONNECT_CALLBACK pProc; // callback proc + USHORT nArgs; // argument count + PVOID pArgs[1]; // variable length arguments +} ACD_COMPLETION, *PACD_COMPLETION; + +// +// A connection block. +// +// For each pending connection, there is a +// connection block that describes the current +// state. +// +typedef struct _ACD_CONNECTION { + LIST_ENTRY ListEntry; // connection list + BOOLEAN fNotif; // TRUE if service has been notified + BOOLEAN fProgressPing; // TRUE if service has pinged + BOOLEAN fCompleting; // TRUE if in AcdSignalCompletionCommon + ULONG ulTimerCalls; // # of total pings + ULONG ulMissedPings; // # of missed pings + LIST_ENTRY CompletionList; // completion list +} ACD_CONNECTION, *PACD_CONNECTION; + +// +// Generic hash table entry. +// +typedef struct _HASH_ENTRY { + LIST_ENTRY ListEntry; + ACD_ADDR szKey; + ULONG ulData; +} HASH_ENTRY, *PHASH_ENTRY; + +extern KSPIN_LOCK AcdSpinLockG; + +extern KEVENT AcdRequestThreadEventG; + +extern LIST_ENTRY AcdNotificationQueueG; +extern LIST_ENTRY AcdCompletionQueueG; +extern LIST_ENTRY AcdConnectionQueueG; +extern LIST_ENTRY AcdDriverListG; + +extern BOOLEAN fConnectionInProgressG; +extern BOOLEAN fProgressPingG; +extern ULONG nTimerCallsG; +extern ULONG nMissedPingsG; + +extern PDEVICE_OBJECT pAcdDeviceObjectG; + +extern ACD_ADDR szComputerName; + +// +// Miscellaneous routines. +// +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ); + +#endif // _ACDDEFS_ diff --git a/private/ntos/tdi/acd/api.c b/private/ntos/tdi/acd/api.c new file mode 100644 index 000000000..4ad13b54f --- /dev/null +++ b/private/ntos/tdi/acd/api.c @@ -0,0 +1,1556 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + api.c + +Abstract: + + Exported routines to transports for automatic connection + management. + +Author: + + Anthony Discolo (adiscolo) 17-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "acddefs.h" +#include "request.h" +#include "mem.h" +#include "debug.h" + +// +// Driver enabled mode. The automatic +// connection system service sets +// this depending on whether a user +// has logged in, and whether there's +// general network connectivity. +// +BOOLEAN fAcdEnabledG; + +// +// Spin lock for this module. +// +KSPIN_LOCK AcdSpinLockG; + +// +// Event signaled when the AcdNotificationRequestThread +// thread has a notification to process. +// +KEVENT AcdRequestThreadEventG; + +// +// This is a list of one irp representing +// a user-space process waiting to create a +// new network connection given an address. +// +LIST_ENTRY AcdNotificationQueueG; + +// +// This is a list of ACD_CONNECTION blocks representing +// requests from transports about unsuccessful connection +// attempts. There may be multiple ACD_COMPLETION block +// linked onto the same ACD_CONNECTION, grouped by +// address. +// +LIST_ENTRY AcdConnectionQueueG; + +// +// This is a list of ACD_COMPLETION blocks representing +// other requests from transports. +// +LIST_ENTRY AcdCompletionQueueG; + +// +// The list of drivers that have binded +// with us. +// +LIST_ENTRY AcdDriverListG; + +// +// Statistics +// +typedef struct _ACD_STATS { + ULONG ulConnects; // connection attempts + ULONG ulCancels; // connection cancels +} ACD_STATS; +ACD_STATS AcdStatsG[ACD_ADDR_MAX]; + +// +// Forward declarations +// +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ); + +VOID +ClearRequests( + IN KIRQL irql + ); + +// +// External variables +// +extern ULONG ulAcdOpenCountG; + + + +VOID +SetDriverMode( + IN BOOLEAN fEnable + ) + +/*++ + +DESCRIPTION + Set the global driver mode value, and inform + all bound transports of the change. + + Note: this call assumes AcdSpinLockG is already + acquired. + +ARGUMENTS + fEnable: the new driver mode value + +RETURN VALUE + None. + +--*/ + +{ + KIRQL dirql; + PLIST_ENTRY pEntry; + PACD_DRIVER pDriver; + + // + // Set the new global driver mode value. + // + fAcdEnabledG = fEnable; + // + // Inform all the drivers that have binded + // with us of the new enable mode. + // + for (pEntry = AcdDriverListG.Flink; + pEntry != &AcdDriverListG; + pEntry = pEntry->Flink) + { + pDriver = CONTAINING_RECORD(pEntry, ACD_DRIVER, ListEntry); + + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = fEnable; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + } +} // SetDriverMode + + + +NTSTATUS +AcdEnable( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Set the enable mode for the driver. This determines + which notifications it will pass up to the automatic + connection system service. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_ENABLE_MODE value. + + STATUS_SUCCESS: if the enabled bit was set successfully. + +--*/ + +{ + KIRQL irql; + BOOLEAN fEnable; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (BOOLEAN)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + fEnable = *(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer; + SetDriverMode(fEnable); + // + // Clear all pending requests if + // we are disabling the driver. + // + if (!fEnable) + ClearRequests(irql); + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return STATUS_SUCCESS; +} // AcdEnable + + + +VOID +CancelNotification( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + Generic cancel routine for irps on the AcdNotificationQueueG. + +ARGUMENTS + pDeviceObject: unused + + pIrp: pointer to the irp to be cancelled. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + + UNREFERENCED_PARAMETER(pDeviceObject); + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + // + // Release the spin lock the I/O system acquired. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // Remove it from our list. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // Complete the request. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // CancelNotification + + + +VOID +AcdCancelNotifications() + +/*++ + +DESCRIPTION + Cancel all irps on the AcdNotification queue. Although + technically more than one user address space can be waiting + for these notifications, we allow only one at this time. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + PLIST_ENTRY pHead; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + + // + // Complete all the irps in the list. + // + while ((pHead = ExInterlockedRemoveHeadList( + &AcdNotificationQueueG, + &AcdSpinLockG)) != NULL) + { + pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry); + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + // + // Complete the irp. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } +} // AcdCancelNotifications + + + +NTSTATUS +AcdWaitForNotification( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Enqueue an connection notification irp. This is done + done by the automatic connection system service. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_NOTIFICATION structure. + + STATUS_PENDING: if the ioctl was successfully enqueued + + STATUS_SUCCESS: if there is a notification already available + +--*/ + +{ + KIRQL irql, irql2; + PLIST_ENTRY pHead; + PACD_COMPLETION pCompletion; + PACD_NOTIFICATION pNotification; + PEPROCESS pProcess; + + // + // Verify the output buffer is sufficient to hold + // an ACD_NOTIFICATION structure. + // + if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof (ACD_NOTIFICATION)) + { + return STATUS_BUFFER_TOO_SMALL; + } + IoAcquireCancelSpinLock(&irql); + KeAcquireSpinLock(&AcdSpinLockG, &irql2); + // + // There is no notification available. + // Mark the irp as pending and wait for one. + // + pIrp->IoStatus.Status = STATUS_PENDING; + IoMarkIrpPending(pIrp); + // + // Set the irp's cancel routine. + // + IoSetCancelRoutine(pIrp, CancelNotification); + // + // Append the irp at the end of the + // connection notification list. + // + InsertTailList(&AcdNotificationQueueG, &pIrp->Tail.Overlay.ListEntry); + // + // Signal the request thread there is + // work to do. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); + + return STATUS_PENDING; +} // AcdWaitForNotification + + + +BOOLEAN +EqualAddress( + IN PACD_ADDR p1, + IN PACD_ADDR p2 + ) +{ + ULONG i; + + if (p1->fType != p2->fType) + return FALSE; + + switch (p1->fType) { + case ACD_ADDR_IP: + return (p1->ulIpaddr == p2->ulIpaddr); + case ACD_ADDR_IPX: + return (BOOLEAN)RtlEqualMemory( + &p1->cNode, + &p2->cNode, + ACD_ADDR_IPX_LEN); + case ACD_ADDR_NB: + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "EqualAddress: NB: (%15s,%15s) result=%d\n", + p1->cNetbios, + p2->cNetbios, + RtlEqualMemory(&p1->cNetbios, &p2->cNetbios, ACD_ADDR_NB_LEN - 1))); + } + return (BOOLEAN)RtlEqualMemory( + &p1->cNetbios, + &p2->cNetbios, + ACD_ADDR_NB_LEN - 1); + case ACD_ADDR_INET: + for (i = 0; i < ACD_ADDR_INET_LEN; i++) { + if (p1->szInet[i] != p2->szInet[i]) + return FALSE; + if (p1->szInet[i] == '\0' || p2->szInet[i] == '\0') + break; + } + return TRUE; + default: + ASSERT(FALSE); + break; + } + + return FALSE; +} // EqualAddress + + + +PACD_CONNECTION +FindConnection( + IN PACD_ADDR pAddr + ) + +/*++ + +DESCRIPTION + Search for a connection block with the specified + address. + +ARGUMENTS + pAddr: a pointer to the target ACD_ADDR + +RETURN VALUE + A PACD_CONNECTION with the specified address, if found; + otherwise NULL. + +--*/ + +{ + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry); + + if (EqualAddress(pAddr, &pCompletion->notif.addr)) + return pConnection; + } + + return NULL; +} // FindConnection + + + +NTSTATUS +AcdConnectionInProgress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Refresh the progress indicator for the connection + attempt. If the progress indicator is not updated + by the user + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_INVALID_CONNECTION: if there is no connection + attempt in progress. + + STATUS_SUCCESS + +--*/ + +{ + KIRQL irql; + PACD_STATUS pStatus; + PACD_CONNECTION pConnection; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_STATUS)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Get the success code from the + // connection attempt and pass it + // to the completion routine. + // + pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer; + KeAcquireSpinLock(&AcdSpinLockG, &irql); + pConnection = FindConnection(&pStatus->addr); + if (pConnection != NULL) + pConnection->fProgressPing = TRUE; + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return (pConnection != NULL) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} // AcdConnectionInProgress + + + +BOOLEAN +AddCompletionToConnection( + IN PACD_COMPLETION pCompletion + ) +{ + PACD_CONNECTION pConnection; + + pConnection = FindConnection(&pCompletion->notif.addr); + // + // If the connection already exists, then add + // the completion request to its list. + // + if (pConnection != NULL) { + InsertTailList(&pConnection->CompletionList, &pCompletion->ListEntry); + return TRUE; + } + // + // This is a connection to a new address. + // Create the connection block, enqueue it, + // and start the connection timer. + // + ALLOCATE_MEMORY(ACD_OBJECT_CONNECTION, pConnection); + if (pConnection == NULL) { + DbgPrint("AddCompletionToConnection: ExAllocatePool failed\n"); + return FALSE; + } + pConnection->fNotif = FALSE; + pConnection->fProgressPing = FALSE; + pConnection->fCompleting = FALSE; + pConnection->ulTimerCalls = 0; + pConnection->ulMissedPings = 0; + InitializeListHead(&pConnection->CompletionList); + InsertHeadList(&pConnection->CompletionList, &pCompletion->ListEntry); + InsertTailList(&AcdConnectionQueueG, &pConnection->ListEntry); + return TRUE; +} // AddCompletionToConnection + + + +BOOLEAN +AddCompletionBlock( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ULONG ulFlags, + IN PACD_ADAPTER pAdapter, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) + +/*++ + +DESCRIPTION + Create a block that represents an outstanding + transport request waiting for an automatic + connection. Link this block into the global + list of outstanding transport requests. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the network address of the connection + + ulFlags: connection flags + + pAdapter: pointer to adapter identifier + + pProc: a completion callback procedure + + nArgs: the number of parameters passed in pArgs + + pArgs: the parameters to pProc + +RETURN VALUE + TRUE if successful, FALSE otherwise + +--*/ + +{ + PACD_COMPLETION pCompletion; + ULONG i; + + ALLOCATE_MEMORY( + sizeof (ACD_COMPLETION) + ((nArgs - 1) * sizeof (PVOID)), + pCompletion); + if (pCompletion == NULL) { + DbgPrint("AcdAddCompletionBlock: ExAllocatePool failed\n"); + return FALSE; + } + // + // Copy the arguments into the information block. + // + pCompletion->ulDriverId = ulDriverId; + pCompletion->fCanceled = FALSE; + pCompletion->fCompleted = FALSE; + RtlCopyMemory(&pCompletion->notif.addr, pAddr, sizeof (ACD_ADDR)); + pCompletion->notif.ulFlags = ulFlags; + if (pAdapter != NULL) { + RtlCopyMemory( + &pCompletion->notif.adapter, + pAdapter, + sizeof (ACD_ADAPTER)); + } + else + RtlZeroMemory(&pCompletion->notif.adapter, sizeof (ACD_ADAPTER)); + pCompletion->pProc = pProc; + pCompletion->nArgs = nArgs; + for (i = 0; i < nArgs; i++) + pCompletion->pArgs[i] = pArgs[i]; + // + // If this is a unsuccessful connection request, + // then insert it onto the connection queue for + // that address; Otherwise, insert it into the list + // for all other requests. + // + if (ulFlags & ACD_NOTIFICATION_SUCCESS) { + InsertTailList(&AcdCompletionQueueG, &pCompletion->ListEntry); + } + else { + if (!AddCompletionToConnection(pCompletion)) { + FREE_MEMORY(pCompletion); + return FALSE; + } + } + // + // Inform the request thread + // there is new work to do. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + + return TRUE; +} // AddCompletionBlock + + + +VOID +AcdNewConnection( + IN PACD_ADDR pAddr, + IN PACD_ADAPTER pAdapter + ) +{ + KIRQL irql; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdNewConnection: ")); + AcdPrintAddress(pAddr); + AcdPrint(("\n")); + } + // + // If the driver is disabled, then fail + // all requests. + // + if (!fAcdEnabledG) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdNewConnection: driver disabled\n")); + } + return; + } + // + // Acquire our spin lock. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Allocate a new completion block. + // + AddCompletionBlock( + 0, + pAddr, + ACD_NOTIFICATION_SUCCESS, + pAdapter, + NULL, + 0, + NULL); + // + // Release the spin lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); +} // AcdNewConnection + + + +BOOLEAN +AcdStartConnection( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) + +/*++ + +DESCRIPTION + Create a new connection completion block, and enqueue + it on the list of network requests to be completed when + a new network connection has been created. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the address of the connection attempt + + ulFlags: connection flags + + pProc: the transport callback to be called when a new + connection has been created. + + nArgs: the number of arguments to pProc. + + pArgs: a pointer to an array of pProc's parameters + +RETURN VALUE + TRUE if successful, FALSE otherwise. + +--*/ + +{ + BOOLEAN fSuccess = FALSE, fFound; + KIRQL irql; + ULONG ulAttributes = 0; + PACD_COMPLETION pCompletion; + PCHAR psz, pszOrg; + ACD_ADDR szOrgAddr; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdStartConnection: ")); + AcdPrintAddress(pAddr); + AcdPrint((", ulFlags=0x%x\n", ulFlags)); + } + // + // If the driver is disabled, then fail + // all requests. + // + if (!fAcdEnabledG) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdStartConnection: driver disabled\n")); + } + return FALSE; + } + // + // Validate the address type. + // + if ((ULONG)pAddr->fType >= ACD_ADDR_MAX) { + AcdPrint(("AcdStartConnection: bad address type (%d)\n", pAddr->fType)); + return FALSE; + } + // + // Acquire our spin lock. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Update statistics. + // + AcdStatsG[pAddr->fType].ulConnects++; + // + // Allocate a new completion block. + // + fSuccess = AddCompletionBlock( + ulDriverId, + pAddr, + ulFlags, + NULL, + pProc, + nArgs, + pArgs); + // + // Release the spin lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return fSuccess; +} // AcdStartConnection + + + +BOOLEAN +AcdCancelConnection( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ACD_CANCEL_CALLBACK pProc, + IN PVOID pArg + ) + +/*++ + +DESCRIPTION + Remove a previously enqueued connection information + block from the list. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the address of the connection attempt + + pProc: the enumerator procecdure + + pArg: the enumerator procedure argument + +RETURN VALUE + None. + +--*/ + +{ + BOOLEAN fCanceled = FALSE; + KIRQL irql; + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdCancelConnection: ulDriverId=0x%x, ")); + AcdPrintAddress(pAddr); + AcdPrint(("\n")); + } + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Enumerate the list looking for + // the information block with the + // supplied parameters. + // + pConnection = FindConnection(pAddr); + if (pConnection != NULL) { + for (pEntry = pConnection->CompletionList.Flink; + pEntry != &pConnection->CompletionList; + pEntry = pEntry->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + // + // If we have a match, remove it from + // the list and free the information block. + // + if (pCompletion->ulDriverId == ulDriverId && + !pCompletion->fCanceled && + !pCompletion->fCompleted) + { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdCancelConnection: pCompletion=0x%x\n", + pCompletion)); + } + if ((*pProc)( + pArg, + pCompletion->notif.ulFlags, + pCompletion->pProc, + pCompletion->nArgs, + pCompletion->pArgs)) + { + pCompletion->fCanceled = TRUE; + fCanceled = TRUE; + // + // Update statistics. + // + AcdStatsG[pAddr->fType].ulCancels++; + break; + } + } + } + } + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return fCanceled; +} // AcdCancelConnection + + + +VOID +ConnectAddressComplete( + BOOLEAN fSuccess, + PVOID *pArgs + ) +{ + PIRP pIrp = pArgs[0]; + PIO_STACK_LOCATION pIrpSp = pArgs[1]; + KIRQL irql; + + // + // Complete the request. + // + pIrp->IoStatus.Status = fSuccess ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; + pIrp->IoStatus.Information = 0; + IoAcquireCancelSpinLock(&irql); + IoSetCancelRoutine(pIrp, NULL); + IoReleaseCancelSpinLock(irql); + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // ConnectAddressComplete + + + +BOOLEAN +CancelConnectAddressCallback( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + return (nArgs == 2 && pArgs[0] == pArg); +} // CancelConnectAddressCallback + + + +VOID +CancelConnectAddress( + PDEVICE_OBJECT pDevice, + PIRP pIrp + ) +{ + KIRQL irql; + PACD_NOTIFICATION pNotification; + + ASSERT(pIrp->Cancel); + // + // Remove our outstanding request. + // + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + // + // If we can't find the request on the connection + // list, then it has already been completed. + // + if (!AcdCancelConnection( + 0, + &pNotification->addr, + CancelConnectAddressCallback, + pIrp)) + { + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return; + } + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + IoSetCancelRoutine(pIrp, NULL); + // + // Release the spin lock the I/O system acquired. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // Complete the I/O request. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // CancelConnectAddress + + + +NTSTATUS +AcdConnectAddress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Manufacture a call to ourselves to simulate a transport + requesting an automatic connection. This allows a user + address space to initiate an automatic connection. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_NOTIFICATION structure. + + STATUS_UNSUCCESSFUL: an error occurred initiating the + automatic connection. + + STATUS_PENDING: success + +--*/ + +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + KIRQL irql; + PACD_NOTIFICATION pNotification; + PVOID pArgs[2]; + + // + // Verify the input buffer is sufficient to hold + // an ACD_NOTIFICATION structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_NOTIFICATION)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + pArgs[0] = pIrp; + pArgs[1] = pIrpSp; + // + // Start the connection. + // + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdConnectAddress: ")); + AcdPrintAddress(&pNotification->addr); + AcdPrint((", ulFlags=0x%x\n", pNotification->ulFlags)); + } + if (pNotification->ulFlags & ACD_NOTIFICATION_SUCCESS) { + AcdNewConnection( + &pNotification->addr, + &pNotification->adapter); + status = STATUS_SUCCESS; + } + else { + IoAcquireCancelSpinLock(&irql); + if (AcdStartConnection( + 0, + &pNotification->addr, + pNotification->ulFlags, + ConnectAddressComplete, + 2, + pArgs)) + { + // + // We enqueued the request successfully. + // Mark the irp as pending. + // + IoSetCancelRoutine(pIrp, CancelConnectAddress); + IoMarkIrpPending(pIrp); + status = STATUS_PENDING; + } + IoReleaseCancelSpinLock(irql); + } + + return status; +} // AcdConnectAddress + + + +VOID +AcdSignalCompletionCommon( + IN PACD_CONNECTION pConnection, + IN BOOLEAN fSuccess + ) +{ + KIRQL irql; + PLIST_ENTRY pEntry; + PACD_COMPLETION pCompletion; + BOOLEAN fFound; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdSignalCompletionCommon: pConnection=0x%x, fCompleting=%d\n", + pConnection, + pConnection->fCompleting)); + } +again: + fFound = FALSE; + // + // Acquire our lock and look for + // the next uncompleted request. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + for (pEntry = pConnection->CompletionList.Flink; + pEntry != &pConnection->CompletionList; + pEntry = pEntry->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdSignalCompletionCommon: pCompletion=0x%x, fCanceled=%d, fCompleted=%d\n", + pCompletion, + pCompletion->fCanceled, + pCompletion->fCompleted)); + } + // + // Only complete this request if it + // hasn't already been completed + // or canceled. + // + if (!pCompletion->fCanceled && !pCompletion->fCompleted) { + pCompletion->fCompleted = TRUE; + fFound = TRUE; + break; + } + } + // + // If there are no more requests to + // complete then remove this connection + // from the connection list and free its + // memory. + // + if (!fFound) { + RemoveEntryList(&pConnection->ListEntry); + while (!IsListEmpty(&pConnection->CompletionList)) { + pEntry = RemoveHeadList(&pConnection->CompletionList); + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + FREE_MEMORY(pCompletion); + } + FREE_MEMORY(pConnection); + // + // Signal the request thread that + // the connection list has changed. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + } + // + // Release our lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // If we found a request, then + // call its completion proc. + // + if (fFound) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdSignalCompletionCommon: pCompletion=0x%x, ", pCompletion)); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint(("\n")); + } + (*pCompletion->pProc)(fSuccess, pCompletion->pArgs); + // + // Look for another request. + // + goto again; + } +} // AcdSignalCompletionCommon + + + +NTSTATUS +AcdSignalCompletion( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + For each thread waiting on the AcdCompletionQueueG, + call the transport-dependent callback to retry the + connection attempt and complete the irp. + +ARGUMENTS + pIrp: unused + + pIrpSp: unused + +RETURN VALUE + STATUS_SUCCESS + +--*/ + +{ + KIRQL irql; + PACD_STATUS pStatus; + PACD_CONNECTION pConnection; + BOOLEAN fFound = FALSE; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_STATUS)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Get the success code from the + // connection attempt and pass it + // to the completion routine. + // + pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer; + KeAcquireSpinLock(&AcdSpinLockG, &irql); + pConnection = FindConnection(&pStatus->addr); + if (pConnection != NULL && !pConnection->fCompleting) { + // + // Set the completion-in-progress flag so + // this request cannot be timed-out after + // we release the spin lock. + // + pConnection->fCompleting = TRUE; + fFound = TRUE; + } + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // If we didn't find the connection block, + // or the completion was already in progress, + // then return an error. + // + if (!fFound) + return STATUS_UNSUCCESSFUL; + + AcdSignalCompletionCommon(pConnection, pStatus->fSuccess); + return STATUS_SUCCESS; +} // AcdSignalCompletion + + + +VOID +ClearRequests( + IN KIRQL irql + ) + +/*++ + +DESCRIPTION + Complete all pending requests with failure status. + This call assumes the AcdSpinLockG is already held, + and it returns with it held. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + PLIST_ENTRY pHead, pEntry; + PACD_COMPLETION pCompletion; + PACD_CONNECTION pConnection; + +again: + // + // Complete all pending connections with + // an error. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + if (!pConnection->fCompleting) { + pConnection->fCompleting = TRUE; + // + // We need to release our lock to + // complete the request. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // Complete the request. + // + AcdSignalCompletionCommon(pConnection, FALSE); + // + // Check for more uncompleted requests. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + goto again; + } + } + // + // Clear out all other pending requests. + // + while (!IsListEmpty(&AcdCompletionQueueG)) { + pHead = RemoveHeadList(&AcdCompletionQueueG); + pCompletion = CONTAINING_RECORD(pHead, ACD_COMPLETION, ListEntry); + + FREE_MEMORY(pCompletion); + } +} // ClearRequests + + + +VOID +AcdReset() + +/*++ + +DESCRIPTION + Complete all pending requests with failure status. + This is called when the reference count on the driver + object goes to zero, and prevents stale requests from + being presented to the system service if it is restarted + when there are pending completion requests. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + PLIST_ENTRY pHead, pEntry; + PACD_COMPLETION pCompletion; + PACD_CONNECTION pConnection; + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Reset the notification mode to disabled. + // + SetDriverMode(FALSE); + // + // Complete all pending connections with + // an error. + // + ClearRequests(irql); + KeReleaseSpinLock(&AcdSpinLockG, irql); +} // AcdReset + + + +NTSTATUS +AcdBind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Return the list of entry points to a client + transport driver. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too + small. STATUS_SUCCESS otherwise. + +--*/ + +{ + NTSTATUS status; + PACD_DRIVER *ppDriver, pDriver; + KIRQL irql, dirql; + + // + // Verify the input buffer a pointer to + // the driver's ACD_DRIVER structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (PACD_DRIVER)) + { + return STATUS_BUFFER_TOO_SMALL; + } + ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer; + pDriver = *ppDriver; +#if DBG + // + // Selectively bind with some transports. + // + switch (pDriver->ulDriverId) { + case 'Nbf ': + break; + case 'Tcp ': +#ifdef notdef + DbgPrint("AcdBind: ignoring Tcp\n"); + pDriver->fEnabled = FALSE; + pIrp->IoStatus.Information = 0; + return STATUS_SUCCESS; +#endif + break; + case 'Nbi ': +#ifdef notdef + DbgPrint("AcdBind: ignoring Nbi\n"); + pDriver->fEnabled = FALSE; + pIrp->IoStatus.Information = 0; + return STATUS_SUCCESS; +#endif + break; + } +#endif + // + // Fill in the entry point structure. + // + pDriver->lpfnNewConnection = AcdNewConnection; + pDriver->lpfnStartConnection = AcdStartConnection; + pDriver->lpfnCancelConnection = AcdCancelConnection; + // + // Insert this block into our driver list. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = fAcdEnabledG; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + InsertTailList(&AcdDriverListG, &pDriver->ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // No data should be copied back. + // + pIrp->IoStatus.Information = 0; + + return STATUS_SUCCESS; +} // AcdBind + + + +NTSTATUS +AcdUnbind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Unbind a client transport driver. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too + small. STATUS_SUCCESS otherwise. + +--*/ + +{ + KIRQL irql, dirql; + PLIST_ENTRY pEntry, pEntry2; + PACD_DRIVER *ppDriver, pDriver; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + // + // Verify the input buffer a pointer to + // the driver's ACD_DRIVER structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (PACD_DRIVER)) + { + return STATUS_BUFFER_TOO_SMALL; + } + ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer; + pDriver = *ppDriver; + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Enumerate the list looking for + // any connection request initiated by the + // specified driver. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + for (pEntry2 = pConnection->CompletionList.Flink; + pEntry2 != &pConnection->CompletionList; + pEntry2 = pEntry2->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry); + + // + // If we have a match, cancel it. + // + if (pCompletion->ulDriverId == pDriver->ulDriverId) + pCompletion->fCanceled = TRUE; + } + } + // + // Set this driver's enable mode to ACD_ENABLE_NONE. + // + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = FALSE; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + // + // Remove this driver from the list. + // + RemoveEntryList(&pDriver->ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // No data should be copied back. + // + pIrp->IoStatus.Information = 0; + + return STATUS_SUCCESS; +} // AcdUnbind + + +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ) +{ +#if DBG + PUCHAR puc; + + switch (pAddr->fType) { + case ACD_ADDR_IP: + puc = (PUCHAR)&pAddr->ulIpaddr; + AcdPrint(("IP: %d.%d.%d.%d", puc[0], puc[1], puc[2], puc[3])); + break; + case ACD_ADDR_IPX: + AcdPrint(( + "IPX: %02x:%02x:%02x:%02x:%02x:%02x", + pAddr->cNode[0], + pAddr->cNode[1], + pAddr->cNode[2], + pAddr->cNode[3], + pAddr->cNode[4], + pAddr->cNode[5])); + break; + case ACD_ADDR_NB: + AcdPrint(("NB: %15.15s", pAddr->cNetbios)); + break; + case ACD_ADDR_INET: + AcdPrint(("INET: %s", pAddr->szInet)); + break; + default: + AcdPrint(("UNKNOWN: ????")); + break; + } +#endif +} // AcdPrintAddress diff --git a/private/ntos/tdi/acd/debug.h b/private/ntos/tdi/acd/debug.h new file mode 100644 index 000000000..421c43de6 --- /dev/null +++ b/private/ntos/tdi/acd/debug.h @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + debug.h + +Abstract: + + Debugging defintions for the Automatic + Connection Driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 3-Aug-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#ifndef _ACDDBG_ +#define _ACDDBG_ + +// +// Debug tracing flags. +// +// To enable debug tracing for a module, set the +// appropriate bit in ntinit\AcdDebugG. +// +#if DBG + +#define ACD_DEBUG_IOCTL 0x00000001 // ntdisp.c/AcdDispatchDeviceControl() +#define ACD_DEBUG_OPENCOUNT 0x00000002 // ntdisp.c/Acd{Open,Close}() +#define ACD_DEBUG_TIMER 0x00000004 // timer.c +#define ACD_DEBUG_CONNECTION 0x00000008 // api.c/AcdStartConnection() +#define ACD_DEBUG_WORKER 0x00000010 // api.c/AcdNotificationRequestThread() +#define ACD_DEBUG_RESET 0x00000020 // api.c/AcdReset() +#define ACD_DEBUG_MEMORY 0x80000000 // memory alloc/free + +#define IF_ACDDBG(flag) if (AcdDebugG & flag) +#define AcdPrint(many_args) DbgPrint many_args + +#define ALLOCATE_MEMORY(fObject, pObject) \ + pObject = AllocateObjectMemory(fObject); \ + IF_ACDDBG(ACD_DEBUG_MEMORY) \ + AcdPrint(("ALLOCATE_MEMORY: %s(%d): fObject=%d, pObject=0x%x\n", __FILE__, __LINE__, fObject, pObject)) + +#define FREE_MEMORY(pObject) \ + IF_ACDDBG(ACD_DEBUG_MEMORY) \ + AcdPrint(("FREE_MEMORY: %s(%d): pObject=0x%x\n", __FILE__, __LINE__, pObject)); \ + FreeObjectMemory(pObject) + +extern ULONG AcdDebugG; + +#else + +#define IF_ACDDBG(flag) if (0) +#define AcdPrint(many_args) + +#define ALLOCATE_MEMORY(fObject, pObject) \ + pObject = AllocateObjectMemory(fObject); + +#define FREE_MEMORY(pObject) \ + FreeObjectMemory(pObject) + +#endif + + +#endif // _ACDDBG_ diff --git a/private/ntos/tdi/acd/makefile b/private/ntos/tdi/acd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/acd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/acd/mem.c b/private/ntos/tdi/acd/mem.c new file mode 100644 index 000000000..e3c9166c1 --- /dev/null +++ b/private/ntos/tdi/acd/mem.c @@ -0,0 +1,289 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.c + +ABSTRACT + Generic hash table manipulation routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + +// +// The maximum number of allocated +// objects we allocate from outside +// our zones. +// +#define MAX_ALLOCATED_OBJECTS 100 + +// +// Rounding up macro. +// +#define ROUNDUP(n, b) (((n) + ((b) - 1)) & ~((b) - 1)) + +// +// Map an object type to a zone. +// +#define OBJECT_INFO(fObject) \ + (fObject < ACD_OBJECT_MAX) ? &AcdObjectInfoG[fObject] : &AcdObjectInfoG[ACD_OBJECT_MAX] + +// +// The spin lock for this module. +// +KSPIN_LOCK AcdMemSpinLockG; + +// +// Zone-based object information. One zone +// per object type. +// +typedef struct _OBJECT_INFORMATION { + ZONE_HEADER zone; + ULONG ulSize; // object size + ULONG ulTag; // ExAllocateFromPoolWithTag() tag + ULONG ulCurrent; // # currently allocated in zone + ULONG ulTotal; // total # zone allocations +} OBJECT_INFORMATION, *POBJECT_INFORMATION; + +OBJECT_INFORMATION AcdObjectInfoG[ACD_OBJECT_MAX + 1]; + +// +// Pool-based object allocation. This is for +// objects that don't fit into any of the zones, +// or when the zone is full. +// +typedef struct _POOL_INFORMATION { + ULONG cbMin; // minimum size + ULONG cbMax; // maximum size + ULONG ulCurrent; // # current allocations + ULONG ulTotal; // total allocations + ULONG ulFailures; // total failures +} POOL_INFORMATION, *PPOOL_INFORMATION; + +POOL_INFORMATION AcdPoolInfoG; + + + +VOID +InitializeObjectAllocator() +{ + NTSTATUS status; + PVOID pMem; + ULONG ulSize; + + KeInitializeSpinLock(&AcdMemSpinLockG); + // + // Initialize zone 0 (ACD_OBJECT_CONNECTION). + // + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag = 'NdcA'; + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize = + ROUNDUP(sizeof (ACD_CONNECTION), 8); + ulSize = PAGE_SIZE; + pMem = ExAllocatePoolWithTag( + NonPagedPool, + ulSize, + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag); + ASSERT(pMem != NULL); + status = ExInitializeZone( + &AcdObjectInfoG[ACD_OBJECT_CONNECTION].zone, + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize, + pMem, + ulSize); + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "InitializeObjectAllocator: zone 0 created: blksiz=%d, size=%d (status=%d)\n", + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize, + ulSize, + status)); + } + // + // Initialize zone 1 (ACD_OBJECT_COMPLETION). + // + AcdObjectInfoG[ACD_OBJECT_MAX].ulTag = 'MdcA'; + // + // Allow for up to 6 parameters to a completion + // request (6 used by tcpip.sys). + // + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize = + ROUNDUP(sizeof (ACD_COMPLETION) + (6 * sizeof (PVOID)), 8); + ulSize = ROUNDUP(6 * AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, PAGE_SIZE), + pMem = ExAllocatePoolWithTag( + NonPagedPool, + ulSize, + AcdObjectInfoG[ACD_OBJECT_MAX].ulTag); + ASSERT(pMem != NULL); + status = ExInitializeZone( + &AcdObjectInfoG[ACD_OBJECT_MAX].zone, + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, + pMem, + ulSize); + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "InitializeObjectAllocator: zone 1 created: blksiz=%d size=%d (status=%d)\n", + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, + ulSize, + status)); + } + // + // Initialize the pool info. + // + AcdPoolInfoG.cbMin = 0xffffffff; + AcdPoolInfoG.cbMax = 0; + AcdPoolInfoG.ulCurrent = 0; + AcdPoolInfoG.ulTotal = 0; + AcdPoolInfoG.ulFailures = 0; +} // InitializeObjectAllocator + + + +PVOID +AllocateObjectMemory( + IN ULONG fObject + ) +{ + KIRQL irql; + POBJECT_INFORMATION pObjectInfo = OBJECT_INFO(fObject); + PVOID pObject; + ULONG cbBytes = 0, ulTag; + static ULONG nAllocations = 0; + + KeAcquireSpinLock(&AcdMemSpinLockG, &irql); + // + // If the zone is full, or the object + // size is greater than the zone object size, + // then use the pool allocator. + // + if (fObject > pObjectInfo->zone.BlockSize) { + cbBytes = fObject; + ulTag = 'PdcA'; + } + else if (ExIsFullZone(&pObjectInfo->zone)) { + cbBytes = pObjectInfo->ulSize; + ulTag = pObjectInfo->ulTag; + } + if (cbBytes) { + // + // Limit memory usage under stress. + // If we have more than 100 outstanding + // requests, then we start dropping + // them. + // + if (AcdPoolInfoG.ulCurrent < MAX_ALLOCATED_OBJECTS) + pObject = ExAllocatePoolWithTag(NonPagedPool, cbBytes, ulTag); + else { + pObject = NULL; + AcdPoolInfoG.ulFailures++; + goto done; + } + if (cbBytes < AcdPoolInfoG.cbMin) + AcdPoolInfoG.cbMin = cbBytes; + if (cbBytes > AcdPoolInfoG.cbMax) + AcdPoolInfoG.cbMax = cbBytes; + AcdPoolInfoG.ulCurrent++; + AcdPoolInfoG.ulTotal++; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "AllocateObjectMemory: allocated type %d from pool: pObject=0x%x\n", + fObject, + pObject)); + } + } + else { + pObject = ExAllocateFromZone(&pObjectInfo->zone); + pObjectInfo->ulCurrent++; + pObjectInfo->ulTotal++; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "AllocateObjectMemory: allocated type %d from zone: pObject=0x%x\n", + fObject, + pObject)); + } + } +#if DBG + IF_ACDDBG(ACD_DEBUG_MEMORY) { + INT i; + + if (!(++nAllocations % 10)) { + for (i = 0; i <= ACD_OBJECT_MAX; i++) { + AcdPrint(( + "Zone %d: ulCurrent=%d, ulTotal=%d\n", + i, + AcdObjectInfoG[i].ulCurrent, + AcdObjectInfoG[i].ulTotal)); + } + AcdPrint(( + "Pool: ulCurrent=%d, ulTotal=%d\n", + AcdPoolInfoG.ulCurrent, + AcdPoolInfoG.ulTotal)); + } + } +#endif +done: + KeReleaseSpinLock(&AcdMemSpinLockG, irql); + + return pObject; +} // AllocateObjectMemory + + + +VOID +FreeObjectMemory( + IN PVOID pObject + ) +{ + KIRQL irql; + INT i; + POBJECT_INFORMATION pObjectInfo; + + KeAcquireSpinLock(&AcdMemSpinLockG, &irql); + for (i = 0; i <= ACD_OBJECT_MAX; i++) { + pObjectInfo = &AcdObjectInfoG[i]; + + if (ExIsObjectInFirstZoneSegment(&pObjectInfo->zone, pObject)) { + ExFreeToZone(&pObjectInfo->zone, pObject); + pObjectInfo->ulCurrent--; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "FreeObjectMemory: freed type %d into zone: pObject=0x%x\n", + i, + pObject)); + } + goto done; + } + } + ExFreePool(pObject); + AcdPoolInfoG.ulCurrent--; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "FreeObjectMemory: freed into pool: pObject=0x%x\n", + pObject)); + } +done: + KeReleaseSpinLock(&AcdMemSpinLockG, irql); +} // FreeObjectMemory + + + +VOID +FreeObjectAllocator() +{ + // Apparently, we can't do this? +} // FreeObjectAllocator diff --git a/private/ntos/tdi/acd/mem.h b/private/ntos/tdi/acd/mem.h new file mode 100644 index 000000000..3811029e7 --- /dev/null +++ b/private/ntos/tdi/acd/mem.h @@ -0,0 +1,41 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + mem.h + +ABSTRACT + Header file for memory allocation routines. + +AUTHOR + Anthony Discolo (adiscolo) 18-Aug-1995 + +REVISION HISTORY + +--*/ + +// +// Pre-defined object types. +// Any other value represents a +// byte count. +// +#define ACD_OBJECT_CONNECTION 0 +#define ACD_OBJECT_MAX 1 + +VOID +InitializeObjectAllocator(); + +PVOID +AllocateObjectMemory( + IN ULONG fObject + ); + +VOID +FreeObjectMemory( + IN PVOID pObject + ); + +VOID +FreeObjectAllocator(); + diff --git a/private/ntos/tdi/acd/ntdisp.c b/private/ntos/tdi/acd/ntdisp.c new file mode 100644 index 000000000..5388656d2 --- /dev/null +++ b/private/ntos/tdi/acd/ntdisp.c @@ -0,0 +1,411 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntdisp.c + +Abstract: + + NT specific routines for dispatching and handling automatic + connection notification IRPs. + + The basic architecture involves a user address space, + a network transport, and this driver. + + The user address space is responsible for creating a + new network connection given a notification from this + driver (IOCTL_ACD_NOTIFICATION). When it gets a + notification, it is also responsible for pinging the + this driver (IOCTL_ACD_KEEPALIVE) so it can be guaranteed + the connection is progressing. Once the connection is + created, it informs this driver of the success or + failure of the connection attempt (IOCTL_ACD_CONNECTION). + + Network transports are responsible for informing this + driver of network unreachable errors via TdiConnect() + or TdiSendDatagram(). When this happens, the transport + is responsible for dequeueing the send request from any + of its internal queues and enqueueing the request in + this driver (AcdWaitForCompletion()), supplying a callback + to be called when the connection has been completed. + +Author: + + Anthony Discolo (adiscolo) 18-Apr-1995 + +Revision History: + +--*/ +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "debug.h" + +// +// Driver reference count +// +ULONG ulAcdOpenCountG; + +// +// Imported routines +// +NTSTATUS +AcdEnable( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +VOID +AcdCancelNotifications(); + +NTSTATUS +AcdWaitForNotification( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdConnectionInProgress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdSignalCompletion( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdConnectAddress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +VOID +AcdReset(); + +NTSTATUS +AcdGetAddressAttributes( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdSetAddressAttributes( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +// +// Internal function prototypes +// +NTSTATUS +AcdCreate( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdDispatchDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdDispatchInternalDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdCleanup( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdClose( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdBind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdUnbind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +// +// All of this code is pageable. +// +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AcdCreate) +#pragma alloc_text(PAGE, AcdDispatchDeviceControl) +#pragma alloc_text(PAGE, AcdDispatchInternalDeviceControl) +#pragma alloc_text(PAGE, AcdCleanup) +#pragma alloc_text(PAGE, AcdClose) +#endif // ALLOC_PRAGMA + + + +NTSTATUS +AcdCreate( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + ulAcdOpenCountG++; + IF_ACDDBG(ACD_DEBUG_OPENCOUNT) { + AcdPrint(("AcdCreate: ulAcdOpenCountG=%d\n", ulAcdOpenCountG)); + } + return STATUS_SUCCESS; +} // AcdCreate + + + +NTSTATUS +AcdDispatchDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + NTSTATUS status; + + + PAGED_CODE(); + // + // Set this in advance. Any IOCTL dispatch routine that cares about it + // will modify it itself. + // + pIrp->IoStatus.Information = 0; + + switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_ACD_RESET: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_RESET\n")); + } + AcdReset(); + status = STATUS_SUCCESS; + break; + case IOCTL_ACD_ENABLE: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_ENABLE\n")); + } + // + // Enable/disable requests to/from the driver. + // + status = AcdEnable(pIrp, pIrpSp); + break; + case IOCTL_ACD_NOTIFICATION: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_NOTIFICATION\n")); + } + // + // This irp will be completed upon the + // next connection attempt to + // allow a user-space process to attempt + // to make a connection. + // + status = AcdWaitForNotification(pIrp, pIrpSp); + break; + case IOCTL_ACD_KEEPALIVE: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_KEEPALIVE\n")); + } + // + // Inform the driver that the connection + // is in the process of being created. + // + status = AcdConnectionInProgress(pIrp, pIrpSp); + break; + case IOCTL_ACD_COMPLETION: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_COMPLETION\n")); + } + // + // Complete all pending irps that initially + // encountered a network unreachable error, + // and have been waiting for a connection to be + // made. + // + status = AcdSignalCompletion(pIrp, pIrpSp); + break; + case IOCTL_ACD_CONNECT_ADDRESS: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_CONNECT_ADDRESS\n")); + } + // + // This allows a user space application to + // generate the same automatic connection + // mechanism as a transport protocol. + // + status = AcdConnectAddress(pIrp, pIrpSp); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatchDeviceControl + + + +NTSTATUS +AcdDispatchInternalDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + NTSTATUS status; + + PAGED_CODE(); + // + // Set this in advance. Any IOCTL dispatch routine that cares about it + // will modify it itself. + // + pIrp->IoStatus.Information = 0; + + switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_INTERNAL_ACD_BIND: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_BIND\n")); + } + // + // Transfer entrypoints to client. + // + status = AcdBind(pIrp, pIrpSp); + break; + case IOCTL_INTERNAL_ACD_UNBIND: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_UNBIND\n")); + } + // + // Remove any pending requests from + // this driver. + // + status = AcdUnbind(pIrp, pIrpSp); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatchInternalDeviceControl + + + +NTSTATUS +AcdCleanup( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + return STATUS_SUCCESS; +} // AcdCleanup + + + +NTSTATUS +AcdClose( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + ulAcdOpenCountG--; + IF_ACDDBG(ACD_DEBUG_OPENCOUNT) { + AcdPrint(("AcdClose: ulAcdOpenCountG=%d\n", ulAcdOpenCountG)); + } + if (!ulAcdOpenCountG) + AcdReset(); + return STATUS_SUCCESS; +} // AcdClose + + + +NTSTATUS +AcdDispatch( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + This is the dispatch routine for the network connection + notification driver. + +ARGUMENTS + pDeviceObject: a pointer to device object for target device + + pIrp: a pointer to I/O request packet + +Return Value: + NTSTATUS + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + switch (pIrpSp->MajorFunction) { + case IRP_MJ_CREATE: + status = AcdCreate(pIrp, pIrpSp); + break; + case IRP_MJ_DEVICE_CONTROL: + return AcdDispatchDeviceControl(pIrp, pIrpSp); + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return AcdDispatchInternalDeviceControl(pIrp, pIrpSp); + case IRP_MJ_CLEANUP: + status = AcdCleanup(pIrp, pIrpSp); + break; + case IRP_MJ_CLOSE: + status = AcdClose(pIrp, pIrpSp); + break; + default: + DbgPrint("AcdDispatch: Invalid major function %lx\n", + pIrpSp->MajorFunction); + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + pIrp->IoStatus.Information = 0; + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatch + diff --git a/private/ntos/tdi/acd/ntinit.c b/private/ntos/tdi/acd/ntinit.c new file mode 100644 index 000000000..24ad73ef0 --- /dev/null +++ b/private/ntos/tdi/acd/ntinit.c @@ -0,0 +1,223 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntinit.c + +Abstract: + + NT specific routines for loading and configuring the + automatic connection notification driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 18-Apr-1995 + +Revision History: + +--*/ +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + + +// +// Global variables +// +#if DBG +ULONG AcdDebugG = 0x0; // see debug.h for flags +#endif + +PDRIVER_OBJECT pAcdDriverObjectG; +PDEVICE_OBJECT pAcdDeviceObjectG; + +HANDLE hSignalNotificationThreadG; + +// +// Imported routines +// +VOID +AcdNotificationRequestThread( + PVOID context + ); + +// +// External function prototypes +// +NTSTATUS +AcdDispatch( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +VOID +AcdConnectionTimer( + IN PDEVICE_OBJECT pDeviceObject, + IN PVOID pContext + ); + +// +// Internal function prototypes +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ); + +BOOLEAN +GetComputerName( + IN PUCHAR szName, + IN USHORT cbName + ); + +VOID +AcdUnload( + IN PDRIVER_OBJECT pDriverObject + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, AcdUnload) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ) + +/*++ + +DESCRIPTION + Initialization routine for the network connection notification driver. + It creates the device object and initializes the driver. + +ARGUMENTS + pDriverObject: a pointer to the driver object created by the system. + + pRegistryPath - the name of the configuration node in the registry. + +RETURN VALUE + The final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + ULONG i; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatusBlock; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // + // Initialize the spin lock. + // + KeInitializeSpinLock(&AcdSpinLockG); + // + // Initialize the notification and completion + // connection queues. + // + InitializeListHead(&AcdNotificationQueueG); + InitializeListHead(&AcdCompletionQueueG); + InitializeListHead(&AcdConnectionQueueG); + InitializeListHead(&AcdDriverListG); + // + // Initialize our zone allocator. + // + InitializeObjectAllocator(); + // + // Create the device object. + // + pAcdDriverObjectG = pDriverObject; + RtlInitUnicodeString(&deviceName, ACD_DEVICE_NAME); + status = IoCreateDevice( + pDriverObject, + 0, + &deviceName, + FILE_DEVICE_ACD, + 0, + FALSE, + &pAcdDeviceObjectG); + + if (!NT_SUCCESS(status)) { + DbgPrint( + "AcdDriverEntry: IoCreateDevice failed (status=0x%x)\n", + status); + return status; + } + // + // Initialize the driver object. + // + //pDriverObject->DriverUnload = AcdUnload; + pDriverObject->DriverUnload = NULL; + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + pDriverObject->MajorFunction[i] = AcdDispatch; + pDriverObject->FastIoDispatch = NULL; + // + // Initialize the connection timer. This is + // used to make sure pending requests aren't + // blocked forever because the user-space + // process died trying to make a connection. + // + IoInitializeTimer(pAcdDeviceObjectG, AcdConnectionTimer, NULL); + // + // Create the worker thread. We need + // a thread because these operations can occur at + // DPC irql. + // + KeInitializeEvent( + &AcdRequestThreadEventG, + NotificationEvent, + FALSE); + status = PsCreateSystemThread( + &hSignalNotificationThreadG, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + AcdNotificationRequestThread, + NULL); + if (!NT_SUCCESS(status)) { + DbgPrint( + "AcdDriverEntry: PsCreateSystemThread failed (status=0x%x)\n", + status); + return status; + } + + return STATUS_SUCCESS; +} // DriverEntry + + + +VOID +AcdUnload( + IN PDRIVER_OBJECT pDriverObject + ) +{ + NTSTATUS status; + + // + // BUGBUG: Make sure to unlink all driver + // blocks before unloading! + // + IoDeleteDevice(pAcdDeviceObjectG); + // + // Free zone allocator. + // + FreeObjectAllocator(); +} // AcdUnload diff --git a/private/ntos/tdi/acd/rasacd.rc b/private/ntos/tdi/acd/rasacd.rc new file mode 100644 index 000000000..d8c0d68bf --- /dev/null +++ b/private/ntos/tdi/acd/rasacd.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "RAS Automatic Connection Driver" +#define VER_INTERNALNAME_STR "rasacd.sys" +#define VER_ORIGINALFILENAME_STR "rasacd.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/acd/request.c b/private/ntos/tdi/acd/request.c new file mode 100644 index 000000000..7976e733d --- /dev/null +++ b/private/ntos/tdi/acd/request.c @@ -0,0 +1,303 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + Worker thread for the automatic connection driver. + +Author: + + Anthony Discolo (adiscolo) 17-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + +// +// External declarations +// +VOID AcdPrintAddress( + IN PACD_ADDR pAddr + ); + + + +VOID +ProcessCompletion( + IN PACD_COMPLETION pCompletion, + IN KIRQL irqlCancel, + IN KIRQL irqlLock + ) +{ + PLIST_ENTRY pHead; + KIRQL irql; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + PACD_NOTIFICATION pNotification; + + ASSERT(!IsListEmpty(&AcdNotificationQueueG)); + // + // Complete the next irp in the + // AcdNotificationQueueG queue. These + // represent the ioctl completions the + // system service has posted. Completing + // this request will start the system service + // to create a new RAS connection. + // Logically, there is always just one. + // + pHead = RemoveHeadList(&AcdNotificationQueueG); + pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + // + // Disable the irp's cancel routine. + // + IoSetCancelRoutine(pIrp, NULL); + // + // Copy the success flag and the address into the + // system buffer. This will get copied into the + // user's buffer on return. + // + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + RtlCopyMemory( + pNotification, + &pCompletion->notif, + sizeof (ACD_NOTIFICATION)); + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: ")); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint((", ulFlags=0x%x\n", pCompletion->notif.ulFlags)); + } + // + // We can release both the cancel lock + // and our lock now. + // + KeReleaseSpinLock(&AcdSpinLockG, irqlLock); + IoReleaseCancelSpinLock(irqlCancel); + // + // Set the status code and the number + // of bytes to be copied back to the user + // buffer. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = sizeof (ACD_NOTIFICATION); + // + // Complete the irp. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // ProcessCompletion + + + +VOID +AcdNotificationRequestThread( + PVOID context + ) + +/*++ + +DESCRIPTION + This thread handles the notification that an automatic + connection may need to be initiated. This needs to + happen in a separate thread, because the notification + may occur at DPC irql. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql, irql2; + PLIST_ENTRY pEntry, pEntry2; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + BOOLEAN bStartTimer, bStopTimer; + + UNREFERENCED_PARAMETER(context); + + IoStartTimer(pAcdDeviceObjectG); + + for (;;) { + bStartTimer = bStopTimer = FALSE; + // + // Acquire our lock. + // + IoAcquireCancelSpinLock(&irql); + KeAcquireSpinLock(&AcdSpinLockG, &irql2); + // + // If there are no irps to complete, + // then go back to sleep. + // + if (IsListEmpty(&AcdNotificationQueueG)) { + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: no ioctl to complete\n")); + } + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); + goto again; + } + // + // Search for connections that haven't + // been processed yet. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + // + // Don't issue a request to the service + // for more than one simultaneous connection. + // + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: pConnection=0x%x, fNotif=%d, fCompleting=%d\n", + pConnection, + pConnection->fNotif, + pConnection->fCompleting)); + } + if (pConnection->fNotif) + break; + // + // Skip all connections that are in + // the process of being completed. + // + if (pConnection->fCompleting) + continue; + // + // Make sure there is at least one + // request in this connection that + // hasn't been canceled. + // + for (pEntry2 = pConnection->CompletionList.Flink; + pEntry2 != &pConnection->CompletionList; + pEntry2 = pEntry2->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry); + + if (!pCompletion->fCanceled) { + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: starting pConnection=0x%x, pCompletion=0x%x\n", + pConnection, + pCompletion)); + } + pConnection->fNotif = TRUE; + // + // This call releases both the cancel lock + // and our lock. + // + ProcessCompletion(pCompletion, irql, irql2); + // + // Start the connection timer. + // + bStartTimer = TRUE; + // + // We can only process one completion + // at a time. + // + goto again; + } + } + } + // + // Complete other requests. + // + if (!IsListEmpty(&AcdCompletionQueueG)) { + pEntry = RemoveHeadList(&AcdCompletionQueueG); + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: starting pCompletion=0x%x\n", + pCompletion)); + } + // + // This call releases both the cancel lock + // and our lock. + // + ProcessCompletion(pCompletion, irql, irql2); + // + // We are done with the completion, + // so we can free the memory now. + // + FREE_MEMORY(pCompletion); + // + // We can only process one completion + // at a time. + // + goto again; + + } + // + // If there are no connections pending, + // then stop the connection timer. + // + if (IsListEmpty(&AcdConnectionQueueG)) + bStopTimer = TRUE; + // + // Release our lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); +again: + // + // Start or stop the timer, depending + // on what we found while we had the + // spinlock. We can't hold our spin + // lock when we call the Io*Timer + // routines. + // +#ifdef notdef + if (bStopTimer) + IoStopTimer(pAcdDeviceObjectG); + else if (bStartTimer) + IoStartTimer(pAcdDeviceObjectG); +#endif + // + // Wait for something to do. This event + // will be signaled by AcdSignalNotification(). + // + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: waiting on AcdPendingCompletionEventG\n")); + } + KeWaitForSingleObject( + &AcdRequestThreadEventG, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(&AcdRequestThreadEventG); + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: AcdPendingCompletionEventG signalled\n")); + } + } +} // AcdNotificationRequestThread + + diff --git a/private/ntos/tdi/acd/request.h b/private/ntos/tdi/acd/request.h new file mode 100644 index 000000000..4d2c69824 --- /dev/null +++ b/private/ntos/tdi/acd/request.h @@ -0,0 +1,31 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + request.h + +ABSTRACT + Header file for completion queue routines. + +AUTHOR + Anthony Discolo (adiscolo) 18-Aug-1995 + +REVISION HISTORY + +--*/ + +PACD_COMPLETION GetNextRequest(); + +BOOLEAN +EqualAddress( + PACD_ADDR pszAddr1, + PACD_ADDR pszAddr2 + ); + +PACD_COMPLETION GetNextRequestAddress( + IN PACD_ADDR pszAddr + ); + +PACD_COMPLETION GetCurrentRequest(); + diff --git a/private/ntos/tdi/acd/sources b/private/ntos/tdi/acd/sources new file mode 100644 index 000000000..b787b8419 --- /dev/null +++ b/private/ntos/tdi/acd/sources @@ -0,0 +1,46 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP= +MINORCOMP= + +TARGETNAME=rasacd +TARGETPATH=obj +TARGETTYPE=DRIVER + +MSC_WARNING_LEVEL=/W3 /WX + +INCLUDES=..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc + +C_DEFINES=-DNT -DNETSCAPE_HACK + +SOURCES= \ + ntinit.c \ + ntdisp.c \ + api.c \ + request.c \ + timer.c \ + mem.c \ + rasacd.rc + diff --git a/private/ntos/tdi/acd/table.c b/private/ntos/tdi/acd/table.c new file mode 100644 index 000000000..0846b97e6 --- /dev/null +++ b/private/ntos/tdi/acd/table.c @@ -0,0 +1,286 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.c + +ABSTRACT + Generic hash table manipulation routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "table.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + + + +PHASH_TABLE +NewTable() +{ + PHASH_TABLE pTable; + INT i; + + ALLOCATE_MEMORY(sizeof (HASH_TABLE), pTable); + if (pTable == NULL) { + DbgPrint("AcdNewTable: ExAllocatePool failed\n"); + return NULL; + } + KeInitializeSpinLock(&pTable->SpinLock); + for (i = 0; i < NBUCKETS; i++) + InitializeListHead(&pTable->ListEntry[i]); + + return pTable; +} // NewTable + + + +VOID +FreeHashTableEntry( + PHASH_ENTRY pHashEntry + ) +{ + FREE_MEMORY(pHashEntry); +} // FreeHashTableEntry + + + +VOID +ClearTable( + PHASH_TABLE pTable + ) +{ + KIRQL irql; + INT i; + PLIST_ENTRY pHead; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + for (i = 0; i < NBUCKETS; i++) { + while (!IsListEmpty(&pTable->ListEntry[i])) { + pHead = RemoveHeadList(&pTable->ListEntry[i]); + pHashEntry = CONTAINING_RECORD(pHead, HASH_ENTRY, ListEntry); + + FreeHashTableEntry(pHashEntry); + } + } + KeReleaseSpinLock(&pTable->SpinLock, irql); +} // ClearTable + + + +VOID +FreeTable( + PHASH_TABLE pTable + ) +{ + ClearTable(pTable); + FREE_MEMORY(pTable); +} // FreeTable + + + +VOID +EnumTable( + IN PHASH_TABLE pTable, + IN PHASH_TABLE_ENUM_PROC pProc, + IN PVOID pArg + ) +{ + INT i; + PLIST_ENTRY pEntry; + PHASH_ENTRY pHashEntry; + KIRQL irql; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + for (i = 0; i < NBUCKETS; i++) { + for (pEntry = pTable->ListEntry[i].Flink; + pEntry != &pTable->ListEntry[i]; + pEntry = pEntry->Flink) + { + pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry); + + // + // If the enumerator procedure + // returns FALSE, terminate the + // enumeration. + // + if (!pProc(pArg, &pHashEntry->szKey, pHashEntry->ulData)) + goto done; + } + } +done: + KeReleaseSpinLock(&pTable->SpinLock, irql); +} // EnumTable + + + +INT +HashString( + IN PACD_ADDR pszKey + ) +{ + ULONG ulHashValue = 0; + CHAR ch; + PCSZ p = (PCSZ)pszKey; + + while (*p != L'\0') { + ch = tolower(*p); + ulHashValue += (INT)(ch) * (INT)(ch); + p++; + } + + return (INT)(ulHashValue % NBUCKETS); +} // HashString + + + +BOOLEAN +IsEqualKey( + PACD_ADDR pszKey1, + PACD_ADDR pszKey2 + ) +{ + BOOLEAN fFound; + + fFound = (BOOLEAN)RtlEqualMemory(pszKey1, pszKey2, sizeof (ACD_ADDR)); + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdIsEqualKey(%s, %s) returns %d\n", pszKey1, pszKey2, fFound)); + } + return fFound; +} // IsEqualKey + + + +PHASH_ENTRY +GetTableEntryNL( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ) +{ + INT nBucket = HashString(pszKey); + PLIST_ENTRY pEntry; + PHASH_ENTRY pHashEntry; + + for (pEntry = pTable->ListEntry[nBucket].Flink; + pEntry != &pTable->ListEntry[nBucket]; + pEntry = pEntry->Flink) + { + pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry); + + if (IsEqualKey(&pHashEntry->szKey, pszKey)) { + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns 0x%x\n", pTable, pszKey, pHashEntry)); + } + return pHashEntry; + } + } + + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns NULL\n", pTable, pszKey)); + } + return NULL; +} // GetTableEntryNL + + + +BOOLEAN +GetTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + OUT PULONG pulData + ) +{ + KIRQL irql; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + pHashEntry = GetTableEntryNL(pTable, pszKey); + KeReleaseSpinLock(&pTable->SpinLock, irql); + + if (pHashEntry != NULL) { + if (pulData != NULL) + *pulData = pHashEntry->ulData; + return TRUE; + } + + return FALSE; +} // GetTableEntry + + + +BOOLEAN +PutTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + IN ULONG ulData + ) +{ + KIRQL irql; + BOOLEAN fSuccess = FALSE; + INT nBucket = HashString(pszKey); + PHASH_ENTRY pHashEntry; + + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdPutTableEntry(0x%x, %s)\n", pTable, pszKey)); + } + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + + pHashEntry = GetTableEntryNL(pTable, pszKey); + if (pHashEntry == NULL) { + ALLOCATE_MEMORY(ACD_OBJECT_HASHENTRY, pHashEntry); + if (pHashEntry == NULL) { + DbgPrint("PutTableEntry: ExAllocatePool failed\n"); + goto done; + } + RtlCopyMemory(pHashEntry->szKey, pszKey, sizeof (ACD_ADDR)); + InsertHeadList( + &pTable->ListEntry[nBucket], + &pHashEntry->ListEntry); + } + pHashEntry->ulData = ulData; + fSuccess = TRUE; + +done: + KeReleaseSpinLock(&pTable->SpinLock, irql); + return fSuccess; +} // PutTableEntry + + + +BOOLEAN +DeleteTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ) +{ + KIRQL irql; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + pHashEntry = GetTableEntryNL(pTable, pszKey); + if (pHashEntry != NULL) { + RemoveEntryList(&pHashEntry->ListEntry); + FreeHashTableEntry(pHashEntry); + } + KeReleaseSpinLock(&pTable->SpinLock, irql); + + return (pHashEntry != NULL); +} // DeleteTableEntry diff --git a/private/ntos/tdi/acd/table.h b/private/ntos/tdi/acd/table.h new file mode 100644 index 000000000..17a5ef1a0 --- /dev/null +++ b/private/ntos/tdi/acd/table.h @@ -0,0 +1,77 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.h + +ABSTRACT + Header file for generic hash table routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +// +// Number of hash table buckets. +// +#define NBUCKETS 13 + +// +// Generic hash table structure. +// +typedef struct _HASH_TABLE { + LIST_ENTRY ListEntry[NBUCKETS]; + KSPIN_LOCK SpinLock; +} HASH_TABLE, *PHASH_TABLE; + +// +// Hash table enumerator procedure. +// Returns TRUE to continue enumeration, +// FALSE to terminate enumeration. +// +typedef BOOLEAN (*PHASH_TABLE_ENUM_PROC)(PVOID, PACD_ADDR, ULONG); + + +PHASH_TABLE +NewTable(); + +VOID +ClearTable( + IN PHASH_TABLE pTable + ); + +VOID +FreeTable( + IN PHASH_TABLE pTable + ); + +VOID +EnumTable( + IN PHASH_TABLE pTable, + IN PHASH_TABLE_ENUM_PROC pProc, + IN PVOID pArg + ); + +BOOLEAN +GetTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + OUT PULONG pulData + ); + +BOOLEAN +PutTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + IN ULONG ulData + ); + +BOOLEAN +DeleteTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ); diff --git a/private/ntos/tdi/acd/timer.c b/private/ntos/tdi/acd/timer.c new file mode 100644 index 000000000..cf4f3ab63 --- /dev/null +++ b/private/ntos/tdi/acd/timer.c @@ -0,0 +1,145 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + Timer thread to monitor connection progress in the + automatic connection driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 25-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "table.h" +#include "acddefs.h" +#include "debug.h" + +// +// Imported routines. +// +VOID +AcdSignalCompletionCommon( + IN PACD_CONNECTION pConnection, + IN BOOLEAN fSuccess + ); + +// +// Keep track how long the user-space +// process has been attempting a connection. +// +#define ACD_MAX_TIMER_CALLS 3*60 // 3 minutes + +// +// We give the user-space process +// some slack on missed pings. +// +#define ACD_MAX_MISSED_PINGS 40 // 20 seconds + + + +VOID +AcdConnectionTimer( + IN PDEVICE_OBJECT pDeviceObject, + IN PVOID pContext + ) +{ + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + BOOLEAN bCancel = FALSE; + + // + // Acquire the spin lock. + // We're guaranteed to be at DPC + // since this is a timer routine. + // + KeAcquireSpinLockAtDpcLevel(&AcdSpinLockG); + // + // If the user-space process responsible + // for creating the connection hasn't + // pinged us in a while, or if it hasn't + // created a connection in 3 minutes, + // cancel all the pending requests. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_TIMER) { + PACD_COMPLETION pCompletion; + + AcdPrint(( + "AcdConnectionTimer: pConnection=0x%x, fNotif=%d, szAddr=", + pConnection, + pConnection->fNotif)); + pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint((", nTimerCalls=%d, nMissedPings=%d\n", + pConnection->ulTimerCalls, + pConnection->ulMissedPings)); + } + // + // If we haven't reported the connection to + // user space yet, or it is in the process of + // being completed, then don't time it out. + // + if (!pConnection->fNotif || pConnection->fCompleting) + continue; + + pConnection->ulTimerCalls++; + if (pConnection->fProgressPing) + pConnection->ulMissedPings = 0; + else + pConnection->ulMissedPings++; + if (pConnection->ulTimerCalls >= ACD_MAX_TIMER_CALLS || + pConnection->ulMissedPings >= ACD_MAX_MISSED_PINGS) + { + IF_ACDDBG(ACD_DEBUG_TIMER) { + AcdPrint(( + "AcdConnectionTimer: canceling pConnection=0x%x\n", + pConnection)); + } + // + // Set the completion-in-progress flag so + // this request cannot be completed after + // we release the spin lock. + // + pConnection->fCompleting = TRUE; + bCancel = TRUE; + break; + } + } + // + // Release the spin lock. + // + KeReleaseSpinLockFromDpcLevel(&AcdSpinLockG); + // + // We now process all the canceled requests. + // + if (bCancel) + AcdSignalCompletionCommon(pConnection, FALSE); +} // AcdConnectionTimer + diff --git a/private/ntos/tdi/dirs b/private/ntos/tdi/dirs new file mode 100644 index 000000000..14819da36 --- /dev/null +++ b/private/ntos/tdi/dirs @@ -0,0 +1,32 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + isn \ + isnn \ + nbf \ + wrapper \ + tcpip \ + acd + +OPTIONAL_DIRS= \ + loopback diff --git a/private/ntos/tdi/irda/dirs b/private/ntos/tdi/irda/dirs new file mode 100644 index 000000000..0ea6a777b --- /dev/null +++ b/private/ntos/tdi/irda/dirs @@ -0,0 +1,10 @@ +DIRS= irlap \ + driver + + + + + + + + diff --git a/private/ntos/tdi/irda/driver/irda.c b/private/ntos/tdi/irda/driver/irda.c new file mode 100644 index 000000000..72bdf3881 --- /dev/null +++ b/private/ntos/tdi/irda/driver/irda.c @@ -0,0 +1,132 @@ +/* + * IRDA.C + * + * + * + * + * + * + * + * + * + * + */ + + +//#include +#include +#include +#include +#include +#include +#include + +int irdaDbgSettings = 1 + \ + DBG_ERROR + \ + DBG_WARN + \ +/*DBG_FUNCTION + */ \ + /*DBG_NDIS +*/ \ +/* DBG_IRLAPLOG +*/ \ + DBG_IRLAP; + +LIST_ENTRY IrdaLinkCbList; + +/* + ******************************************************************************** + * DriverEntry + ******************************************************************************** + * + * + * + */ +NTSTATUS DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath) +{ + + NTSTATUS Status = STATUS_SUCCESS; + + DEBUGMSG(DBG_FUNCTION,("+DriverEntry(IRDA)\n")); + + InitializeListHead(&IrdaLinkCbList); + + // IRLMP initialize + // IRLAP initialize + + if ((Status = IrdaNdisInitialize()) != STATUS_SUCCESS) + { + goto done; + } + +done: + + DEBUGMSG(DBG_FUNCTION, ("-DriverEntry(IRDA), rc %x\n", Status)); + + return Status; +} + +void +IrdaTimerInitialize(PIRDA_TIMER pTimer, + VOID (*ExpFunc)(PVOID Context), + UINT Timeout, + PVOID Context) +{ + CTEInitTimer(&pTimer->CteTimer); + pTimer->ExpFunc = ExpFunc; + pTimer->Context = Context; + pTimer->Timeout = Timeout; + + DEBUGMSG(DBG_FUNCTION, ("IrdaTimerIntialize %s\n", pTimer->pName)); +} + +void +TimerFunc(CTEEvent *Event, void *Arg) +{ + PIRDA_TIMER pIrdaTimer = (PIRDA_TIMER) Arg; + int rc; + + DEBUGMSG(DBG_FUNCTION, ("Timer expired, context %x\n", + pIrdaTimer)); + + if (pIrdaTimer->Late != TRUE) + { + pIrdaTimer->ExpFunc(pIrdaTimer->Context); + } + else + { + DEBUGMSG(DBG_WARN, + (TEXT("IRDA TIMER LATE, ignoring\r\n"))); + + pIrdaTimer->Late = FALSE; + } + + return; +} + +VOID +IrdaTimerStart(PIRDA_TIMER pIrdaTimer) +{ + + pIrdaTimer->Late = FALSE; + CTEStartTimer(&pIrdaTimer->CteTimer, pIrdaTimer->Timeout, + TimerFunc, (PVOID) pIrdaTimer); + + DEBUGMSG(DBG_FUNCTION, ("Start timer %s (%dms) context %x\n", + pIrdaTimer->pName, + pIrdaTimer->Timeout, + pIrdaTimer)); + return; +} + +VOID +IrdaTimerStop(PIRDA_TIMER pIrdaTimer) +{ + if (CTEStopTimer(&pIrdaTimer->CteTimer) == 0) + { + pIrdaTimer->Late = TRUE; + } + DEBUGMSG(DBG_FUNCTION, ("Timer %s stopped\n", pIrdaTimer->pName)); + + return; +} + diff --git a/private/ntos/tdi/irda/driver/irndis.c b/private/ntos/tdi/irda/driver/irndis.c new file mode 100644 index 000000000..903e53570 --- /dev/null +++ b/private/ntos/tdi/irda/driver/irndis.c @@ -0,0 +1,706 @@ +/* + * + * + * + * + * + * + * + * + * + * + * + */ + + +//#include +#include +#include +#include +#include +#include + +NDIS_HANDLE NdisIrdaHandle = NULL; + +// Translate an OID query to LAP definition +VOID +OidToLapQos( + UINT ParmTable[], + UINT ValArray[], + UINT Cnt, + PUINT pBitField) +{ + UINT i, j; + + *pBitField = 0; + for (i = 0; i < Cnt; i++) + for (j = 0; j <= PV_TABLE_MAX_BIT; j++) + if (ValArray[i] == ParmTable[j]) + *pBitField |= 1<SyncEvent); + + NdisRequest.RequestType = NdisRequestQueryInformation; + NdisRequest.DATA.QUERY_INFORMATION.Oid = Oid; + NdisRequest.DATA.QUERY_INFORMATION.InformationBuffer = pQBuf; + NdisRequest.DATA.QUERY_INFORMATION.InformationBufferLength = + *pQBufLen * sizeof(UINT); + + NdisRequest(&Status, pIrdaLinkCb->NdisBindingHandle, &NdisRequest); + + if (Status == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pIrdaLinkCb->SyncEvent, 0); + Status = pIrdaLinkCb->SyncStatus; + } + + *pQBufLen = NdisRequest.DATA.QUERY_INFORMATION.BytesWritten / sizeof(UINT); + + return Status; +} + +// Sync request to set an Oid +NDIS_STATUS +IrdaSetOid( + IN PIRDA_LINK_CB pIrdaLinkCb, + IN NDIS_OID Oid, + IN UINT Val) +{ + NDIS_REQUEST NdisRequest; + NDIS_STATUS Status; + + NdisResetEvent(&pIrdaLinkCb->SyncEvent); + + NdisRequest.RequestType = NdisRequestSetInformation; + NdisRequest.DATA.SET_INFORMATION.Oid = Oid; + NdisRequest.DATA.SET_INFORMATION.InformationBuffer = &Val; + NdisRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(UINT); + + NdisRequest(&Status, pIrdaLinkCb->NdisBindingHandle, &NdisRequest); + + if (Status == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pIrdaLinkCb->SyncEvent, 0); + Status = pIrdaLinkCb->SyncStatus; + } + return Status; +} + +// Allocate a message for LAP to use for internally generated frames. +IRDA_MSG * +AllocMacIMsg(PIRDA_LINK_CB pIrdaLinkCb) +{ + NDIS_PHYSICAL_ADDRESS pa = NDIS_PHYSICAL_ADDRESS_CONST(-1, -1); + IRDA_MSG *pIMsg; + + pIMsg = (IRDA_MSG *) NdisInterlockedRemoveHeadList( + &pIrdaLinkCb->IMsgList, &pIrdaLinkCb->SpinLock); + + if (pIMsg == NULL) + { + NdisAllocateMemory(&pIMsg, sizeof(IRDA_MSG) + IRDA_MSG_DATA_SIZE, + 0, pa); + if (pIMsg == NULL) + return NULL; + pIrdaLinkCb->IMsgListLen++; + } + + // Indicate driver owns message + pIMsg->IRDA_MSG_pOwner = &pIrdaLinkCb->IMsgList; + + // Setup the pointers + pIMsg->IRDA_MSG_pHdrWrite = \ + pIMsg->IRDA_MSG_pHdrRead = pIMsg->IRDA_MSG_Header + IRDA_HEADER_LEN; + pIMsg->IRDA_MSG_pBase = \ + pIMsg->IRDA_MSG_pRead = \ + pIMsg->IRDA_MSG_pWrite = (BYTE *) pIMsg + sizeof(IRDA_MSG); + pIMsg->IRDA_MSG_pLimit = pIMsg->IRDA_MSG_pBase + IRDA_MSG_DATA_SIZE-1; + + return pIMsg; +} + +void IrdaRequestComplete( + IN NDIS_HANDLE IrdaBindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS Status + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) IrdaBindingContext; + + DEBUGMSG(DBG_NDIS, ("+IrdaRequestComplete()\n")); + + pIrdaLinkCb->SyncStatus = Status; + NdisSetEvent(&pIrdaLinkCb->SyncEvent); + + return; +} + +VOID IrdaOpenAdapterComplete( + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_STATUS Status, + IN NDIS_STATUS OpenErrorStatus + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) IrdaBindingContext; + + DEBUGMSG(DBG_NDIS, ("+IrdaOpenAdapterComplete() IrdaBindingContext %x, Status %x\n", + IrdaBindingContext, Status)); + + pIrdaLinkCb->SyncStatus = Status; + NdisSetEvent(&pIrdaLinkCb->SyncEvent); + + DEBUGMSG(DBG_NDIS, ("-IrdaOpenAdapterComplete()\n")); + + return; +} + +VOID IrdaCloseAdapterComplete( + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_STATUS Status + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) IrdaBindingContext; + + DEBUGMSG(DBG_NDIS, ("+IrdaCloseAdapterComplete()\n")); + + pIrdaLinkCb->SyncStatus = Status; + NdisSetEvent(&pIrdaLinkCb->SyncEvent); + + DEBUGMSG(DBG_NDIS, ("-IrdaCloseAdapterComplete()\n")); + + return; +} + +VOID IrdaSendComplete( + IN NDIS_HANDLE Context, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS Status + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context; + PIRDA_PROTOCOL_RESERVED ProtocolReserved = \ + (PIRDA_PROTOCOL_RESERVED) NdisPacket->ProtocolReserved; + PIRDA_MSG pIMsg = ProtocolReserved->pIMsg; + PNDIS_BUFFER NdisBuffer; + + if (pIMsg->IRDA_MSG_pOwner == &pIrdaLinkCb->IMsgList) + { + NdisInterlockedInsertTailList(&pIrdaLinkCb->IMsgList, + &pIMsg->Linkage, + &pIrdaLinkCb->SpinLock); + } + + if (NdisPacket){ + NdisUnchainBufferAtFront(NdisPacket, &NdisBuffer); + while (NdisBuffer){ + NdisFreeBuffer(NdisBuffer); + NdisUnchainBufferAtFront(NdisPacket, &NdisBuffer); + } + + NdisFreePacket(NdisPacket); + } + + DEBUGMSG(DBG_NDIS, ("+IrdaSendComplete()\n")); + return; +} + +VOID IrdaTransferDataComplete( + IN NDIS_HANDLE IrdaBindingContext, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) +{ + DEBUGMSG(DBG_NDIS, ("+IrdaTransferDataComplete()\n")); + return; +} + +void IrdaResetComplete( + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_STATUS Status + ) +{ + DEBUGMSG(DBG_NDIS, ("+IrdaResetComplete()\n")); + return; +} + +NDIS_STATUS IrdaReceive( + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_HANDLE MacReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookAheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) +{ + DEBUGMSG(DBG_NDIS, ("+IrdaReceive()\n")); + + return NDIS_STATUS_SUCCESS; +} + +VOID IrdaReceiveComplete( + IN NDIS_HANDLE IrdaBindingContext + ) +{ + DEBUGMSG(DBG_NDIS, ("+IrdaReceiveComplete()\n")); + + return; +} + +VOID IrdaStatus( + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) IrdaBindingContext; + + if (GeneralStatus == NDIS_STATUS_MEDIA_BUSY) + { + DEBUGMSG(DBG_NDIS, ("STATUS_MEDIA_BUSY\n")); + } +#ifdef DEBUG + else + { + DEBUGMSG(DBG_NDIS, ("Unknown Status indication\n")); + } +#endif + + return; +} + +VOID IrdaStatusComplete( + IN NDIS_HANDLE IrdaBindingContext + ) +{ + DEBUGMSG(DBG_NDIS, ("IrdaStatusComplete()\n")); + + return; +} + +INT IrdaReceivePacket( + IN NDIS_HANDLE IrdaBindingContext, + IN PNDIS_PACKET Packet + ) +{ + UINT BufCnt, TotalLen, BufLen; + PNDIS_BUFFER pNdisBuf; + IRDA_MSG IMsg; + BYTE *pData; + PIRDA_LINK_CB pIrdaLinkCb = IrdaBindingContext; + + DEBUGMSG(DBG_NDIS, ("+IrdaReceivePacket(%x)\n", pIrdaLinkCb)); + + NdisQueryPacket(Packet, NULL, &BufCnt, &pNdisBuf, &TotalLen); + + DEBUGMSG(DBG_NDIS, (" BufCnt %d, TotalLen %d\n", BufCnt, TotalLen)); + + NdisQueryBuffer(pNdisBuf, &pData, &BufLen); + + IMsg.Prim = MAC_DATA_IND; + IMsg.IRDA_MSG_pRead = pData; + IMsg.IRDA_MSG_pWrite = pData + BufLen; + + IrlapUp(pIrdaLinkCb->IrlapContext, &IMsg); + + return 0; +} + +VOID IrdaBindAdapter( + OUT PNDIS_STATUS pStatus, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING AdapterName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ) +{ + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM MediumArray[] = {NdisMediumIrda}; + UINT SelectedMediumIndex; + PIRDA_LINK_CB pIrdaLinkCb; + NDIS_PHYSICAL_ADDRESS pa = NDIS_PHYSICAL_ADDRESS_CONST(-1, -1); + UINT UintArray[8]; + UINT UintArrayCnt; + IRDA_MSG *pIMsg; + // ******************************************************* + // ******************************************************* + // TEMP - some these will come out of the registry + IRDA_QOS_PARMS LocalQos; + BYTE DscvInfoBuf[64]; + int DscvInfoLen; + DWORD Val, Mask; + int i; +#define DISCOVERY_HINT_CHARSET 0x820400 +#define DISCOVERY_NICKNAME "Aoxomoxoa" +#define DISCOVERY_NICKNAME_LEN 9 +#define DISCOVERY_SLOTS 8 + + LocalQos.bfBaud = BPS_9600 | BPS_19200 | BPS_115200; + LocalQos.bfMaxTurnTime = MAX_TAT_500; + LocalQos.bfDataSize = DATA_SIZE_64|DATA_SIZE_128|DATA_SIZE_256; + LocalQos.bfWindowSize = FRAMES_1|FRAMES_2|FRAMES_3; + LocalQos.bfBofs = BOFS_3; + LocalQos.bfMinTurnTime = MIN_TAT_10; + LocalQos.bfDisconnectTime = DISC_TIME_12; + + Val = DISCOVERY_HINT_CHARSET; + + // Build the discovery info + DscvInfoLen = 0; + for (i = 0, Mask = 0xFF000000; i < 4; i++, Mask >>= 8) + { + if (Mask & Val || DscvInfoLen > 0) + { + DscvInfoBuf[DscvInfoLen++] = (BYTE) ((Mask & Val) >> (8 * (3-i))); + } + } + memcpy(DscvInfoBuf+DscvInfoLen, DISCOVERY_NICKNAME, DISCOVERY_NICKNAME_LEN); + DscvInfoLen += DISCOVERY_NICKNAME_LEN; + // TEMP ****************************************************** + // ******************************************************* + + DEBUGMSG(1, ("+IrdaBindAdapter() \"%ws\", BindContext %x\n", + AdapterName->Buffer, BindContext)); + + NdisAllocateMemory((PVOID *)&pIrdaLinkCb, sizeof(IRDA_LINK_CB), 0, pa); + + if (!pIrdaLinkCb) + { + *pStatus = STATUS_INSUFFICIENT_RESOURCES; + goto exit10; + } + + NdisZeroMemory(pIrdaLinkCb, sizeof(IRDA_LINK_CB)); + // Add a signature + NdisInitializeEvent(&pIrdaLinkCb->SyncEvent); + NdisResetEvent(&pIrdaLinkCb->SyncEvent); + NdisAllocateSpinLock(&pIrdaLinkCb->SpinLock); + + NdisAllocateBufferPool(pStatus, + &pIrdaLinkCb->BufferPool, + IRDA_NDIS_BUFFER_POOL_SIZE); + if (*pStatus != NDIS_STATUS_SUCCESS) + { + DEBUGMSG(DBG_ERROR, ("NdisAllocateBufferPool failed\n")); + goto error10; // free pIrdaLinkCB + } + + NdisAllocatePacketPool(pStatus, + &pIrdaLinkCb->PacketPool, + IRDA_NDIS_PACKET_POOL_SIZE, + sizeof(IRDA_PROTOCOL_RESERVED)-1 + \ + sizeof(NDIS_IRDA_PACKET_INFO)); + if (*pStatus != NDIS_STATUS_SUCCESS) + { + DEBUGMSG(DBG_ERROR, ("NdisAllocatePacketPool failed\n")); + goto error20; // free pIrdaLinkCb, Buffer pool + } + + NdisInitializeListHead(&pIrdaLinkCb->IMsgList); + + // For internally generated LAP messages + pIrdaLinkCb->IMsgListLen = 0; + for (i = 0; i < IRDA_MSG_LIST_LEN; i++) + { + NdisAllocateMemory(&pIMsg, sizeof(IRDA_MSG) + IRDA_MSG_DATA_SIZE, + 0, pa); + if (pIMsg == NULL) + { + *pStatus = STATUS_INSUFFICIENT_RESOURCES; + goto error40; + } + NdisInterlockedInsertTailList(&pIrdaLinkCb->IMsgList, + &pIMsg->Linkage, + &pIrdaLinkCb->SpinLock); + pIrdaLinkCb->IMsgListLen++; + } + + NdisOpenAdapter( + pStatus, + &OpenErrorStatus, + &pIrdaLinkCb->NdisBindingHandle, + &SelectedMediumIndex, + MediumArray, + 1, + NdisIrdaHandle, + pIrdaLinkCb, + AdapterName, + 0, + NULL); + + DEBUGMSG(DBG_NDIS, ("NdisOpenAdapter(), status %x\n", + pIrdaLinkCb->NdisBindingHandle, *pStatus)); + + if (*pStatus == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pIrdaLinkCb->SyncEvent, 0); + *pStatus = pIrdaLinkCb->SyncStatus; + } + + if (*pStatus != NDIS_STATUS_SUCCESS) + { + goto error30; // free pIrdaLinkCb, Buffer pool, Packet pool + } + + // Query adapters capabilities + + UintArrayCnt = sizeof(UintArray)/sizeof(UINT); + *pStatus = IrdaQueryOid(pIrdaLinkCb, + OID_IRDA_SUPPORTED_SPEEDS, + UintArray, &UintArrayCnt); + if (*pStatus != NDIS_STATUS_SUCCESS) + { + DEBUGMSG(DBG_ERROR, + ("Query IRDA_SUPPORTED_SPEEDS failed %x\n", + *pStatus)); + goto error30; + } + + OidToLapQos(vBaudTable, + UintArray, + UintArrayCnt, + &LocalQos.bfBaud); + + UintArrayCnt = sizeof(UintArray)/sizeof(UINT); + *pStatus = IrdaQueryOid(pIrdaLinkCb, + OID_IRDA_TURNAROUND_TIME, + UintArray, &UintArrayCnt); + + if (*pStatus != NDIS_STATUS_SUCCESS) + { + DEBUGMSG(DBG_ERROR, + ("Query IRDA_SUPPORTED_SPEEDS failed %x\n", + *pStatus)); + goto error30; + } + + OidToLapQos(vMinTATTable, + UintArray, + UintArrayCnt, + &LocalQos.bfMinTurnTime); + + IrlapOpenLink(pStatus, + pIrdaLinkCb, + &LocalQos, + DscvInfoBuf, + DscvInfoLen, + DISCOVERY_SLOTS); + + if (*pStatus != STATUS_SUCCESS) + { + goto error30; + } + + InsertTailList(&IrdaLinkCbList, &pIrdaLinkCb->Linkage); + + goto exit10; + +error40: + + pIMsg = (IRDA_MSG *) NdisInterlockedRemoveHeadList( + &pIrdaLinkCb->IMsgList, &pIrdaLinkCb->SpinLock); + + while (pIMsg != NULL) + { + NdisFreeMemory(pIMsg, sizeof(IRDA_MSG) + IRDA_MSG_DATA_SIZE, 0); + pIMsg = (IRDA_MSG *) NdisInterlockedRemoveHeadList( + &pIrdaLinkCb->IMsgList, &pIrdaLinkCb->SpinLock); + pIMsg = (IRDA_MSG *) RemoveHeadList(&pIrdaLinkCb->IMsgList); + } + +error30: + NdisFreePacketPool(pIrdaLinkCb->PacketPool); + +error20: + NdisFreeBufferPool(pIrdaLinkCb->BufferPool); + +error10: + + NdisFreeMemory(pIrdaLinkCb, sizeof(IRDA_LINK_CB), 0); + +exit10: + DEBUGMSG(DBG_NDIS, ("-IrdaBindAdapter() status %x\n", + *pStatus)); + + return; +} + +VOID IrdaUnbindAdapter( + OUT PNDIS_STATUS pStatus, + IN NDIS_HANDLE IrdaBindingContext, + IN NDIS_HANDLE UnbindContext + ) +{ + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) IrdaBindingContext; + + DEBUGMSG(DBG_NDIS, ("+IrdaUnbindAdapter()\n")); + + NdisInitializeEvent(&pIrdaLinkCb->SyncEvent); + NdisResetEvent(&pIrdaLinkCb->SyncEvent); + + NdisCloseAdapter(pStatus, pIrdaLinkCb->NdisBindingHandle); + + if(*pStatus == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pIrdaLinkCb->SyncEvent, 0); + *pStatus = pIrdaLinkCb->SyncStatus; + } + + if (*pStatus == NDIS_STATUS_SUCCESS){ + NdisFreeMemory(pIrdaLinkCb, sizeof(IRDA_LINK_CB), 0); + } + + DEBUGMSG(DBG_NDIS, ("-IrdaUnbindAdapter() Status %x\n", + *pStatus)); + + return; +} + +VOID IrdaUnload( + VOID + ) +{ + DEBUGMSG(DBG_NDIS, ("+IrdaUnload()\n")); + + return; +} + +NTSTATUS IrdaNdisInitialize() +{ + NDIS_STATUS Status; + NDIS40_PROTOCOL_CHARACTERISTICS pc; + NDIS_STRING ProtocolName = NDIS_STRING_CONST("IRDA"); + UINT ProtocolReservedLength; + + DEBUGMSG(DBG_NDIS,("+IrdaNdisInitialize()\n")); + + NdisZeroMemory((PVOID)&pc, sizeof(NDIS40_PROTOCOL_CHARACTERISTICS)); + pc.MajorNdisVersion = 0x04; + pc.MinorNdisVersion = 0x00; + pc.OpenAdapterCompleteHandler = IrdaOpenAdapterComplete; + pc.CloseAdapterCompleteHandler = IrdaCloseAdapterComplete; + pc.SendCompleteHandler = IrdaSendComplete; + pc.TransferDataCompleteHandler = IrdaTransferDataComplete; + pc.ResetCompleteHandler = IrdaResetComplete; + pc.RequestCompleteHandler = IrdaRequestComplete; + pc.ReceiveHandler = IrdaReceive; + pc.ReceiveCompleteHandler = IrdaReceiveComplete; + pc.StatusHandler = IrdaStatus; + pc.StatusCompleteHandler = IrdaStatusComplete; + pc.BindAdapterHandler = IrdaBindAdapter; + pc.UnbindAdapterHandler = IrdaUnbindAdapter; + pc.UnloadHandler = IrdaUnload; + pc.Name = ProtocolName; + pc.ReceivePacketHandler = IrdaReceivePacket; + pc.TranslateHandler = NULL; + + NdisRegisterProtocol(&Status, + &NdisIrdaHandle, + (PNDIS_PROTOCOL_CHARACTERISTICS)&pc, + sizeof(NDIS40_PROTOCOL_CHARACTERISTICS)); + + // Do any LAP/LMP initialization here + + DEBUGMSG(DBG_NDIS, ("-IrdaNdisInitialize(), rc %x\n", Status)); + + return Status; +} + +UINT +MacConfigRequest( + PIRDA_LINK_CB pIrdaLinkCb, + PIRDA_MSG pMsg) +{ + switch (pMsg->IRDA_MSG_Op) + { + case MAC_INITIALIZE_LINK: + case MAC_RECONFIG_LINK: + pIrdaLinkCb->ExtraBofs = pMsg->IRDA_MSG_NumBOFs; + pIrdaLinkCb->MinTat = pMsg->IRDA_MSG_MinTat; + return IrdaSetOid(pIrdaLinkCb, + OID_IRDA_LINK_SPEED, + (UINT) pMsg->IRDA_MSG_Baud); + + case MAC_MEDIA_SENSE: + ASSERT(0); + + } + + return SUCCESS; + +} +UINT IrmacDown( + IN PVOID Context, + PIRDA_MSG pMsg) +{ + NDIS_STATUS Status; + PNDIS_PACKET NdisPacket = NULL; + PNDIS_BUFFER NdisBuffer = NULL; + PIRDA_PROTOCOL_RESERVED ProtocolReserved; + PNDIS_IRDA_PACKET_INFO IrdaPacketInfo; + PIRDA_LINK_CB pIrdaLinkCb = (PIRDA_LINK_CB) Context; + + DEBUGMSG(DBG_FUNCTION, ("+IrmacDown()\n")); + + + if (pMsg->Prim == MAC_CONTROL_REQ) + { + return MacConfigRequest(pIrdaLinkCb, pMsg); + } + + NdisAllocatePacket(&Status, &NdisPacket, pIrdaLinkCb->PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + { + return 1; + } + + NdisAllocateBuffer(&Status, &NdisBuffer, pIrdaLinkCb->PacketPool, + pMsg->IRDA_MSG_pHdrRead, + pMsg->IRDA_MSG_pHdrWrite-pMsg->IRDA_MSG_pHdrRead); + if (Status != NDIS_STATUS_SUCCESS) + { + return 1; + } + NdisChainBufferAtFront(NdisPacket, NdisBuffer); + + NdisAllocateBuffer(&Status, &NdisBuffer, pIrdaLinkCb->PacketPool, + pMsg->IRDA_MSG_pRead, + pMsg->IRDA_MSG_pWrite-pMsg->IRDA_MSG_pRead); + if (Status != NDIS_STATUS_SUCCESS) + { + return 1; + } + NdisChainBufferAtBack(NdisPacket, NdisBuffer); + + ProtocolReserved = (PIRDA_PROTOCOL_RESERVED)(NdisPacket->ProtocolReserved); + + ProtocolReserved->pIMsg = pMsg; + + IrdaPacketInfo = (PNDIS_IRDA_PACKET_INFO) \ + (ProtocolReserved->MediaInfo.ClassInformation); + + IrdaPacketInfo->ExtraBOFs = pIrdaLinkCb->ExtraBofs; + IrdaPacketInfo->MinTurnAroundTime = pIrdaLinkCb->MinTat; + + NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(NdisPacket, + &ProtocolReserved->MediaInfo, + sizeof(MEDIA_SPECIFIC_INFORMATION) -1 + + sizeof(NDIS_IRDA_PACKET_INFO)); + NdisSend(&Status, pIrdaLinkCb->NdisBindingHandle, NdisPacket); + + return 0; +} diff --git a/private/ntos/tdi/irda/driver/makefile b/private/ntos/tdi/irda/driver/makefile new file mode 100644 index 000000000..58189757d --- /dev/null +++ b/private/ntos/tdi/irda/driver/makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/irda/driver/sources b/private/ntos/tdi/irda/driver/sources new file mode 100644 index 000000000..70d52d5e4 --- /dev/null +++ b/private/ntos/tdi/irda/driver/sources @@ -0,0 +1,15 @@ +TARGETNAME=irda +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib \ + $(BASEDIR)\public\sdk\lib\*\tdi.lib \ + ..\lib\*\irlap.lib + +INCLUDES=$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;..\..\inc;..\inc + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -DIRDA + +SOURCES= irda.c \ + irndis.c diff --git a/private/ntos/tdi/irda/inc/af_irda.h b/private/ntos/tdi/irda/inc/af_irda.h new file mode 100644 index 000000000..5e5d36b0d --- /dev/null +++ b/private/ntos/tdi/irda/inc/af_irda.h @@ -0,0 +1,100 @@ +// +// this is the header file that describes the IRDA address family +// +// CREATED 4/28: AldenG +// + +#ifndef __AFIRDA__ +#define __AFIRDA__ + +#include + +#define AF_IRDA 22 // see winsock.h +#define PF_IRDA AF_IRDA + +#define SOL_IRLMP 0x00FF + +#define IRLMP_ENUMDEVICES 0x00000010 +#define IRLMP_IAS_SET 0x00000011 +#define IRLMP_IAS_QUERY 0x00000012 +#define IRLMP_SEND_PDU_LEN 0x00000013 +#define IRLMP_EXCLUSIVE_MODE 0x00000014 +#define IRLMP_IRLPT_MODE 0x00000015 +#define IRLMP_9WIRE_MODE 0x00000016 + +#define IAS_ATTRIB_NO_CLASS 0x00000010 +#define IAS_ATTRIB_NO_ATTRIB 0x00000000 +#define IAS_ATTRIB_INT 0x00000001 +#define IAS_ATTRIB_OCTETSEQ 0x00000002 +#define IAS_ATTRIB_STR 0x00000003 + +typedef struct _SOCKADDR_IRDA +{ + u_short irdaAddressFamily; + u_char irdaDeviceID[4]; + char irdaServiceName[25]; +} SOCKADDR_IRDA, *PSOCKADDR_IRDA; + +typedef struct _IRDA_DEVICE_INFO +{ + u_char irdaDeviceID[4]; + char irdaDeviceName[22]; + u_char Reserved[2]; +} IRDA_DEVICE_INFO, *PIRDA_DEVICE_INFO, FAR *LPIRDA_DEVICE_INFO; + +typedef struct _DEVICELIST +{ + ULONG numDevice; + IRDA_DEVICE_INFO Device[1]; +} DEVICELIST, *PDEVICELIST, FAR *LPDEVICELIST; + +typedef struct _IAS_SET +{ + char irdaClassName[61]; + char irdaAttribName[61]; + u_short irdaAttribType; + union + { + int irdaAttribInt; + struct + { + int Len; + u_char OctetSeq[1]; + u_char Reserved[3]; + } irdaAttribOctetSeq; + struct + { + int Len; + u_char CharSet; + u_char UsrStr[1]; + u_char Reserved[2]; + } irdaAttribUsrStr; + } irdaAttribute; +} IAS_SET, *PIAS_SET, FAR *LPIAS_SET; + +typedef struct _IAS_QUERY +{ + u_char irdaDeviceID[4]; + char irdaClassName[61]; + char irdaAttribName[61]; + u_short irdaAttribType; + union + { + int irdaAttribInt; + struct + { + int Len; + u_char OctetSeq[1]; + u_char Reserved[3]; + } irdaAttribOctetSeq; + struct + { + int Len; + u_char CharSet; + u_char UsrStr[1]; + u_char Reserved[2]; + } irdaAttribUsrStr; + } irdaAttribute; +} IAS_QUERY, *PIAS_QUERY, FAR *LPIAS_QUERY; + +#endif // __AFIRDA__ diff --git a/private/ntos/tdi/irda/inc/decdirda.h b/private/ntos/tdi/irda/inc/decdirda.h new file mode 100644 index 000000000..d93b0d9e3 --- /dev/null +++ b/private/ntos/tdi/irda/inc/decdirda.h @@ -0,0 +1,174 @@ +// returns pointers pOutStr +TCHAR *DecodeIRDA(int *pFrameType,// return frame type (-1 = bad frame) + BYTE *pFrameBuf, // pointer to buffer containing IRLAP frame + UINT FrameLen, // length of buffer + TCHAR *pOutStr, // string where decode packet is placed + UINT DecodeLayer,// 2-LAP only, 3-LAP/LMP, 4-LAP/LMP/TTP + BOOL fNoConnAddr,// TRUE->Don't show connection address in str + int DispMode // DISP_ASCII/HEX/BOTH +); + +#define DISP_ASCII 1 +#define DISP_HEX 2 +#define DISP_BOTH 3 + +extern int BaudBitMask; + +#define IRLAP_BOF 0xC0 +#define IRLAP_EOF 0xC1 +#define IRLAP_ESC 0x7D +#define IRLAP_COMP_BIT 0x20 + +#define IRLAP_BROADCAST 0xfe +#define _IRLAP_CMD 0x01 +#define _IRLAP_RSP 0x00 + +#define IRLAP_I_FRM 0x00 +#define IRLAP_S_FRM 0x01 +#define IRLAP_U_FRM 0x03 + +/* +** Unnumbered Frame types with P/F bit set to 0 +*/ +#define IRLAP_UI 0x03 +#define IRLAP_XID_CMD 0x2f +#define IRLAP_TEST 0xe3 +#define IRLAP_SNRM 0x83 +#define IRLAP_DISC 0x43 +#define IRLAP_UA 0x63 +#define IRLAP_FRMR 0x87 +#define IRLAP_DM 0x0f +#define IRLAP_XID_RSP 0xaf + +/* +** Supervisory Frames +*/ +#define IRLAP_RR 0x01 +#define IRLAP_RNR 0x05 +#define IRLAP_REJ 0x09 +#define IRLAP_SREJ 0x0d + + +#define IRLAP_GET_ADDR(addr) (addr >> 1) +#define IRLAP_GET_CRBIT(addr) (addr & 1) +#define IRLAP_GET_PFBIT(cntl) ((cntl >>4) & 1) +#define IRLAP_GET_UCNTL(cntl) (cntl & 0xEF) +#define IRLAP_GET_SCNTL(cntl) (cntl & 0x0F) +#define IRLAP_FRAME_TYPE(cntl) (cntl & 0x01 ? (cntl & 3) : 0) +#define IRLAP_GET_NR(cntl) ((cntl & 0xE0) >> 5) +#define IRLAP_GET_NS(cntl) ((cntl & 0xE) >> 1) + +/* +** XID stuff +*/ +#define XID_DISCV_FORMAT_ID 0x01 +#define XID_NEGPARMS_FORMAT_ID 0x02 + +typedef struct +{ + BYTE SrcAddr[4]; + BYTE DestAddr[4]; + BYTE NoOfSlots:2; + BYTE GenNewAddr:1; + BYTE Reserved:5; + BYTE SlotNo; + BYTE Version; +} XID_DISCV_FORMAT; + +/* +** SNRM +*/ +typedef struct +{ + BYTE SrcAddr[4]; + BYTE DestAddr[4]; + BYTE ConnAddr; + BYTE FirstPI; +} SNRM_FORMAT; + +/* +** UA +*/ +typedef struct +{ + BYTE SrcAddr[4]; + BYTE DestAddr[4]; + BYTE FirstPI; +} UA_FORMAT; + +/* +** LM-PDU stuff +*/ +typedef struct +{ + BYTE DLSAP_SEL:7; + BYTE CntlBit:1; + BYTE SLSAP_SEL:7; + BYTE RsvrdBi1:1; +} LM_HEADER; + +/* LM-PDU frame types */ +#define LM_PDU_CNTL_FRAME 1 +#define LM_PDU_DATA_FRAME 0 + +typedef struct +{ + BYTE OpCode:7; + BYTE ABit:1; +} LM_CNTL_FORMAT; + +/* Opcodes */ +#define LM_PDU_CONNECT 1 +#define LM_PDU_DISCONNECT 2 +#define LM_PDU_ACCESSMODE 3 + +#define LM_PDU_REQUEST 0 +#define LM_PDU_CONFIRM 1 + +#define LM_PDU_SUCCESS 0 +#define LM_PDU_FAILURE 1 +#define LM_PDU_UNSUPPORTED 0xFF + +#define LM_PDU_MULTIPLEXED 0 +#define LM_PDU_EXCLUSIVE 1 + +/* Max disconnect reason code, see _LM_PDU_DscReason[] in decdirda.c */ +#define LM_PDU_MAX_DSC_REASON 0x8 + +/* +** Negotiation Parameter Identifiers +*/ +#define NEG_PI_BAUD 0x01 +#define NEG_PI_MAX_TAT 0x82 +#define NEG_PI_DATA_SZ 0x83 +#define NEG_PI_WIN_SZ 0x84 +#define NEG_PI_BOFS 0x85 +#define NEG_PI_MIN_TAT 0x86 +#define NEG_PI_DISC_THRESH 0x08 + +// Tiny TP! + +#define TTP_PFLAG_NO_PARMS 0 +#define TTP_PFLAG_PARMS 1 + +#define TTP_MBIT_NOT_FINAL 1 +#define TTP_MBIT_FINAL 0 + +typedef struct +{ + BYTE InitialCredit : 7; + BYTE ParmFlag : 1; +} TTP_CONN_HEADER; + +typedef struct +{ + BYTE AdditionalCredit : 7; + BYTE MoreBit : 1; +} TTP_DATA_HEADER; + +#define net_short(x) ((((x)&0xff) << 8) | (((x)&0xff00) >> 8)) + +#define net_long(x) (((((DWORD UNALIGNED)(x))&0xffL)<<24) | \ + ((((DWORD UNALIGNED)(x))&0xff00L)<<8) | \ + ((((DWORD UNALIGNED)(x))&0xff0000L)>>8) | \ + ((((DWORD UNALIGNED)(x))&0xff000000L)>>24)) diff --git a/private/ntos/tdi/irda/inc/irda.h b/private/ntos/tdi/irda/inc/irda.h new file mode 100644 index 000000000..ab9d55674 --- /dev/null +++ b/private/ntos/tdi/irda/inc/irda.h @@ -0,0 +1,857 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irda.h +* +* Description: Definitions used across the IRDA stack +* +* Author: mbert +* +* Date: 4/15/95 +* +* This file primarily defines the IRDA message (IRDA_MSG) used for +* communicating with the stack and communication between the layers +* of the stack. IRDA_MSG provides the following services: +* MAC_CONTROL_SERVICE +* IRLAP_DISCOVERY_SERVICE +* IRDA_DISCONNECT_SERVICE +* IRDA_CONNECT_SERVICE +* IRDA_DATA_SERVICE +* IRLMP_ACCESSMODE_SERVICE +* IRLMP_IAS_SERVICE +* +* IRDA_MSG usage: +* +* +-------+ +* | IRLAP | +* +-------+ +* | +* | IRMAC_Down(IRDA_MSG) +* \|/ +* +-------+ +* | IRMAC | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | MAC_DATA_REQ | IRDA_DATA_SERVICE | +* | | o IRDA_MSG_pHdrRead = start of IRDA headers | +* | | o IRDA_MSG_pHdrWrite = end of header | +* | | o IRDA_MSG_pRead = start of data | +* | | o IRDA_MSG_pWrite = end of data | +* |--------------------------+-----------------------------------------------| +* | MAC_CONTROL_REQ | MAC_CONTROL_SERVICE | +* | | o IRDA_MSG_Op = MAC_INITIALIZIE_LINK | +* | | - IRDA_MSG_Port | +* | | - IRDA_MSG_Baud | +* | | - IRDA_MSG_MinTat = min turn time | +* | | - IRDA_MSG_NumBOFs = # added when tx'ing | +* | | - IRDA_MSG_DataSize = max rx frame | +* | | - IRDA_MSG_SetIR = TRUE/FALSE (does an | +* | | EscapeComm(SETIR) to select int/ext | +* | | dongle) | +* | | o IRDA_MSG_Op = MAC_MEDIA_SENSE | +* | | - IRDA_MSG_SenseTime (in ms) | +* | | o IRDA_MSG_Op = MAC_RECONFIG_LINK | +* | | - IRDA_MSG_Baud | +* | | - IRDA_MSG_NumBOFs = # added when tx'ing | +* | | - IRDA_MSG_DataSize = max rx frame | +* | | - IRDA_MSG_MinTat = min turn time | +* | | o IRDA_MSG_OP = MAC_SHUTDOWN_LINK | +* |--------------------------------------------------------------------------| +* +* +-------+ +* | IRLAP | +* +-------+ +* /|\ +* | IRLAP_Up(IRDA_MSG) +* | +* +-------+ +* | IRMAC | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | MAC_DATA_IND | IRDA_DATA_SERVICE | +* | | o IRDA_MSG_pRead = start of frame | +* | | (includes IRLAP header) | +* | | o IRDA_MSG_pWrite = end of frame | +* | | (excludes FCS) | +* |--------------------------+-----------------------------------------------| +* | MAC_CONTROL_CONF | MAC_CONTROL_SERVICE | +* | | o IRDA_MSG_Op = MAC_MEDIA_SENSE | +* | | - IRDA_MSG_OpStatus = MAC_MEDIA_BUSY | +* | | MAC_MEDIA_CLEAR | +* |--------------------------------------------------------------------------| +* +* +-------+ +* | IRLMP | +* +-------+ +* | +* | IRLAP_Down(IRDA_MSG) +* \|/ +* +-------+ +* | IRLAP | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | IRLAP_DISCOVERY_REQ | IRLAP_DISCOVERY_SERVICE | +* | IRLAP_Down() returns | o IRDA_MSG_SenseMedia = TRUE/FALSE | +* | IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR or | +* | IRLAP_REMOTE_CONNECT_IN_PROGRESS_ERR when indicated | +* |--------------------------+-----------------------------------------------| +* | IRLAP_CONNECT_REQ | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_RemoteDevAddr | +* | IRLAP_Down() returns | | +* | IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR when indicated | +* |--------------------------+-----------------------------------------------| +* | IRLAP_CONNECT_RESP | no parms | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DISCONNECT_REQ | no parms | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DATA_REQ | IRDA_DATA_SERVICE | +* | IRLAP_UDATA_REQ | o IRDA_MSG_pHdrRead = start of IRLMP header | +* | IRLAP_Down() returns | o IRDA_MSG_pHdrWrite = end of header | +* | IRLAP_REMOTE_BUSY to | o IRDA_MSG_pRead = start of data | +* | to flow off LMP. | o IRDA_MSG_pWrite = end of data | +* |--------------------------------------------------------------------------| +* | IRLAP_FLOWON_REQ | no parms | +* |--------------------------------------------------------------------------| +* +* +-------+ +* | IRLMP | +* +-------+ +* /|\ +* | IRLMP_Up(IRDA_MSG) +* | +* +-------+ +* | IRLAP | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | IRLAP_DISCOVERY_IND | IRLAP_DISCOVERY_SERVICE | +* | | o pDevList = Discovery info of device that | +* | | initiated discovery | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DISCOVERY_CONF | IRLAP_DISCOVERY_SERVICE | +* | | o IRDA_MSG_pDevList = list of discovered | +* | | devices, NULL when | +* | | status != IRLAP_DISCOVERY_COMPLETED | +* | | o IRDA_MSG_DscvStatus = | +* | | MAC_MEDIA_BUSY | +* | | IRLAP_REMOTE_DISCOVERY_IN_PROGRESS | +* | | IRLAP_DISCOVERY_COLLISION | +* | | IRLAP_REMOTE_CONNECTION_IN_PROGRESS | +* | | IRLAP_DISCOVERY_COMPLETED | +* |--------------------------+-----------------------------------------------| +* | IRLAP_CONNECT_IND | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_RemoteDevAddr | +* | | o IRDA_MSG_pQOS = Negotiated QOS | +* |--------------------------------------------------------------------------| +* | IRLAP_CONNECT_CONF | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_pQOS = Negotiated QOS, only when | +* | | successful | +* | | o IRDA_MSG_ConnStatus = | +* | | IRLAP_CONNECTION_COMPLETE | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DISCONNECT_IND | IRDA_DISCONNECT_SERVICE | +* | | o IRDA_MSG_DiscStatus = | +* | | IRLAP_DISCONNECT_COMPLETED | +* | | IRLAP_REMOTED_INITIATED | +* | | IRLAP_PRIMARY_CONFLICT | +* | | IRLAP_REMOTE_DISCOVERY_IN_PROGRESS | +* | | IRLAP_NO_RESPONSE | +* | | IRLAP_DECLINE_RESET | +* | | MAC_MEDIA_BUSY | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DATA_IND | IRDA_DATA_SERVICE | +* | IRLAP_UDATA_IND | o IRDA_MSG_pRead = start of IRLMP packet | +* | | o IRDA_MSG_pWrite = end of IRLMP packet | +* |--------------------------+-----------------------------------------------| +* | IRLAP_DATA_CONF | IRDA_DATA_SERVICE | +* | IRLAP_UDATA_CONF | o IRDA_MSG_DataStatus = | +* | | ILAP_DATA_REQUEST_COMPLETED | +* | | IRLAP_DATA_REQUEST_FAILED_LINK_RESET | +* |--------------------------+-----------------------------------------------| +* | IRLAP_STATUS_IND | no parms | +* |--------------------------------------------------------------------------| +* +* +--------------+ +* | TransportAPI | +* +--------------+ +* | +* | IRLMP_Down(IRLMPContext, IRDA_MSG) +* \|/ +* +-------+ +* | IRLMP | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | IRLMP_DISCOVERY_REQ | no parms | +* |--------------------------+-----------------------------------------------| +* | IRLMP_CONNECT_REQ | IRDA_CONNECT_SERVICE | +* | IRLMP_Down() returns | o IRDA_MSG_RemoteDevAddr | +* | IRLMP_LINK_IN_USE | o IRDA_MSG_RemoteLSAPSel | +* | when the requested | o IRDA_MSG_pQOS (may be NULL) | +* | connection is to a | o IRDA_MSG_pConnData | +* | remote device other | o IRDA_MSG_ConnDataLen | +* | than the one the link | o IRDA_MSG_LocalLSAPSel | +* | is currently connected | o IRDA_MSG_pContext | +* | or connecting to. | o IRDA_MSG_UseTTP | +* | | o IRDA_MSG_TTPCredits | +* | | o IRDA_MSG_MaxSDUSize - Max size that this | +* | | IRLMP client can receive. | +* |--------------------------+-----------------------------------------------| +* | IRLMP_CONNECT_RESP | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_pConnData | +* | | o IRDA_MSG_ConnDataLen | +* | | o IRDA_MSG_pContext | +* | | o IRDA_MSG_MaxSDUSize - Max size that this | +* | | IRLMP client can receive. | +* | | o IRDA_MSG_TTPCredits | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DISCONNECT_REQ | IRDA_DISCONNECT_SERVICE | +* | | o IRDA_MSG_pDiscData | +* | | o IRDA_MSG_DiscDataLen | +* | | | +* | | | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DATA/UDATA_REQ | IRDA_DATA_SERVICE | +* | IRLMP_Down() may return| o IRDA_MSG_pDataContext = ptr to NDIS_BUFFER| +* | IRLMP_REMOTE_BUSY, | o IRDA_MSG_IrCOMM_9Wire = TRUE/FALSE | +* | when tx cred exhausted| | +* | in multiplexed mode. | | +* | IRLAP_REMOTE_BUSY, | | +* | when remote IRLAP | | +* | flowed off in exclMode| | +* | In either case the req | | +* | was successful. | | +* |--------------------------------------------------------------------------| +* | IRLMP_ACCESSMODE_REQ | IRLMP_ACCESSMODE_SERVICE | +* | IRLMP_Down() may return| o IRDA_MSG_AccessMode = IRLMP_MULTIPLEXED | +* | IRLMP_IN_EXCLUSIVE_MODE| IRLMP_EXCLUSIVE | +* | if already in excl-mode| o IRDA_MSG_IrLPTMode - TRUE, doesn't send | +* | IRLMP_IN_MULTIPLEXED...| the Access PDU | +* | if other LSAPs exist or| | +* | requesting trans to this state when already in it. | +* |--------------------------------------------------------------------------| +* | IRLMP_FLOWON_REQ | no parms | +* |--------------------------------------------------------------------------| +* | IRLMP_MORECREDIT_REQ | IRDA_CONNECT_SERVICE (cuz parm is defined) | +* | | o IRDA_MSG_TTPCredits | +* |--------------------------------------------------------------------------| +* | IRLMP_GETVALUEBYCLASS_REQ| IRDA_IAS_SERVICE | +* | | o IRDA_MSG_pIASQuery | +* | | o IRDA_MSG_AttribLen | +* | | o IRDA_MSG_IASQueryPerms | +* |--------------------------------------------------------------------------| +* +* +* +--------------+ +* | TransportAPI | +* +--------------+ +* /|\ +* | TransportAPI_Up(TransportAPIContext, IRDA_MSG) +* | +* +-------+ +* | IRLMP | +* +-------+ +* |**************************************************************************| +* | Prim | MsgType and parameters | +* |==========================================================================| +* | IRLAP_DISCOVERY_IND | IRLAP_DISCOVERY_SERVICE | +* | | o pDevList = aged Discovery list | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DISCOVERY_CONF | same as IRLAP_DISCOVERY_CONF. The device list | +* | | however is the one maintained in IRLMP | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DISCONNECT_IND | IRDA_DISCONNECT_SERVICE | +* | | o IRDA_MSG_DiscReason = | +* | | see IRLMP_DISC_REASON below | +* | | o IRDA_MSG_pDiscData - may be NULL | +* | | o IRDA_MSG_DiscDataLen | +* |--------------------------+-----------------------------------------------| +* | IRLMP_CONNECT_IND | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_RemoteDevAddr | +* | | o IRDA_MSG_RemoteLSAPSel; | +* | | o IRDA_MSG_LocalLSAPSel; | +* | | o IRDA_MSG_pQOS | +* | | o IRDA_MSG_pConnData | +* | | o IRDA_MSG_ConnDataLen | +* | | o IRDA_MSG_pContext | +* | | o IRDA_MSG_MaxSDUSize - Max size that this | +* | | IRLMP client can send to peer | +* | | o IRDA_MSG_MaxPDUSize | +* |--------------------------+-----------------------------------------------| +* | IRLMP_CONNECT_CONF | IRDA_CONNECT_SERVICE | +* | | o IRDA_MSG_pQOS | +* | | o IRDA_MSG_pConnData | +* | | o IRDA_MSG_ConnDataLen | +* | | o IRDA_MSG_pContext | +* | | o IRDA_MSG_MaxSDUSize - Max size that this | +* | | IRLMP client can send to peer | +* | | o IRDA_MSG_MaxPDUSize | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DATA_IND | IRDA_DATA_SERVICE | +* | | o IRDA_MSG_pRead = start of User Data | +* | | o IRDA_MSG_pWrite = end of User Data | +* | | o IRDA_MSG_FinalSeg = TRUE/FALSE | +* |--------------------------+-----------------------------------------------| +* | IRLMP_DATA_CONF | IRDA_DATA_SERVICE | +* | | o IRDA_MSG_pDataContext = ptr to NDIS_BUFFER| +* | | o IRDA_MSG_DataStatus = | +* | | IRLMP_DATA_REQUEST_COMPLETED | +* | | IRLMP_DATA_REQUEST_FAILED | +* |--------------------------+-----------------------------------------------| +* | IRLMP_ACCESSMODE_IND | IRLMP_ACCESSMODE_SERVICE | +* | | o IRDA_MSG_AccessMode = | +* | | IRLMP_EXCLUSIVE | +* | | IRLMP_MULTIPLEXED | +* |--------------------------+-----------------------------------------------| +* | IRLMP_ACCESSMODE_CONF | IRLMP_ACCESSMODE_SERVICE | +* | | o IRDA_MSG_AccessMode = | +* | | IRLMP_EXCLUSIVE | +* | | IRLMP_MULTIPLEXED | +* | | o IRDA_MSG_ModeStatus = | +* | | IRLMP_ACCESSMODE_SUCCESS | +* | | IRLMP_ACCESSMODE_FAILURE | +* |--------------------------+-----------------------------------------------| +* |IRLMP_GETVALUEBYCLASS_CONF| IRDA_DATA_SERVICE | +* | | o IRDA_MSG_pIASQuery | +* | | o IRDA_MSG_IASStatus = An IRLMP_DISC_REASON | +* | | (see below) | +* |--------------------------------------------------------------------------| +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + +#define TEMPERAMENTAL_SERIAL_DRIVER // drivers busted. intercharacter delays cause + // IrLAP to reset. + +#ifdef DEBUG +// Prototypes for Debugging Output +void IRDA_DebugOut (TCHAR *pFormat, ...); +void IRDA_DebugStartLog (void); +void IRDA_DebugEndLog (void *, void *); +#endif + +// Debug zone definitions. +/* +#define ZONE_IRDA DEBUGZONE(0) +#define ZONE_IRLAP DEBUGZONE(2) +#ifdef PEG +#define ZONE_IRMAC DEBUGZONE(1) +#define ZONE_IRLMP DEBUGZONE(3) +#define ZONE_IRLMP_CONN DEBUGZONE(4) +#define ZONE_IRLMP_CRED DEBUGZONE(5) +#else +extern int ZONE_IRMAC; +extern int ZONE_IRLMP; +extern int ZONE_IRLMP_CONN; +extern int ZONE_IRLMP_CRED; +#endif +#define ZONE_DISCOVER DEBUGZONE(8) +#define ZONE_PRINT DEBUGZONE(9) +#define ZONE_ADDR DEBUGZONE(10) +#define ZONE_MISC DEBUGZONE(11) +#define ZONE_ALLOC DEBUGZONE(12) +#define ZONE_FUNCTION DEBUGZONE(13) +#define ZONE_WARN DEBUGZONE(14) +#define ZONE_ERROR DEBUGZONE(15) +*/ + + +#define IRDA_ALLOC_MEM(ptr, sz, id) ((ptr) = CTEAllocMem(sz)) +#define IRDA_FREE_MEM(ptr) CTEFreeMem((ptr)) + +//extern CRITICAL_SECTION IrdaCS; + +// Time how low we wait for the critical section +/* +#define ENTER_IRDA_WITH_CRITICAL_SECTION(s) do { \ + LPWSTR Owner = IrdaCSOwner; \ + DWORD StartTick = GetTickCount(); \ + EnterCriticalSection (&IrdaCS); \ + IrdaCSOwner = s; \ + StartTick = GetTickCount() - StartTick; \ + if (StartTick > MaxWaitIrdaCS) { \ + DEBUGMSG (1, (TEXT("%s: IRDA Wait for CS %dms (Owner=%s)\r\n"), \ + s, StartTick, Owner)); \ + MaxWaitIrdaCS = StartTick; \ + } \ + } while (0) + + +#define LEAVE_IRDA_WITH_CRITICAL_SECTION IrdaCSOwner = TEXT(""), LeaveCriticalSection(&IrdaCS) + +#else // TIME_CS +#define ENTER_IRDA_WITH_CRITICAL_SECTION(s) EnterCriticalSection(&IrdaCS) + +#define LEAVE_IRDA_WITH_CRITICAL_SECTION LeaveCriticalSection(&IrdaCS) +#endif // TIME_CS +*/ +#define STATIC static + +#define RetOnErr(func) do {if((_rc = func) != SUCCESS) return _rc;} while(0) + +typedef struct +{ + CTETimer CteTimer; + VOID (*ExpFunc)(PVOID Context); + PVOID Context; + UINT Timeout; + BOOL Late; +#ifdef DEBUG + char *pName; +#endif +} IRDA_TIMER, *PIRDA_TIMER; + +#define IRMAC_CONTEXT(ilcb) ((ilcb)->IrmacContext) +#define IRLAP_CONTEXT(ilcb) ((ilcb)->IrlapContext) +#define IRLMP_CONTEXT(ilcb) ((ilcb)->IrlmpContext) + +// Device/Discovery Information +#define IRLAP_DSCV_INFO_LEN 32 +#define IRDA_DEV_ADDR_LEN 4 + +typedef struct +{ + LIST_ENTRY Linkage; + BYTE DevAddr[IRDA_DEV_ADDR_LEN]; + int DscvMethod; + int IRLAP_Version; + BYTE DscvInfo[IRLAP_DSCV_INFO_LEN]; + int DscvInfoLen; + int NotSeenCnt; // used by IRLMP to determine when to remove + // the device from its list + PVOID LinkContext; // Link on which device was discovered +} IRDA_DEVICE; + +// IRLAP Quality of Service +#define BIT_0 1 +#define BIT_1 2 +#define BIT_2 4 +#define BIT_3 8 +#define BIT_4 16 +#define BIT_5 32 +#define BIT_6 64 +#define BIT_7 128 +#define BIT_8 256 + +#define BPS_2400 BIT_0 // Baud Rates +#define BPS_9600 BIT_1 +#define BPS_19200 BIT_2 +#define BPS_38400 BIT_3 +#define BPS_57600 BIT_4 +#define BPS_115200 BIT_5 +#define BPS_4000000 BIT_8 + +#define MAX_TAT_500 BIT_0 // Maximum Turnaround Time (millisecs) +#define MAX_TAT_250 BIT_1 +#define MAX_TAT_100 BIT_2 +#define MAX_TAT_50 BIT_3 +#define MAX_TAT_25 BIT_4 +#define MAX_TAT_10 BIT_5 +#define MAX_TAT_5 BIT_6 + +#define DATA_SIZE_64 BIT_0 // Data Size (bytes) +#define DATA_SIZE_128 BIT_1 +#define DATA_SIZE_256 BIT_2 +#define DATA_SIZE_512 BIT_3 +#define DATA_SIZE_1024 BIT_4 +#define DATA_SIZE_2048 BIT_5 + +#define FRAMES_1 BIT_0 // Window Size +#define FRAMES_2 BIT_1 +#define FRAMES_3 BIT_2 +#define FRAMES_4 BIT_3 +#define FRAMES_5 BIT_4 +#define FRAMES_6 BIT_5 +#define FRAMES_7 BIT_6 + +#define BOFS_48 BIT_0 // Additional Beginning of Frame Flags +#define BOFS_24 BIT_1 +#define BOFS_12 BIT_2 +#define BOFS_5 BIT_3 +#define BOFS_3 BIT_4 +#define BOFS_2 BIT_5 +#define BOFS_1 BIT_6 +#define BOFS_0 BIT_7 + +#define MIN_TAT_10 BIT_0 // Minumum Turnaround Time (millisecs) +#define MIN_TAT_5 BIT_1 +#define MIN_TAT_1 BIT_2 +#define MIN_TAT_0_5 BIT_3 +#define MIN_TAT_0_1 BIT_4 +#define MIN_TAT_0_05 BIT_5 +#define MIN_TAT_0_01 BIT_6 +#define MIN_TAT_0 BIT_7 + +#define DISC_TIME_3 BIT_0 // Link Disconnect/Threshold Time (seconds) +#define DISC_TIME_8 BIT_1 +#define DISC_TIME_12 BIT_2 +#define DISC_TIME_16 BIT_3 +#define DISC_TIME_20 BIT_4 +#define DISC_TIME_25 BIT_5 +#define DISC_TIME_30 BIT_6 +#define DISC_TIME_40 BIT_7 + +typedef struct +{ + UINT bfBaud; + UINT bfMaxTurnTime; + UINT bfDataSize; + UINT bfWindowSize; + UINT bfBofs; + UINT bfMinTurnTime; + UINT bfDisconnectTime; // holds threshold time also +} IRDA_QOS_PARMS; + + +// IrDA Message Primitives +typedef enum +{ + MAC_DATA_REQ = 0, // Keep in sync with table in irlaplog.c + MAC_DATA_IND, + MAC_CONTROL_REQ, + MAC_CONTROL_CONF, + IRLAP_DISCOVERY_REQ, + IRLAP_DISCOVERY_IND, + IRLAP_DISCOVERY_CONF, + IRLAP_CONNECT_REQ, + IRLAP_CONNECT_IND, + IRLAP_CONNECT_RESP, + IRLAP_CONNECT_CONF, + IRLAP_DISCONNECT_REQ, + IRLAP_DISCONNECT_IND, + IRLAP_DATA_REQ, // Don't fuss with the order, CONF must be 2 from REQ + IRLAP_DATA_IND, + IRLAP_DATA_CONF, + IRLAP_UDATA_REQ, + IRLAP_UDATA_IND, + IRLAP_UDATA_CONF, + IRLAP_STATUS_IND, + IRLAP_FLOWON_REQ, + IRLAP_FLOWON_IND, + IRLMP_DISCOVERY_REQ, + IRLMP_DISCOVERY_IND, + IRLMP_DISCOVERY_CONF, + IRLMP_CONNECT_REQ, + IRLMP_CONNECT_IND, + IRLMP_CONNECT_RESP, + IRLMP_CONNECT_CONF, + IRLMP_DISCONNECT_REQ, + IRLMP_DISCONNECT_IND, + IRLMP_DATA_REQ, + IRLMP_DATA_IND, + IRLMP_DATA_CONF, + IRLMP_UDATA_REQ, + IRLMP_UDATA_IND, + IRLMP_UDATA_CONF, + IRLMP_ACCESSMODE_REQ, + IRLMP_ACCESSMODE_IND, + IRLMP_ACCESSMODE_CONF, + IRLMP_FLOWON_REQ, + IRLMP_FLOWON_IND, + IRLMP_MORECREDIT_REQ, + IRLMP_GETVALUEBYCLASS_REQ, + IRLMP_GETVALUEBYCLASS_CONF +} IRDA_SERVICE_PRIM; + +typedef enum +{ + MAC_MEDIA_BUSY, // keep in sync with IRDA_StatStr in irlaplog.c + MAC_MEDIA_CLEAR, + IRLAP_DISCOVERY_COLLISION, + IRLAP_REMOTE_DISCOVERY_IN_PROGRESS, + IRLAP_REMOTE_CONNECT_IN_PROGRSS, + IRLAP_DISCOVERY_COMPLETED, + IRLAP_REMOTE_CONNECTION_IN_PROGRESS, + IRLAP_CONNECTION_COMPLETED, + IRLAP_REMOTE_INITIATED, + IRLAP_PRIMARY_CONFLICT, + IRLAP_DISCONNECT_COMPLETED, + IRLAP_NO_RESPONSE, + IRLAP_DECLINE_RESET, + IRLAP_DATA_REQUEST_COMPLETED, + IRLAP_DATA_REQUEST_FAILED_LINK_RESET, + IRLAP_DATA_REQUEST_FAILED_REMOTE_BUSY, + IRLMP_NO_RESPONSE, + IRLMP_ACCESSMODE_SUCCESS, + IRLMP_ACCESSMODE_FAILURE, + IRLMP_DATA_REQUEST_COMPLETED, + IRLMP_DATA_REQUEST_FAILED +} IRDA_SERVICE_STATUS; + +// MAC Control Service Request Message - MAC_CONTROL_REQ/CONF +typedef enum +{ + MAC_INITIALIZE_LINK, // keep in sync with MAC_OpStr in irlaplog.c + MAC_SHUTDOWN_LINK, + MAC_RECONFIG_LINK, + MAC_MEDIA_SENSE, +} MAC_CONTROL_OPERATION; + +typedef struct +{ + MAC_CONTROL_OPERATION Op; + int Port; + int Baud; + int NumBOFs; + int MinTat; + int DataSize; + int SenseTime; + IRDA_SERVICE_STATUS OpStatus; + BOOL SetIR; +} MAC_CONTROL_SERVICE; + +// IRLAP Discovery Service Request Message - IRLAP_DISCOVERY_IND/CONF +typedef struct +{ + LIST_ENTRY *pDevList; + IRDA_SERVICE_STATUS DscvStatus; + BOOL SenseMedia; +} IRLAP_DISCOVERY_SERVICE; + +// IRDA Connection Service Request Message - IRLAP_CONNECT_REQ/IND/CONF +// IRLMP_CONNECT_REQ/CONF +typedef struct +{ + BYTE RemoteDevAddr[IRDA_DEV_ADDR_LEN]; + IRDA_QOS_PARMS *pQOS; + int LocalLSAPSel; + int RemoteLSAPSel; + BYTE *pConnData; + int ConnDataLen; + void *pContext; + int MaxPDUSize; + int MaxSDUSize; + int TTPCredits; + IRDA_SERVICE_STATUS ConnStatus; + BOOL UseTTP; +} IRDA_CONNECT_SERVICE; + +// IRDA Disconnection Service Request Message - IRLAP_DISCONNECT_REQ/IND +// IRLMP_DISCONNECT_REQ/IND +typedef enum +{ + IRLMP_USER_REQUEST = 1, + IRLMP_UNEXPECTED_IRLAP_DISC, + IRLMP_IRLAP_CONN_FAILED, + IRLMP_IRLAP_RESET, + IRLMP_LM_INITIATED_DISC, + IRLMP_DISC_LSAP, + IRLMP_NO_RESPONSE_LSAP, + IRLMP_NO_AVAILABLE_LSAP, + IRLMP_MAC_MEDIA_BUSY, + IRLMP_IRLAP_REMOTE_DISCOVERY_IN_PROGRESS, + + IRLMP_IAS_NO_SUCH_OBJECT, // these are added for the IAS_GetValueByClass.Conf + IRLMP_IAS_NO_SUCH_ATTRIB, + IRLMP_IAS_SUCCESS, + IRLMP_IAS_SUCCESS_LISTLEN_GREATER_THAN_ONE, + + IRLMP_UNSPECIFIED_DISC = 0xFF +} IRLMP_DISC_REASON; + +typedef struct +{ + BYTE *pDiscData; // IRLMP_DISCONNECT_REQ/IND only + int DiscDataLen; // IRLMP_DISCONNECT_REQ/IND only + IRLMP_DISC_REASON DiscReason; // IRLMP_DISCONNECT_REQ/IND only + IRDA_SERVICE_STATUS DiscStatus; // Indication only +} IRDA_DISCONNECT_SERVICE; + +// IRDA Data Service Request Message +#define IRLAP_HEADER_LEN 2 +#define IRLMP_HEADER_LEN 6 +#define TTP_HEADER_LEN 8 +#define IRDA_HEADER_LEN IRLAP_HEADER_LEN+IRLMP_HEADER_LEN+TTP_HEADER_LEN+1 + // + 1 IRComm WACK!! + +typedef struct +{ + void *pOwner; + void *pDataContext; // How IRDA gets user data + int SegCount; // Number of segments + BOOL FinalSeg; + BYTE *pBase; + BYTE *pLimit; + BYTE *pRead; + BYTE *pWrite; + void *pTdiSendComp; + void *pTdiSendCompCnxt; + BOOL IrCOMM_9Wire; +#ifdef TEMPERAMENTAL_SERIAL_DRIVER + int FCS; +#endif + IRDA_SERVICE_STATUS DataStatus; // for CONF + // |------------------------| + // | pRead o------------- + // |------------------------| | + // | pWrite o---------- | + // |------------------------| | | + // | pBase o------- | | + // |------------------------| | | | + // | pLimit o---- | | | + // |------------------------| | | | | + // | | | | | + // ------------------------ | | | | + // | |<---- | | + // | | | | | + // | |<--------<- + // | | | + // | |<- + // ------------------------ + BYTE *pHdrRead; + BYTE *pHdrWrite; + BYTE Header[IRDA_HEADER_LEN]; + // |------------------------| + // | pHdrRead o------------- + // |------------------------| | + // | pHdrWrite o---------- | + // |------------------------| | | + // Header--->| | | | + // | | | | + // | |<--------<- + // | | | + // | |<------- + // ------------------------ + // + // On the receive side, all headers are contained + // at pRead, not in the above Header array + // +} IRDA_DATA_SERVICE; + +typedef enum +{ + IRLMP_MULTIPLEXED, + IRLMP_EXCLUSIVE +} IRLMP_ACCESSMODE; + +typedef struct +{ + IRLMP_ACCESSMODE AccessMode; + IRDA_SERVICE_STATUS ModeStatus; + BOOL IrLPTMode; // if true don't send PDU +} IRLMP_ACCESSMODE_SERVICE; + +typedef struct +{ + IAS_QUERY *pIASQuery; + int AttribLen; // OctetSeq or UsrStr len + int IASQueryPerms; + IRLMP_DISC_REASON IASStatus; +} IRLMP_IAS_SERVICE; + +typedef struct irda_msg +{ + LIST_ENTRY Linkage; + IRDA_SERVICE_PRIM Prim; + union + { + MAC_CONTROL_SERVICE MAC_ControlService; + IRLAP_DISCOVERY_SERVICE IRLAP_DiscoveryService; + IRDA_DISCONNECT_SERVICE IRDA_DisconnectService; + IRDA_CONNECT_SERVICE IRDA_ConnectService; + IRDA_DATA_SERVICE IRDA_DataService; + IRLMP_ACCESSMODE_SERVICE IRLMP_AccessModeService; + IRLMP_IAS_SERVICE IRLMP_IASService; + } MsgType; + +} IRDA_MSG, *PIRDA_MSG; + +#define IRDA_MSG_Op MsgType.MAC_ControlService.Op +#define IRDA_MSG_Port MsgType.MAC_ControlService.Port +#define IRDA_MSG_Baud MsgType.MAC_ControlService.Baud +#define IRDA_MSG_NumBOFs MsgType.MAC_ControlService.NumBOFs +#define IRDA_MSG_MinTat MsgType.MAC_ControlService.MinTat +#define IRDA_MSG_DataSize MsgType.MAC_ControlService.DataSize +#define IRDA_MSG_OpStatus MsgType.MAC_ControlService.OpStatus +#define IRDA_MSG_SetIR MsgType.MAC_ControlService.SetIR +#define IRDA_MSG_SenseTime MsgType.MAC_ControlService.SenseTime + +#define IRDA_MSG_pOwner MsgType.IRDA_DataService.pOwner +#define IRDA_MSG_pDataContext MsgType.IRDA_DataService.pDataContext +#define IRDA_MSG_SegCount MsgType.IRDA_DataService.SegCount +#define IRDA_MSG_FinalSeg MsgType.IRDA_DataService.FinalSeg +#define IRDA_MSG_pHdrRead MsgType.IRDA_DataService.pHdrRead +#define IRDA_MSG_pHdrWrite MsgType.IRDA_DataService.pHdrWrite +#define IRDA_MSG_Header MsgType.IRDA_DataService.Header +#define IRDA_MSG_pBase MsgType.IRDA_DataService.pBase +#define IRDA_MSG_pLimit MsgType.IRDA_DataService.pLimit +#define IRDA_MSG_pRead MsgType.IRDA_DataService.pRead +#define IRDA_MSG_pWrite MsgType.IRDA_DataService.pWrite +#define IRDA_MSG_DataStatus MsgType.IRDA_DataService.DataStatus +#define IRDA_MSG_pTdiSendComp MsgType.IRDA_DataService.pTdiSendComp +#define IRDA_MSG_pTdiSendCompCnxt MsgType.IRDA_DataService.pTdiSendCompCnxt +#define IRDA_MSG_IrCOMM_9Wire MsgType.IRDA_DataService.IrCOMM_9Wire +#ifdef TEMPERAMENTAL_SERIAL_DRIVER +#define IRDA_MSG_FCS MsgType.IRDA_DataService.FCS +#endif + +#define IRDA_MSG_pDevList MsgType.IRLAP_DiscoveryService.pDevList +#define IRDA_MSG_DscvStatus MsgType.IRLAP_DiscoveryService.DscvStatus +#define IRDA_MSG_SenseMedia MsgType.IRLAP_DiscoveryService.SenseMedia + +#define IRDA_MSG_RemoteDevAddr MsgType.IRDA_ConnectService.RemoteDevAddr +#define IRDA_MSG_pQOS MsgType.IRDA_ConnectService.pQOS +#define IRDA_MSG_LocalLSAPSel MsgType.IRDA_ConnectService.LocalLSAPSel +#define IRDA_MSG_RemoteLSAPSel MsgType.IRDA_ConnectService.RemoteLSAPSel +#define IRDA_MSG_pConnData MsgType.IRDA_ConnectService.pConnData +#define IRDA_MSG_ConnDataLen MsgType.IRDA_ConnectService.ConnDataLen +#define IRDA_MSG_ConnStatus MsgType.IRDA_ConnectService.ConnStatus +#define IRDA_MSG_pContext MsgType.IRDA_ConnectService.pContext +#define IRDA_MSG_UseTTP MsgType.IRDA_ConnectService.UseTTP +#define IRDA_MSG_MaxSDUSize MsgType.IRDA_ConnectService.MaxSDUSize +#define IRDA_MSG_MaxPDUSize MsgType.IRDA_ConnectService.MaxPDUSize +#define IRDA_MSG_TTPCredits MsgType.IRDA_ConnectService.TTPCredits + +#define IRDA_MSG_pDiscData MsgType.IRDA_DisconnectService.pDiscData +#define IRDA_MSG_DiscDataLen MsgType.IRDA_DisconnectService.DiscDataLen +#define IRDA_MSG_DiscReason MsgType.IRDA_DisconnectService.DiscReason +#define IRDA_MSG_DiscStatus MsgType.IRDA_DisconnectService.DiscStatus + +#define IRDA_MSG_AccessMode MsgType.IRLMP_AccessModeService.AccessMode +#define IRDA_MSG_ModeStatus MsgType.IRLMP_AccessModeService.ModeStatus +#define IRDA_MSG_IrLPTMode MsgType.IRLMP_AccessModeService.IrLPTMode + +#define IRDA_MSG_pIASQuery MsgType.IRLMP_IASService.pIASQuery +#define IRDA_MSG_AttribLen MsgType.IRLMP_IASService.AttribLen +#define IRDA_MSG_IASQueryPerms MsgType.IRLMP_IASService.IASQueryPerms +#define IRDA_MSG_IASStatus MsgType.IRLMP_IASService.IASStatus + +extern LIST_ENTRY IrdaLinkCbList; + +VOID IrdaTimerInitialize(PIRDA_TIMER pTimer, + VOID (*ExpFunc)(PVOID Context), + UINT Timeout, + PVOID Context); + +VOID IrdaTimerStart(PIRDA_TIMER pTimer); + +VOID IrdaTimerStop(PIRDA_TIMER pTimer); + diff --git a/private/ntos/tdi/irda/inc/irdalink.h b/private/ntos/tdi/irda/inc/irdalink.h new file mode 100644 index 000000000..e96f13665 --- /dev/null +++ b/private/ntos/tdi/irda/inc/irdalink.h @@ -0,0 +1,34 @@ +NTSTATUS IrdaNdisInitialize(); + +#define IRDA_NDIS_BUFFER_POOL_SIZE 4 +#define IRDA_NDIS_PACKET_POOL_SIZE 4 +#define IRDA_MSG_LIST_LEN 2 +#define IRDA_MSG_DATA_SIZE 64 + +typedef struct +{ + PIRDA_MSG pIMsg; + MEDIA_SPECIFIC_INFORMATION MediaInfo; +} IRDA_PROTOCOL_RESERVED, *PIRDA_PROTOCOL_RESERVED; + +typedef struct IrdaLinkControlBlock +{ + LIST_ENTRY Linkage; + NDIS_SPIN_LOCK SpinLock; + NDIS_HANDLE BindContext; + NDIS_HANDLE NdisBindingHandle; + NDIS_EVENT SyncEvent; + NDIS_STATUS SyncStatus; + int MediaBusy; + PVOID IrlapContext; + PVOID IrlmpContext; + NDIS_HANDLE BufferPool; + NDIS_HANDLE PacketPool; + LIST_ENTRY IMsgList; + int IMsgListLen; + UINT ExtraBofs; // These should be per connection for + UINT MinTat; // multipoint +} IRDA_LINK_CB, *PIRDA_LINK_CB; + +IRDA_MSG *AllocMacIMsg(PIRDA_LINK_CB); + diff --git a/private/ntos/tdi/irda/inc/irerr.h b/private/ntos/tdi/irda/inc/irerr.h new file mode 100644 index 000000000..7490c6fba --- /dev/null +++ b/private/ntos/tdi/irda/inc/irerr.h @@ -0,0 +1,110 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irerr.h +* +* Description: IR error defines +* +* Author: mmiller +* +* Date: 4/25/95 +* +*/ + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#define IR_ERROR_BASE 20000 +#define IRLAP_ERROR_BASE IR_ERROR_BASE + 100 +#define IRLMP_ERROR_BASE IR_ERROR_BASE + 200 + +#define IRMAC_TX_OVERFLOW (IR_ERROR_BASE+0) +#define IRMAC_WRITE_FAILED (IR_ERROR_BASE+1) +#define IRMAC_READ_FAILED (IR_ERROR_BASE+2) +#define IRMAC_BAD_FCS (IR_ERROR_BASE+3) +#define IRMAC_RX_OVERFLOW (IR_ERROR_BASE+4) +#define IRMAC_TIMEOUT (IR_ERROR_BASE+5) +#define IRMAC_BAD_PRIM (IR_ERROR_BASE+6) +#define IRMAC_BAD_OP (IR_ERROR_BASE+7) +#define IRMAC_OPEN_PORT_FAILED (IR_ERROR_BASE+8) +#define IRMAC_SET_BAUD_FAILED (IR_ERROR_BASE+9) +#define IRMAC_MALLOC_FAILED (IR_ERROR_BASE+10) +#define IRMAC_ALREADY_INIT (IR_ERROR_BASE+11) +#define IRMAC_BAD_TIMER (IR_ERROR_BASE+12) +#define IRMAC_NOT_INITIALIZED (IR_ERROR_BASE+13) +#define IRMAC_LINK_RESET (IR_ERROR_BASE+14) + +#define IRLAP_NOT_INITIALIZED (IRLAP_ERROR_BASE + 0) +#define IRLAP_BAD_PRIM (IRLAP_ERROR_BASE + 1) +#define IRLAP_BAD_STATE (IRLAP_ERROR_BASE + 2) +#define IRLAP_BAD_OPSTATUS (IRLAP_ERROR_BASE + 3) +#define IRLAP_BAD_OP (IRLAP_ERROR_BASE + 4) +#define IRLAP_MALLOC_FAILED (IRLAP_ERROR_BASE + 5) +#define IRLAP_BAUD_NEG_ERR (IRLAP_ERROR_BASE + 6) +#define IRLAP_DISC_NEG_ERR (IRLAP_ERROR_BASE + 7) +#define IRLAP_MAXTAT_NEG_ERR (IRLAP_ERROR_BASE + 8) +#define IRLAP_MINTAT_NEG_ERR (IRLAP_ERROR_BASE + 9) +#define IRLAP_DATASIZE_NEG_ERR (IRLAP_ERROR_BASE + 10) +#define IRLAP_WINSIZE_NEG_ERR (IRLAP_ERROR_BASE + 11) +#define IRLAP_BOFS_NEG_ERR (IRLAP_ERROR_BASE + 12) +#define IRLAP_LINECAP_ERR (IRLAP_ERROR_BASE + 13) +#define IRLAP_BAD_SLOTNO (IRLAP_ERROR_BASE + 14) +#define IRLAP_XID_CMD_NOT_P (IRLAP_ERROR_BASE + 15) +#define IRLAP_SNRM_NO_QOS (IRLAP_ERROR_BASE + 16) +#define IRLAP_UA_NO_QOS (IRLAP_ERROR_BASE + 17) +#define IRLAP_XID_CMD_RSP (IRLAP_ERROR_BASE + 18) +#define IRLAP_SNRM_NOT_CMD (IRLAP_ERROR_BASE + 19) +#define IRLAP_SNRM_NOT_P (IRLAP_ERROR_BASE + 20) +#define IRLAP_UA_NOT_RSP (IRLAP_ERROR_BASE + 21) +#define IRLAP_UA_NOT_F (IRLAP_ERROR_BASE + 22) +#define IRLAP_MSG_LIST_EMPTY (IRLAP_ERROR_BASE + 23) +#define IRLAP_MSG_LIST_FULL (IRLAP_ERROR_BASE + 24) +#define IRLAP_RXD_BAD_FRAME (IRLAP_ERROR_BASE + 25) +#define IRLAP_BAD_CRBIT_IFRAME (IRLAP_ERROR_BASE + 26) +#define IRLAP_BAD_DATA_REQUEST (IRLAP_ERROR_BASE + 27) +#define IRLAP_DISC_CMD_RSP (IRLAP_ERROR_BASE + 28) +#define IRLAP_DISC_CMD_NOT_P (IRLAP_ERROR_BASE + 29) +#define IRLAP_DM_RSP_NOT_F (IRLAP_ERROR_BASE + 30) +#define IRLAP_DM_RSP_CMD (IRLAP_ERROR_BASE + 31) +#define IRLAP_FRMR_RSP_CMD (IRLAP_ERROR_BASE + 32) +#define IRLAP_FRMR_RSP_NOT_F (IRLAP_ERROR_BASE + 33) +#define IRLAP_BAD_QOS (IRLAP_ERROR_BASE + 34) +#define IRLAP_NULL_MSG (IRLAP_ERROR_BASE + 35) +#define IRLAP_BAD_MAX_SLOT (IRLAP_ERROR_BASE + 36) +#define IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR (IRLAP_ERROR_BASE + 37) +#define IRLAP_REMOTE_CONNECTION_IN_PROGRESS_ERR (IRLAP_ERROR_BASE + 38) +#define IRLAP_REMOTE_BUSY (IRLAP_ERROR_BASE + 39) + +#define IRLMP_NOT_INITIALIZED (IRLMP_ERROR_BASE + 0) +#define IRLMP_LSAP_BAD_STATE (IRLMP_ERROR_BASE + 1) +#define IRLMP_USER_DATA_LEN_EXCEEDED (IRLMP_ERROR_BASE + 2) +#define IRLMP_LINK_IN_USE (IRLMP_ERROR_BASE + 3) +#define IRLMP_TIMER_START_FAILED (IRLMP_ERROR_BASE + 4) +#define IRLMP_ALLOC_FAILED (IRLMP_ERROR_BASE + 5) +#define IRLMP_LINK_BAD_STATE (IRLMP_ERROR_BASE + 6) +#define IRLMP_LSAP_SEL_IN_USE (IRLMP_ERROR_BASE + 7) +#define IRLMP_CREDIT_CALC_ERROR (IRLMP_ERROR_BASE + 8) +#define IRLMP_NO_TX_CREDIT (IRLMP_ERROR_BASE + 9) +#define IRLMP_TX_DATA_LEN_EXCEEDED (IRLMP_ERROR_BASE + 10) +#define IRLMP_DATA_IND_BAD_FRAME (IRLMP_ERROR_BASE + 11) +#define IRLMP_SCHEDULE_EVENT_FAILED (IRLMP_ERROR_BASE + 12) +#define IRLMP_LOCAL_BUSY (IRLMP_ERROR_BASE + 13) +#define IRLMP_BAD_PRIM (IRLMP_ERROR_BASE + 14) +#define IRLMP_BAD_ACCESSMODE (IRLMP_ERROR_BASE + 15) +#define IRLMP_LINK_BUSY (IRLMP_ERROR_BASE + 16) +#define IRLMP_IN_MULTIPLEXED_MODE (IRLMP_ERROR_BASE + 17) +#define IRLMP_IN_EXCLUSIVE_MODE (IRLMP_ERROR_BASE + 18) +#define IRLMP_NOT_LSAP_IN_EXCLUSIVE_MODE (IRLMP_ERROR_BASE + 19) +#define IRLMP_INVALID_LSAP_CB (IRLMP_ERROR_BASE + 20) +#define IRLMP_REMOTE_BUSY (IRLMP_ERROR_BASE + 21) +#define IRLMP_TIMER_STOP_FAILED (IRLMP_ERROR_BASE + 22) +#define IRLMP_BAD_IAS_OBJECT_ID (IRLMP_ERROR_BASE + 23) +#define IRLMP_NO_SUCH_IAS_CLASS (IRLMP_ERROR_BASE + 24) +#define IRLMP_NO_SUCH_IAS_ATTRIBUTE (IRLMP_ERROR_BASE + 25) +#define IRLMP_UNSUPPORTED_IAS_OPERATION (IRLMP_ERROR_BASE + 26) +#define IRLMP_BAD_IAS_QUERY_FROM_REMOTE (IRLMP_ERROR_BASE + 27) +#define IRLMP_IAS_QUERY_IN_PROGRESS (IRLMP_ERROR_BASE + 28) +#define IRLMP_UNSOLICITED_IAS_RESPONSE (IRLMP_ERROR_BASE + 29) +#define IRLMP_SHUTDOWN_IN_PROGESS (IRLMP_ERROR_BASE + 30) diff --git a/private/ntos/tdi/irda/inc/irlap.h b/private/ntos/tdi/irda/inc/irlap.h new file mode 100644 index 000000000..dbcc68361 --- /dev/null +++ b/private/ntos/tdi/irda/inc/irlap.h @@ -0,0 +1,66 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlap.h +* +* Description: IRLAP Protocol and control block definitions +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ + +// Sequence number modulus +#define IRLAP_MOD 8 +#define PV_TABLE_MAX_BIT 8 + +extern UINT vBaudTable[]; +extern UINT vMaxTATTable[]; +extern UINT vMinTATTable[]; +extern UINT vDataSizeTable[]; +extern UINT vWinSizeTable[]; +extern UINT vBOFSTable[]; +extern UINT vDiscTable[]; +extern UINT vThreshTable[]; +extern UINT vBOFSDivTable[]; + +VOID IrlapOpenLink( + OUT PNTSTATUS Status, + IN PIRDA_LINK_CB pIrdaLinkCb, + IN IRDA_QOS_PARMS *pQos, + IN BYTE *pDscvInfo, + IN int DscvInfoLen, + IN UINT MaxSlot); + +UINT IrlapDown(IN PVOID Context, + IN PIRDA_MSG); + +UINT IrlapUp(IN PVOID Context, + IN PIRDA_MSG); + +UINT IRLAP_Shutdown(); + +UINT IrlapGetQosParmVal(UINT[], UINT, UINT *); + +void IRLAP_PrintState(); + + + +typedef struct +{ + LIST_ENTRY ListHead; + int Len; +} IRDA_MSG_LIST; + +// I've exported these for the tester +UINT DequeMsgList(IRDA_MSG_LIST *, IRDA_MSG **); +UINT EnqueMsgList(IRDA_MSG_LIST *, IRDA_MSG *, int); +void InitMsgList(IRDA_MSG_LIST *); + + + + + + diff --git a/private/ntos/tdi/irda/inc/irlaplog.h b/private/ntos/tdi/irda/inc/irlaplog.h new file mode 100644 index 000000000..71f67866f --- /dev/null +++ b/private/ntos/tdi/irda/inc/irlaplog.h @@ -0,0 +1,44 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlaplog.h +* +* Description: IRLAP state machine logging and errors +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ + +#ifdef DEBUG + +extern TCHAR *IRDA_PrimStr[]; +extern TCHAR *IRLAP_StateStr[]; +extern TCHAR *MAC_OpStr[]; +extern TCHAR *IRDA_TimerStr[]; +extern TCHAR *IRDA_StatStr[]; + +#define SPRINT_BUF_LEN 1000 + +#define EXPAND_ADDR(Addr) (Addr[0], Addr[1], Addr[2], Addr[3]) + +void IRLAP_EventLogStart(PIRLAP_CB, TCHAR *pFormat, ...); +void __cdecl IRLAP_LogAction(PIRLAP_CB, TCHAR *pFormat, ...); +void IRLAP_EventLogComplete(PIRLAP_CB); +TCHAR *FrameToStr(IRDA_MSG *); + +#define IRLAP_LOG_START(X) IRLAP_EventLogStart X +#define IRLAP_LOG_ACTION(X) IRLAP_LogAction X +#define IRLAP_LOG_COMPLETE(X) IRLAP_EventLogComplete(X) + +#else + +#define IRLAP_LOG_START(X) (0) +#define IRLAP_LOG_ACTION(X) (0) +#define IRLAP_LOG_COMPLETE(X) (0) + +#endif + + diff --git a/private/ntos/tdi/irda/inc/irlmp.h b/private/ntos/tdi/irda/inc/irlmp.h new file mode 100644 index 000000000..ac46e4107 --- /dev/null +++ b/private/ntos/tdi/irda/inc/irlmp.h @@ -0,0 +1,60 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlmp.h +* +* Description: IRLMP Protocol and control block definitions +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ + +#define IRLMP_MAX_USER_DATA_LEN 53 + +// IrLMP Entry Points + +UINT IRLMP_Initialize(int Port, BOOL SetIR, BYTE DscvInfo[], int DscvInfoLen, + IRDA_QOS_PARMS *pQOS, int MaxSlot, CHAR *pDeviceName, + int DeviceNameLen); + +UINT IrlmpDown(PVOID IrlmpContext, PIRDA_MSG pIMsg); +UINT IrlmpUp(PVOID IrlmpContext, PIRDA_MSG pIMsg); + +UINT IRLMP_RegisterLSAPProtocol(int LSAP, BOOL UseTTP); +UINT IRLMP_Shutdown(); + +#ifdef DEBUG +void IRLMP_PrintState(); +#endif + +// IAS + +#define IAS_ASCII_CHAR_SET 0 + +// IAS Attribute value types +#define IAS_ATTRIB_VAL_MISSING 0 +#define IAS_ATTRIB_VAL_INTEGER 1 +#define IAS_ATTRIB_VAL_BINARY 2 +#define IAS_ATTRIB_VAL_STRING 3 + +// IAS Operation codes +#define IAS_OPCODE_GET_VALUE_BY_CLASS 4 // The only one I do + +extern const CHAR IAS_ClassName_Device[]; +extern const CHAR IAS_AttribName_DeviceName[]; +extern const CHAR IAS_AttribName_IrLMPSupport[]; +extern const CHAR IAS_AttribName_TTPLsapSel[]; +extern const CHAR IAS_AttribName_IrLMPLsapSel[]; + +extern const BYTE IAS_ClassNameLen_Device; +extern const BYTE IAS_AttribNameLen_DeviceName; +extern const BYTE IAS_AttribNameLen_IRLMPSupport; +extern const BYTE IAS_AttribNameLen_TTPLsapSel; +extern const BYTE IAS_AttribNameLen_IrLMPLsapSel; + +UINT IAS_AddAttribute(IAS_SET *pIASSet); + +UINT IAS_DeleteObject(CHAR *pClassName); diff --git a/private/ntos/tdi/irda/inc/irmac.h b/private/ntos/tdi/irda/inc/irmac.h new file mode 100644 index 000000000..534e83525 --- /dev/null +++ b/private/ntos/tdi/irda/inc/irmac.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irmac.h +* +* Description: IRLAP MAC definitions and entry point prototypes +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ + +// Entry Points + +UINT IrmacInitialize(); + +UINT IrmacDown( + IN PVOID IrmacContext, + PIRDA_MSG pMsg); + +UINT IRMAC_RxFrame(IRDA_MSG *pMsg); +UINT IRMAC_TimerExpired(IRDA_TIMER Timer); +void IRMAC_PrintState(); + + + + + + + diff --git a/private/ntos/tdi/irda/inc/oscfg.h b/private/ntos/tdi/irda/inc/oscfg.h new file mode 100644 index 000000000..49eab5e00 --- /dev/null +++ b/private/ntos/tdi/irda/inc/oscfg.h @@ -0,0 +1,62 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: oscfg.h +* +* Description: OS configuration section +* +* Author: mmiller +* +* Date: 4/25/95 +* +*/ +#ifdef PEG + +// Pegasus specific includes/defines +#include "peg.h" + +#define DbgPrint NKDbgPrintfW + +#else // PEG + +#include +//#include +//#include +//#include + +//#include +//#include +//#include + +#define RETAILMSG(exp,p) (0) +#define DEBUGMSG(exp,p) (0) + +// NT specific includes/defines +//#include "windows.h" +//#include "stdio.h" + +//#define DbgPrint printf +//typedef char TCHAR; + +//#define RETAILMSG(exp,p) ((exp)?DbgPrint p,1:0) + +//#ifdef DEBUG + +//#define DEBUGMSG(exp,p) ((exp)?DbgPrint p,1:0) +//#undef NDEBUG + +//#define DEBUGZONE(x) 1<| | +* | IRLAP | | TIMER | +* | |<--------------------------| | +* |---------| IRLAP_TimerExp() |-------| +* /|\ | +* | | +* IrlapUp() | |IrmacDown() +* IRLAP_GetRxMsg() | | +* | \|/ +* |---------| +* | IRMAC | +* |---------| +* +* +* Discovery Request +* +* |-------| IRLAP_DISCOVERY_REQ |-------| +* | |---------------------------------------------------->| | +* | IRLMP | | IRLAP | +* | |<----------------------------------------------------| | +* |-------| IRLAP_DISCOVERY_CONF |-------| +* DscvStatus = IRLAP_DISCOVERY_COMPLETE +* IRLAP_DISCOVERY_COLLISION +* MAC_MEDIA_BUSY +* +* Connect Request +* +* |-------| IRLAP_CONNECT_REQ |-------| +* | |---------------------------------------------------->| | +* | IRLMP | | IRLAP | +* | |<----------------------------------------------------| | +* |-------| IRLAP_CONNECT_CONF |-------| +* ConnStatus = IRLAP_CONNECTION_COMPLETE +* IRLAP_DISCONNECT_IND +* DiscStatus = IRLAP_NO_RESPONSE +* MAC_MEDIA_BUSY +* +* Disconnect Request +* +* |-------| IRLAP_DISCONNECT_REQ |-------| +* | |---------------------------------------------------->| | +* | IRLMP | | IRLAP | +* | |<----------------------------------------------------| | +* |-------| IRLAP_DISCONNECT_IND |-------| +* DiscStatus = IRLAP_DISCONNECT_COMPLETE +* IRLAP_NO_RESPONSE +* +* UData/Data Request +* +* |-------| IRLAP_DATA/UDATA_REQ |-------| +* | |---------------------------------------------------->| | +* | IRLMP | | IRLAP | +* | |<----------------------------------------------------| | +* |-------| IRLAP_DATA_CONF |-------| +* DataStatus = IRLAP_DATA_REQUEST_COMPLETED +* IRLAP_DATA_REQUEST_FAILED_LINK_RESET +* +* See irda.h for complete message definitions +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef TEMPERAMENTAL_SERIAL_DRIVER +int TossedDups; +#endif + +STATIC UINT _rc; // return code +STATIC IRDA_MSG IMsg; // for locally generated messages to LMP/MAC +STATIC UINT IRLAP_SlotTable[] = {1, 6, 8, 16}; +STATIC IRLAP_FRMR_FORMAT FrmRejFormat; + +BYTE IRLAP_BroadcastDevAddr[IRDA_DEV_ADDR_LEN] = + {0xFF,0xFF,0xFF,0xFF}; + +// Parameter Value (PV) tables used for negotation +// bit0 1 2 3 4 5 6 7 8 +// ------------------------------------------------------- +UINT vBaudTable[] = {2400, 9600, 19200, 38400, 57600, 115200,0, 0, 4000000}; +UINT vMaxTATTable[] = {500, 250, 100, 50, 25, 10, 5, 0, 0 }; +UINT vMinTATTable[] = {10000,5000, 1000, 500, 100, 50, 10,0, 0 }; +UINT vDataSizeTable[] = {64, 128, 256, 512, 1024, 2048, 0, 0, 0 }; +UINT vWinSizeTable[] = {1, 2, 3, 4, 5, 6, 7, 0, 0 }; +UINT vBOFSTable[] = {48, 24, 12, 5, 3, 2, 1, 0, 0 }; +UINT vDiscTable[] = {3, 8, 12, 16, 20, 25, 30,40,0 }; +UINT vThreshTable[] = {0, 3, 3, 3, 3, 3, 3, 3, 0 }; +UINT vBOFSDivTable[] = {48, 12, 6, 3, 2, 1, 1, 1, 0 }; + +// Tables for determining number of BOFS for baud and min turn time +// min turn time - 10ms 5ms 1ms 0.5ms 0.1ms 0.05ms 0.01ms +// ------------------------------------------------------------- +UINT BOFS_9600[] = {10, 5, 1, 0, 0, 0, 0}; +UINT BOFS_19200[] = {20, 10, 2, 1, 0, 0, 0}; +UINT BOFS_38400[] = {40, 20, 4, 2, 0, 0, 0}; +UINT BOFS_57600[] = {58, 29, 6, 3, 1, 0, 0}; +UINT BOFS_115200[] = {115, 58, 12, 6, 1, 1, 0}; + +// Tables for determining maximum line capacity for baud, max turn time +// max turn time - 500ms 250ms 100ms 50ms 25ms 10ms 5ms +// ------------------------------------------------------------- +UINT MAXCAP_9600[] = {400, 200, 80, 0, 0, 0, 0}; +UINT MAXCAP_19200[] = {800, 400, 160, 0, 0, 0, 0}; +UINT MAXCAP_38400[] = {1600, 800, 320, 0, 0, 0, 0}; +UINT MAXCAP_57600[] = {2360, 1180, 472, 0, 0, 0, 0}; +UINT MAXCAP_115200[] = {4800, 2400, 960, 480, 240, 96, 48}; + +// prototypes +STATIC UINT InitializeState(PIRLAP_CB, IRLAP_STN_TYPE); +STATIC UINT ReturnTxMsgs(PIRLAP_CB); +STATIC UINT ProcessConnectReq(PIRLAP_CB, PIRDA_MSG); +STATIC UINT ProcessConnectResp(PIRLAP_CB, PIRDA_MSG); +STATIC UINT ProcessDiscoveryReq(PIRLAP_CB, PIRDA_MSG); +STATIC UINT ProcessDisconnectReq(PIRLAP_CB); +STATIC UINT ProcessDataAndUDataReq(PIRLAP_CB, PIRDA_MSG); +STATIC UINT XmitTxMsgList(PIRLAP_CB, BOOL, BOOL *); +STATIC UINT GotoPCloseState(PIRLAP_CB); +STATIC UINT GotoNDMThenDscvOrConn(PIRLAP_CB); +STATIC UINT ProcessMACControlConf(PIRLAP_CB, PIRDA_MSG); +STATIC UINT ProcessMACDataInd(PIRLAP_CB, PIRDA_MSG , BOOL *); +STATIC UINT ProcessDscvXIDCmd(PIRLAP_CB, IRLAP_XID_DSCV_FORMAT *, BYTE *); +STATIC UINT ProcessDscvXIDRsp(PIRLAP_CB, IRLAP_XID_DSCV_FORMAT *, BYTE *); +STATIC void ExtractQosParms(IRDA_QOS_PARMS *, BYTE *, BYTE *); +STATIC UINT InitDscvCmdProcessing(PIRLAP_CB, IRLAP_XID_DSCV_FORMAT *); +STATIC void ExtractDeviceInfo(IRDA_DEVICE *, IRLAP_XID_DSCV_FORMAT *, BYTE *); +STATIC BOOL DevInDevList(BYTE[], LIST_ENTRY *); +STATIC UINT AddDevToList(PIRLAP_CB, IRLAP_XID_DSCV_FORMAT *, BYTE *); +STATIC void ClearDevList(LIST_ENTRY *); +STATIC UINT ProcessSNRM(PIRLAP_CB, IRLAP_SNRM_FORMAT *, BYTE *); +STATIC UINT ProcessUA(PIRLAP_CB, IRLAP_UA_FORMAT *, BYTE *); +STATIC UINT ProcessDISC(PIRLAP_CB); +STATIC UINT ProcessRD(PIRLAP_CB); +STATIC UINT ProcessRNRM(PIRLAP_CB); +STATIC UINT ProcessDM(PIRLAP_CB); +STATIC UINT ProcessFRMR(PIRLAP_CB); +STATIC UINT ProcessTEST(PIRLAP_CB, PIRDA_MSG, IRLAP_UA_FORMAT *, int, int); +STATIC UINT ProcessUI(PIRLAP_CB, PIRDA_MSG, int, int); +STATIC UINT ProcessREJ_SREJ(PIRLAP_CB, int, PIRDA_MSG, int, int, UINT); +STATIC UINT ProcessRR_RNR(PIRLAP_CB, int, PIRDA_MSG, int, int, UINT); +STATIC UINT ProcessIFrame(PIRLAP_CB, PIRDA_MSG, int, int, UINT, UINT, BOOL *); +STATIC BOOL InvalidNsOrNr(PIRLAP_CB, UINT, UINT); +STATIC BOOL InvalidNr(PIRLAP_CB, UINT); +STATIC BOOL InWindow(UINT, UINT, UINT); +STATIC UINT ProcessInvalidNsOrNr(PIRLAP_CB, int); +STATIC UINT ProcessInvalidNr(PIRLAP_CB, int); +STATIC UINT InsertRxWinAndForward(PIRLAP_CB, PIRDA_MSG, UINT, BOOL *); +STATIC UINT ResendRejects(PIRLAP_CB, UINT); +STATIC UINT FreeAckedTxMsgs(PIRLAP_CB, UINT); +STATIC UINT MissingRxFrames(PIRLAP_CB); +STATIC UINT IFrameOtherStates(PIRLAP_CB, int, int); +STATIC UINT NegotiateQosParms(PIRLAP_CB, IRDA_QOS_PARMS *); +STATIC UINT ApplyQosParms(PIRLAP_CB); +STATIC UINT StationConflict(PIRLAP_CB); +STATIC UINT ApplyDefaultParms(PIRLAP_CB); +STATIC UINT ResendDISC(PIRLAP_CB); +STATIC BOOL IgnoreState(PIRLAP_CB); +STATIC BOOL MyDevAddr(PIRLAP_CB, BYTE []); +STATIC VOID SlotTimerExp(PVOID); +STATIC VOID FinalTimerExp(PVOID); +STATIC VOID PollTimerExp(PVOID); +STATIC VOID BackoffTimerExp(PVOID); +STATIC VOID WDogTimerExp(PVOID); +STATIC VOID QueryTimerExp(PVOID); + +#ifdef DEBUG +void _inline IRLAP_TimerStart(PIRLAP_CB pIrlapCb, PIRDA_TIMER pTmr) +{ + IRLAP_LOG_ACTION((pIrlapCb, "Start %s timer for %dms", pTmr->pName, + pTmr->Timeout)); + IrdaTimerStart(pTmr); +} + +void _inline IRLAP_TimerStop(PIRLAP_CB pIrlapCb, PIRDA_TIMER pTmr) +{ + IRLAP_LOG_ACTION((pIrlapCb, "Stop %s timer", pTmr->pName)); + IrdaTimerStop(pTmr); +} +#else +#define IRLAP_TimerStart(c,t) IrdaTimerStart(t) +#define IRLAP_TimerStop(c,t) IrdaTimerStop(t) +#endif + +VOID +IrlapOpenLink(OUT PNTSTATUS Status, + IN PIRDA_LINK_CB pIrdaLinkCb, + IN IRDA_QOS_PARMS *pQos, + IN BYTE *pDscvInfo, + IN int DscvInfoLen, + IN UINT MaxSlot) +{ + UINT rc = SUCCESS; + int i; + IRDA_MSG *pMsg; + PIRLAP_CB pIrlapCb; + + DEBUGMSG(DBG_IRLAP, ("IrlapOpenLink\n")); + + if ((pIrlapCb = CTEAllocMem(sizeof(IRLAP_CB))) == NULL) + { + DEBUGMSG(DBG_ERROR, ("Alloc failed\n")); + *Status = STATUS_INSUFFICIENT_RESOURCES; + return; + } + + pIrdaLinkCb->IrlapContext = pIrlapCb; + + DscvInfoLen = DscvInfoLen > IRLAP_DSCV_INFO_LEN ? + IRLAP_DSCV_INFO_LEN : DscvInfoLen; + + memcpy(pIrlapCb->LocalDevice.DscvInfo, pDscvInfo, DscvInfoLen); + + pIrlapCb->LocalDevice.DscvInfoLen = DscvInfoLen; + + memcpy(&pIrlapCb->LocalQos, pQos, sizeof(IRDA_QOS_PARMS)); + + pIrlapCb->Sig = IRLAP_CB_SIG; + pIrlapCb->pIrdaLinkCb = pIrdaLinkCb; + + InitMsgList(&pIrlapCb->TxMsgList); + + InitializeListHead(&pIrlapCb->DevList); + + for (i = 0; i < IRLAP_MOD; i++) + { + pIrlapCb->TxWin.pMsg[i] = NULL; + pIrlapCb->RxWin.pMsg[i] = NULL; + } + + // Get the local MAX TAT (for final timeout) + if ((pIrlapCb->LocalMaxTAT = IrlapGetQosParmVal(vMaxTATTable, + pIrlapCb->LocalQos.bfMaxTurnTime, NULL)) == -1) + { + *Status = STATUS_UNSUCCESSFUL; + return /*IRLAP_BAD_QOS*/; + } + + // initialize as PRIMARY so UI frames in contention + // state sends CRBit = cmd + if ((rc = InitializeState(pIrlapCb, PRIMARY)) != SUCCESS) + { + CTEFreeMem(pIrlapCb); + *Status = STATUS_UNSUCCESSFUL; + return; + } + + pIrlapCb->State = NDM; + + // Generate random local address + StoreULAddr(pIrlapCb->LocalDevice.DevAddr, (ULONG) GetMyDevAddr(FALSE)); + + pIrlapCb->LocalDevice.IRLAP_Version = 1; + + pIrlapCb->Baud = IRLAP_DEFAULT_BAUD; + pIrlapCb->RemoteMaxTAT = IRLAP_DEFAULT_MAX_TAT; + pIrlapCb->RemoteDataSize = IRLAP_DEFAULT_DATA_SIZE; + pIrlapCb->RemoteWinSize = IRLAP_DEFAULT_WIN_SIZE; + pIrlapCb->RemoteNumBOFS = IRLAP_DEFAULT_BOFS; + + pIrlapCb->ConnAddr = IRLAP_BROADCAST_CONN_ADDR; + + pIrlapCb->N1 = 0; // calculated at negotiation + pIrlapCb->N2 = 0; + pIrlapCb->N3 = 5; // recalculated after negotiation ?? + +#ifdef DEBUG + pIrlapCb->PollTimer.pName = "Poll"; + pIrlapCb->FinalTimer.pName = "Final" ; + pIrlapCb->SlotTimer.pName = "Slot"; + pIrlapCb->QueryTimer.pName = "Query"; + pIrlapCb->WDogTimer.pName = "WatchDog"; + pIrlapCb->BackoffTimer.pName = "Backoff"; +#endif + + IrdaTimerInitialize(&pIrlapCb->PollTimer, + PollTimerExp, + pIrlapCb->RemoteMaxTAT, + pIrlapCb); + + IrdaTimerInitialize(&pIrlapCb->FinalTimer, + FinalTimerExp, + pIrlapCb->LocalMaxTAT, + pIrlapCb); + + IrdaTimerInitialize(&pIrlapCb->SlotTimer, + SlotTimerExp, + IRLAP_SLOT_TIMEOUT, + pIrlapCb); + + IrdaTimerInitialize(&pIrlapCb->QueryTimer, + QueryTimerExp, + (IRLAP_MAX_SLOTS + 4) * IRLAP_SLOT_TIMEOUT*2, + pIrlapCb); + + IrdaTimerInitialize(&pIrlapCb->WDogTimer, + WDogTimerExp, + 3000, + pIrlapCb); + + IrdaTimerInitialize(&pIrlapCb->BackoffTimer, + BackoffTimerExp, + 0, + pIrlapCb); + + // Initialize Link + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_INITIALIZE_LINK; + IMsg.IRDA_MSG_Baud = IRLAP_DEFAULT_BAUD; + IMsg.IRDA_MSG_NumBOFs = IRLAP_DEFAULT_BOFS; + IMsg.IRDA_MSG_DataSize = IRLAP_DEFAULT_DATA_SIZE; + IMsg.IRDA_MSG_MinTat = 0; + + rc = IrmacDown(pIrlapCb->pIrdaLinkCb, &IMsg); + + *Status = rc; + return; +} + + +VOID +IrlapCloseLink(PIRLAP_CB pIrlapCb) +{ + return; +} + +/***************************************************************************** +* +* @func UINT | InitializeState | resets link control block +* +* @parm IRLAP_STN_TYPE | StationType| sets station type and the CRBit +* in the control block +*/ +UINT +InitializeState(PIRLAP_CB pIrlapCb, + IRLAP_STN_TYPE StationType) +{ + int i; + + pIrlapCb->StationType = StationType; + + if (StationType == PRIMARY) + pIrlapCb->CRBit = IRLAP_CMD; + else + pIrlapCb->CRBit = IRLAP_RSP; + + pIrlapCb->RemoteBusy = FALSE; + pIrlapCb->LocalBusy = FALSE; + pIrlapCb->ClrLocalBusy = FALSE; + pIrlapCb->NoResponse = FALSE; + pIrlapCb->LocalDiscReq = FALSE; + pIrlapCb->ConnAfterClose = FALSE; + pIrlapCb->DscvAfterClose = FALSE; + pIrlapCb->GenNewAddr = FALSE; + pIrlapCb->StatusSent = FALSE; + pIrlapCb->Vs = 0; + pIrlapCb->Vr = 0; + pIrlapCb->WDogExpCnt = 0; + + ClearDevList(&pIrlapCb->DevList); + + memset(&pIrlapCb->RemoteQos, 0, sizeof(IRDA_QOS_PARMS)); + memset(&pIrlapCb->NegotiatedQos, 0, sizeof(IRDA_QOS_PARMS)); + + // Return msgs on tx list and in tx window + RetOnErr(ReturnTxMsgs(pIrlapCb)); + + // Cleanup RxWin + pIrlapCb->RxWin.Start = 0; + pIrlapCb->RxWin.End = 0; + for (i = 0; i < IRLAP_MOD; i++) + { + // Receive window + if (pIrlapCb->RxWin.pMsg[i] != NULL) + { + /* RETURN THESE BACK TO NDIS + RetOnErr(EnqueMsgList(&pIrlapCb->RxMsgFreeList, + pIrlapCb->RxWin.pMsg[i], + pIrlapCb->MaxRxMsgFreeListLen)); + */ + pIrlapCb->RxWin.pMsg[i] = NULL; + } + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc return desc +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +/* +UINT +IRLAP_Shutdown() +{ + UINT rc = SUCCESS; + + IRLAP_LOG_START(pIrlapCb, (TEXT("IRLAP Shutdown"))); + + if ((rc = ReturnTxMsgs(pIrlapCb)) == SUCCESS) + { + // Shutdown Link + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_SHUTDOWN_LINK; + rc = IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg); + } + if (pIrlapCb->pRxMsgOut != NULL) + { + IRDA_FREE_MEM(pIrlapCb->pRxMsgOut); + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->SlotTimer); + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->QueryTimer); + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->PollTimer); + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->BackoffTimer); + IRLAP_TimerStop(pIrlapCb, IRMAC_MediaSenseTimer); + + IRLAP_LOG_COMPLETE(pIrlapCb); + + return rc; +} +*/ +/***************************************************************************** +* +* @func UINT | IrlapDown | Entry point into IRLAP for LMP +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag IRLAP_BAD_PRIMITIVE | Received message that didn't contain one +* of the primitives defined below +* IRLAP_NOT_INITIALIZED | IRLAP has not been intialize with +* IrlapInitialize() +* +* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message +* +* @comm Processes the following service requests: +* IRLAP_DISCOVERY_REQ, +* IRLAP_CONNECT_REQ, +* IRLAP_CONNECT_RESP, +* IRLAP_DISCONNECT_REQ, +* IRLAP_DATA_REQ, +* IRLAP_UDATA_REQ, +*/ +UINT +IrlapDown(PVOID Context, + PIRDA_MSG pMsg) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + UINT rc = SUCCESS; + + IRLAP_LOG_START((pIrlapCb, IRDA_PrimStr[pMsg->Prim])); + + switch (pMsg->Prim) + { + case IRLAP_DISCOVERY_REQ: + rc = ProcessDiscoveryReq(pIrlapCb, pMsg); + break; + + case IRLAP_CONNECT_REQ: + rc = ProcessConnectReq(pIrlapCb, pMsg); + break; + + case IRLAP_CONNECT_RESP: + rc = ProcessConnectResp(pIrlapCb, pMsg); + break; + + case IRLAP_DISCONNECT_REQ: + rc = ProcessDisconnectReq(pIrlapCb); + break; + + case IRLAP_DATA_REQ: + case IRLAP_UDATA_REQ: + rc = ProcessDataAndUDataReq(pIrlapCb, pMsg); + break; + + case IRLAP_FLOWON_REQ: + if (pIrlapCb->LocalBusy) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Local busy condition cleared"))); + pIrlapCb->LocalBusy = FALSE; + pIrlapCb->ClrLocalBusy = TRUE; + } + break; + + default: + rc = IRLAP_BAD_PRIM; + + } + + IRLAP_LOG_COMPLETE(pIrlapCb); + + return (rc); +} +/***************************************************************************** +* +* @func UINT | IrlapUp | Entry point into IRLAP for MAC +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag IRLAP_BAD_PRIMITIVE | Received message that didn't contain one +* of the primitives defined below +* IRLAP_NOT_INITIALIZED | IRLAP has not been intialize with +* IrlapInitialize() +* +* @parm IRDA_MSG * | pMsg | Pointer to an IRDA Message +* +* @comm Processes the following service requests: +* MAC_DATA_IND +* MAC_CONTROL_CONF +*/ +UINT +IrlapUp(PVOID Context, PIRDA_MSG pMsg) +{ + UINT rc = SUCCESS; + BOOL FreeMsg = TRUE; + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + // Whats this again ??? !!! pIrlapCb->pRxMsgOut = NULL; + + ASSERT(pIrlapCb->Sig == IRLAP_CB_SIG); + + switch (pMsg->Prim) + { + case MAC_DATA_IND: +// IRLAP_LOG_START((pIrlapCb, TEXT("MAC_DATA_IND: %s"), FrameToStr(pMsg))); + IRLAP_LOG_START((pIrlapCb, TEXT("MAC_DATA_IND"))); + + rc = ProcessMACDataInd(pIrlapCb, pMsg, &FreeMsg); + + /* What dis all about? + if (FreeMsg && SUCCESS == rc) + { + rc = EnqueMsgList(&pIrlapCb->RxMsgFreeList, pMsg, + pIrlapCb->MaxRxMsgFreeListLen); + } + */ + break; + + case MAC_CONTROL_CONF: + IRLAP_LOG_START((pIrlapCb, IRDA_PrimStr[pMsg->Prim])); + rc = ProcessMACControlConf(pIrlapCb, pMsg); + break; + + default: + IRLAP_LOG_START((pIrlapCb, IRDA_PrimStr[pMsg->Prim])); + rc = IRLAP_BAD_PRIM; + + } + + IRLAP_LOG_COMPLETE(pIrlapCb); + + return (rc); +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc return desc +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ + +/* THIS FUCKER GOES +UINT +IRLAP_GetRxMsg(IRDA_MSG **ppMsg) +{ + UINT rc = SUCCESS; + + ASSERT(pIrlapCb->pRxMsgOut == NULL); + + if ((rc = DequeMsgList(&pIrlapCb->RxMsgFreeList, ppMsg)) == SUCCESS) + { + (*ppMsg)->IRDA_MSG_pBase = ((BYTE *) (*ppMsg)) + sizeof(IRDA_MSG); + (*ppMsg)->IRDA_MSG_pLimit = ((BYTE *) (*ppMsg)) + + sizeof(IRDA_MSG)+ 5 + pIrlapCb->LocalDataSize; + + pIrlapCb->pRxMsgOut = *ppMsg; + } + + return rc; +} +*/ + +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ReturnTxMsgs(PIRLAP_CB pIrlapCb) +{ + int i; + IRDA_MSG *pMsg; + + // Return messages on TxMsgList to LMP + while (DequeMsgList(&pIrlapCb->TxMsgList, &pMsg) == SUCCESS) + { + pMsg->Prim += 2; // make it a confirm + pMsg->IRDA_MSG_DataStatus = IRLAP_DATA_REQUEST_FAILED_LINK_RESET; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, pMsg)); + } + + pIrlapCb->TxWin.Start = 0; + pIrlapCb->TxWin.End = 0; + // Transmit window + for (i = 0; i < IRLAP_MOD; i++) + { + if (pIrlapCb->TxWin.pMsg[i] != NULL) + { + pIrlapCb->TxWin.pMsg[i]->Prim = IRLAP_DATA_CONF; + pIrlapCb->TxWin.pMsg[i]->IRDA_MSG_DataStatus = + IRLAP_DATA_REQUEST_FAILED_LINK_RESET; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, pIrlapCb->TxWin.pMsg[i])); + + pIrlapCb->TxWin.pMsg[i] = NULL; + } + } + + return SUCCESS; +} + +/***************************************************************************** +* +* @func BOOL | MyDevAddr | Determines if DevAddr matches the local +* device address or is the broadcast +* +* @rdesc TRUE if address is mine or broadcast else FALS +* +* @parm BYTE [] | DevAddr | Device Address +* +*/ +BOOL +MyDevAddr(PIRLAP_CB pIrlapCb, + BYTE DevAddr[]) +{ + if (memcmp(DevAddr, IRLAP_BroadcastDevAddr, IRDA_DEV_ADDR_LEN) != 0 && + memcmp(DevAddr, pIrlapCb->LocalDevice.DevAddr, IRDA_DEV_ADDR_LEN) != 0) + { + return FALSE; + } + return TRUE; +} + +/***************************************************************************** +* +* @func UINT | ProcessConnectReq | Process connect request from LMP +* +* @rdesc 0, otherwise one of the following errors: +* @flag IRLAP_BAD_STATE | Requested connection in an invalid state +* +* @parm IRDA_MSG * | pMsg | pointer to an IRDA_MSG +* +* @comm +* comments +*/ +UINT +ProcessConnectReq(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg) +{ + switch (pIrlapCb->State) + { + case NDM: + // Save Remote Address for later use + memcpy(pIrlapCb->RemoteDevice.DevAddr, pMsg->IRDA_MSG_RemoteDevAddr, + IRDA_DEV_ADDR_LEN); + + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_MEDIA_SENSE_TIME; + + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + pIrlapCb->State = CONN_MEDIA_SENSE; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (media sense)"))); + break; + + case DSCV_REPLY: + return IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR; + + case P_CLOSE: + memcpy(pIrlapCb->RemoteDevice.DevAddr, + pMsg->IRDA_MSG_RemoteDevAddr, IRDA_DEV_ADDR_LEN); + pIrlapCb->ConnAfterClose = TRUE; + break; + + default: + return IRLAP_BAD_STATE; + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessConnectResp | Process connect response from LMP +* +* @rdesc 0, otherwise one of the following errors: +* @flag IRLAP_BAD_STATE | Requested connection in an invalid state +* +* @parm IRDA_MSG * | pMsg | pointer to an IRDA_MSG +* +* @comm +* comments +*/ +UINT +ProcessConnectResp(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg) +{ + + if (pIrlapCb->State != SNRM_RECEIVED) + { + return IRLAP_BAD_STATE; + } + + pIrlapCb->ConnAddr = pIrlapCb->SNRMConnAddr; + RetOnErr(SendUA(pIrlapCb, TRUE)); + RetOnErr(ApplyQosParms(pIrlapCb)); + + RetOnErr(InitializeState(pIrlapCb, SECONDARY)); + // start watchdog timer with poll timeout + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + pIrlapCb->State = S_NRM; + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessDiscoveryReq | Process Discovery request from LMP +* +* @rdesc 0, otherwise one of the following errors: +* @flag IRLAP_BAD_STATE | Requested discovery in an invalid state +* +* @comm +* comments +*/ +UINT +ProcessDiscoveryReq(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg) +{ + IRDA_MSG IMsg; + + switch (pIrlapCb->State) + { + case NDM: + if (pMsg->IRDA_MSG_SenseMedia == TRUE) + { + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_MEDIA_SENSE_TIME; + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + pIrlapCb->State = DSCV_MEDIA_SENSE; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (media sense)"))); + } + else + { + pIrlapCb->SlotCnt = 0; + pIrlapCb->GenNewAddr = FALSE; + + ClearDevList(&pIrlapCb->DevList); + + RetOnErr(SendDscvXIDCmd(pIrlapCb)); + + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_DSCV_SENSE_TIME; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (dscv sense)"))); + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + + pIrlapCb->State = DSCV_QUERY; + } + break; + + case DSCV_REPLY: + return IRLAP_REMOTE_DISCOVERY_IN_PROGRESS_ERR; + + case SNRM_RECEIVED: + return IRLAP_REMOTE_CONNECTION_IN_PROGRESS_ERR; + + case P_CLOSE: + pIrlapCb->DscvAfterClose = TRUE; + break; + + default: + return IRLAP_BAD_STATE; + } + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessDisconnectReq | Process disconnect request from LMP +* +* @rdesc 0, otherwise one of the following errors: +* @flag IRLAP_BAD_STATE | Requested disconnect in an invalid state +* +* @comm +* comments +*/ +UINT +ProcessDisconnectReq(PIRLAP_CB pIrlapCb) +{ + RetOnErr(ReturnTxMsgs(pIrlapCb)); + + switch (pIrlapCb->State) + { + case NDM: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + break; + + case SNRM_SENT: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + case DSCV_REPLY: + case CONN_MEDIA_SENSE: + pIrlapCb->State = NDM; + break; + + case BACKOFF_WAIT: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->BackoffTimer); + pIrlapCb->State = NDM; + break; + + case SNRM_RECEIVED: + pIrlapCb->ConnAddr = pIrlapCb->SNRMConnAddr; + RetOnErr(SendDM(pIrlapCb)); + pIrlapCb->ConnAddr = IRLAP_BROADCAST_CONN_ADDR; + pIrlapCb->State = NDM; + break; + + case P_XMIT: + pIrlapCb->LocalDiscReq = TRUE; + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->PollTimer); + RetOnErr(SendDISC(pIrlapCb)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->RetryCnt = 0; + pIrlapCb->State = P_CLOSE; + break; + + case P_RECV: + pIrlapCb->LocalDiscReq = TRUE; + pIrlapCb->State = P_DISCONNECT_PEND; + break; + + case S_NRM: + pIrlapCb->LocalDiscReq = TRUE; + pIrlapCb->State = S_DISCONNECT_PEND; + break; + + default: + return IRLAP_BAD_STATE; + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessDataReq | Process data request from LMP +* +* @rdesc 0, otherwise one of the following errors: +* @flag IRLAP_BAD_STATE | Requested data in an invalid state +* @flag IRLAP_TX_MSG_LIST_FULL | Tx Msg List has become full, can't process +* +* @comm +* comments +*/ +UINT +ProcessDataAndUDataReq(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg) +{ + BOOL LinkTurned; + int DataSize = (pMsg->IRDA_MSG_pHdrWrite - pMsg->IRDA_MSG_pHdrRead) + + (pMsg->IRDA_MSG_pWrite - pMsg->IRDA_MSG_pRead); + + if (DataSize > pIrlapCb->RemoteDataSize) + { + return IRLAP_BAD_DATA_REQUEST; + } + + switch (pIrlapCb->State) + { + case P_XMIT: + // Enque message, then drain the message list. If the link + // was turned in the process of draining messages stop Poll Timer, + // start Final Timer and enter P_RECV. Otherwise we'll stay in P_XMIT + // waiting for more data requests from LMP or Poll Timer expiration + RetOnErr(EnqueMsgList(&pIrlapCb->TxMsgList, pMsg, -1)); + + RetOnErr(XmitTxMsgList(pIrlapCb, FALSE, &LinkTurned)); + + if (LinkTurned) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->PollTimer); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->State = P_RECV; + } + return SUCCESS; + + case P_DISCONNECT_PEND: // For pending disconnect states, take the message. + case S_DISCONNECT_PEND: // They will be returned when the link disconnects + case P_RECV: + case S_NRM: + // Que the message for later transmission + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Queueing request"))); + + RetOnErr(EnqueMsgList(&pIrlapCb->TxMsgList, pMsg, -1)); + + return SUCCESS; + + default: + if (pMsg->Prim == IRLAP_DATA_REQ) + { + return IRLAP_BAD_STATE; + } + else + { + if (pIrlapCb->State == NDM) + { + return SendUIFrame(pIrlapCb, pMsg); + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + } + } + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +XmitTxMsgList(PIRLAP_CB pIrlapCb, BOOL AlwaysTurnLink, BOOL *pLinkTurned) +{ + UINT rc = SUCCESS; + IRDA_MSG *pMsg; + UINT LinkTurned; + + LinkTurned = FALSE; + + // If the remote is not busy send data + // If we need to clear the local busy condition, don't send data send RR + if (!pIrlapCb->RemoteBusy && !pIrlapCb->ClrLocalBusy) + { + while ((rc == SUCCESS) && !LinkTurned && + (DequeMsgList(&pIrlapCb->TxMsgList, &pMsg) == SUCCESS)) + { + if (pMsg->Prim == IRLAP_DATA_REQ) + { + // Insert message into transmit window + pIrlapCb->TxWin.pMsg[pIrlapCb->Vs] = pMsg; + + // Send message. If full window or there are no + // more data requests, send with PF Set (turns link). + if ((pIrlapCb->Vs == (pIrlapCb->TxWin.Start + + pIrlapCb->RemoteWinSize-1) % IRLAP_MOD) || + (0 == pIrlapCb->TxMsgList.Len /*AlwaysTurnLink*/)) + { + rc = SendIFrame(pIrlapCb, + pMsg, + pIrlapCb->Vs, + IRLAP_PFBIT_SET); + LinkTurned = TRUE; + } + else + { + rc = SendIFrame(pIrlapCb, + pMsg, + pIrlapCb->Vs, + IRLAP_PFBIT_CLEAR); + } + pIrlapCb->Vs = (pIrlapCb->Vs + 1) % IRLAP_MOD; + } + else // IRLAP_UDATA_REQUEST + { + // For now, always turn link + rc = SendUIFrame(pIrlapCb, pMsg); + pMsg->Prim = IRLAP_UDATA_CONF; + pMsg->IRDA_MSG_DataStatus = IRLAP_DATA_REQUEST_COMPLETED; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, pMsg)); + LinkTurned = TRUE; + } + } + pIrlapCb->TxWin.End = pIrlapCb->Vs; + } + + if (rc == SUCCESS) + { + if ((AlwaysTurnLink && !LinkTurned) || pIrlapCb->ClrLocalBusy) + { + rc = SendRR_RNR(pIrlapCb); + LinkTurned = TRUE; + if (pIrlapCb->ClrLocalBusy) + { + pIrlapCb->ClrLocalBusy = FALSE; + } + } + } + + if (pLinkTurned != NULL) + { + *pLinkTurned = LinkTurned; + } + + return (rc); +} + +UINT +GotoPCloseState(PIRLAP_CB pIrlapCb) +{ + if (!pIrlapCb->LocalDiscReq) + { + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_REMOTE_INITIATED; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + } + + pIrlapCb->State = P_CLOSE; + + return SUCCESS; +} + +UINT +GotoNDMThenDscvOrConn(PIRLAP_CB pIrlapCb) +{ + if (pIrlapCb->ConnAfterClose) + { + pIrlapCb->ConnAfterClose = FALSE; + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_MEDIA_SENSE_TIME; + + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + pIrlapCb->State = CONN_MEDIA_SENSE; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (media sense)"))); + + return SUCCESS; + } + + if (pIrlapCb->DscvAfterClose) + { + pIrlapCb->DscvAfterClose = FALSE; + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_MEDIA_SENSE_TIME; + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + pIrlapCb->State = DSCV_MEDIA_SENSE; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (media sense)"))); + return SUCCESS; + } + pIrlapCb->State = NDM; + return SUCCESS; +} + +/***************************************************************************** +* +* @func UINT | ProcessMACControlConf | Process a control confirm from MAC +* +* @rdesc SUCCESS, otherwise one of the following error codes +* @flag IRLAP_BAD_OP | Bad Operation, must be MAC_MEDIA_SENSE +* @flag IRLAP_BAD_OPSTATUS | Invalid return status for operation +* @flag IRLAP_BAD_STATE | CONTROL_CONF in invalid state +* +* @parm IRDA_MSG * | pMsg | pointer to an IRDA_MSG +* +* @comm +* comments +* +*/ +UINT +ProcessMACControlConf(PIRLAP_CB pIrlapCb, PIRDA_MSG pMsg) +{ + if (pMsg->IRDA_MSG_Op != MAC_MEDIA_SENSE) + return IRLAP_BAD_OP; + + switch (pIrlapCb->State) + { + case DSCV_MEDIA_SENSE: + switch (pMsg->IRDA_MSG_OpStatus) + { + case MAC_MEDIA_CLEAR: + pIrlapCb->SlotCnt = 0; + pIrlapCb->GenNewAddr = FALSE; + + ClearDevList(&pIrlapCb->DevList); + + RetOnErr(SendDscvXIDCmd(pIrlapCb)); + + pMsg->Prim = MAC_CONTROL_REQ; + pMsg->IRDA_MSG_Op = MAC_MEDIA_SENSE; + pMsg->IRDA_MSG_SenseTime = IRLAP_DSCV_SENSE_TIME; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (dscv sense)"))); + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,pMsg)); + + pIrlapCb->State = DSCV_QUERY; + break; + + case MAC_MEDIA_BUSY: + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = NULL; + IMsg.IRDA_MSG_DscvStatus = MAC_MEDIA_BUSY; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + break; + + default: + return IRLAP_BAD_OPSTATUS; + } + break; + + case CONN_MEDIA_SENSE: + switch (pMsg->IRDA_MSG_OpStatus) + { + case MAC_MEDIA_CLEAR: + + // Generate a random connection address + pIrlapCb->ConnAddr = IRLAP_RAND(1, 0x7e); + + pIrlapCb->RetryCnt = 0; + + RetOnErr(SendSNRM(pIrlapCb, TRUE)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->State = SNRM_SENT; + break; + + case MAC_MEDIA_BUSY: + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = MAC_MEDIA_BUSY; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + break; + + default: + return IRLAP_BAD_OPSTATUS; + } + break; + + case DSCV_QUERY: + switch (pMsg->IRDA_MSG_OpStatus) + { + case MAC_MEDIA_CLEAR: + // Nobody responded, procede as if the slot timer expired + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Media clear, making fake slot exp"))); + + SlotTimerExp(pIrlapCb); + break; + + case MAC_MEDIA_BUSY: + // Some responding, give'm more time + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Media busy, starting slot timer"))); + + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->SlotTimer); + break; + } + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessMACDataInd | Processes MAC Data +* +* @rdesc SUCCESS, otherwise one of the following error codes +* @flag ?? | invalid return status for operation +* +* @parm IRDA_MSG * | pMsg | pointer to an IRDA_MSG +* +* @comm +* +*/ +UINT +ProcessMACDataInd(PIRLAP_CB pIrlapCb, PIRDA_MSG pMsg, BOOL *pFreeMsg) +{ + int Addr = (int) IRLAP_GET_ADDR(*(pMsg->IRDA_MSG_pRead)); + int CRBit = (int) IRLAP_GET_CRBIT(*(pMsg->IRDA_MSG_pRead)); + int Cntl = (int) *(pMsg->IRDA_MSG_pRead + 1); + int PFBit = IRLAP_GET_PFBIT(Cntl); + UINT Ns = IRLAP_GET_NS(Cntl); + UINT Nr = IRLAP_GET_NR(Cntl); + int XIDFormatID = (int) *(pMsg->IRDA_MSG_pRead+2); + IRLAP_XID_DSCV_FORMAT *pXIDFormat = (IRLAP_XID_DSCV_FORMAT *) + (pMsg->IRDA_MSG_pRead + 3); + IRLAP_SNRM_FORMAT *pSNRMFormat = (IRLAP_SNRM_FORMAT *) + (pMsg->IRDA_MSG_pRead + 2); + IRLAP_UA_FORMAT *pUAFormat = (IRLAP_UA_FORMAT *) + (pMsg->IRDA_MSG_pRead + 2); + + if (Addr != pIrlapCb->ConnAddr && Addr != IRLAP_BROADCAST_CONN_ADDR) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Ignoring, connection address %02X"), Addr)); + return SUCCESS; + } + + pIrlapCb->StatusSent = FALSE; // don't ask + + FrmRejFormat.CntlField = Cntl; // for later maybe + + // Peer has sent a frame so clear the NoResponse condition + if (pIrlapCb->NoResponse) + { + pIrlapCb->NoResponse = FALSE; + pIrlapCb->RetryCnt = 0; + pIrlapCb->WDogExpCnt = 0; + } + + switch (IRLAP_FRAME_TYPE(Cntl)) + { + /*****************/ + case IRLAP_I_FRAME: + /*****************/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("I-frame"))); + return ProcessIFrame(pIrlapCb, pMsg, + CRBit, PFBit, Ns, Nr, pFreeMsg); + + /*****************/ + case IRLAP_S_FRAME: + /*****************/ + switch (IRLAP_GET_SCNTL(Cntl)) + { + /*-----------*/ + case IRLAP_RR: + case IRLAP_RNR: + /*-----------*/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("RR/RNR-frame"))); + return ProcessRR_RNR(pIrlapCb, + IRLAP_GET_SCNTL(Cntl), + pMsg, CRBit, PFBit, Nr); + /*------------*/ + case IRLAP_SREJ: + case IRLAP_REJ: + /*------------*/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("SJREJ/REJ-frame"))); + return ProcessREJ_SREJ(pIrlapCb, + IRLAP_GET_SCNTL(Cntl), + pMsg, CRBit, PFBit, Nr); + } + break; + + /*****************/ + case IRLAP_U_FRAME: + /*****************/ + switch (IRLAP_GET_UCNTL(Cntl)) + { + /*---------------*/ + case IRLAP_XID_CMD: + /*---------------*/ + // Should always be a command + if (CRBit != IRLAP_CMD) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Received XID cmd with CRBit = rsp"))); + return IRLAP_XID_CMD_RSP; + } + // Poll bit should always be set + if (PFBit != IRLAP_PFBIT_SET) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Received XID command without Poll set"))); + return IRLAP_XID_CMD_NOT_P; + } + + if (XIDFormatID == IRLAP_XID_DSCV_FORMAT_ID) + { + // Slot No is less than max slot or 0xff + if (pXIDFormat->SlotNo>IRLAP_SlotTable[pXIDFormat->NoOfSlots] + && pXIDFormat->SlotNo != IRLAP_END_DSCV_SLOT_NO) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Invalid slot number %d"), + pXIDFormat->SlotNo)); + return IRLAP_BAD_SLOTNO; + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("DscvXIDCmd"))); + return ProcessDscvXIDCmd(pIrlapCb, + pXIDFormat, + pMsg->IRDA_MSG_pWrite); + } + else + { + return SUCCESS; // ignore per errata + } + + /*---------------*/ + case IRLAP_XID_RSP: + /*---------------*/ + if (XIDFormatID == IRLAP_XID_DSCV_FORMAT_ID) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("DscvXIDRsp"))); + return ProcessDscvXIDRsp(pIrlapCb, + pXIDFormat,pMsg->IRDA_MSG_pWrite); + } + else + { + return SUCCESS; // ignore per errata + } + + /*------------*/ + case IRLAP_SNRM: // or IRLAP_RNRM + /*------------*/ + if (IRLAP_PFBIT_SET != PFBit) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Received SNRM/RNRM without P set"))); + return IRLAP_SNRM_NOT_P; + } + if (IRLAP_CMD == CRBit) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("SNRM"))); + return ProcessSNRM(pIrlapCb, + pSNRMFormat, + pMsg->IRDA_MSG_pWrite); + } + else + { + return ProcessRNRM(pIrlapCb); + } + + /*----------*/ + case IRLAP_UA: + /*----------*/ + if (CRBit != IRLAP_RSP) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Received UA as a command"))); + return IRLAP_UA_NOT_RSP; + } + if (PFBit != IRLAP_PFBIT_SET) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Received UA without F set"))); + return IRLAP_UA_NOT_F; + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("UA"))); + return ProcessUA(pIrlapCb, pUAFormat, pMsg->IRDA_MSG_pWrite); + + /*------------*/ + case IRLAP_DISC: // or IRLAP_RD + /*------------*/ + if (IRLAP_PFBIT_SET != PFBit) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Received DISC/RD command without Poll set"))); + return IRLAP_DISC_CMD_NOT_P; + } + if (IRLAP_CMD == CRBit) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("DISC"))); + return ProcessDISC(pIrlapCb); + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("RD"))); + return ProcessRD(pIrlapCb); + } + + /*----------*/ + case IRLAP_UI: + /*----------*/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("UI"))); + return ProcessUI(pIrlapCb, pMsg, CRBit, PFBit); + + /*------------*/ + case IRLAP_TEST: + /*------------*/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("TEST"))); + return ProcessTEST(pIrlapCb, pMsg, pUAFormat, CRBit, PFBit); + + /*------------*/ + case IRLAP_FRMR: + /*------------*/ + if (IRLAP_RSP != CRBit) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Received FRMR cmd (must be resp)"))); + return IRLAP_FRMR_RSP_CMD; + } + if (IRLAP_PFBIT_SET != PFBit) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Received FRMR resp without Final set"))); + return IRLAP_FRMR_RSP_NOT_F; + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("FRMR"))); + return ProcessFRMR(pIrlapCb); + + /*----------*/ + case IRLAP_DM: + /*----------*/ + if (IRLAP_RSP != CRBit) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Received DM command (must be response)"))); + return IRLAP_DM_RSP_CMD; + } + if (IRLAP_PFBIT_SET != PFBit) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Received DM response without Final set"))); + return IRLAP_DM_RSP_NOT_F; + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("DM"))); + return ProcessDM(pIrlapCb); + } + break; + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessDscvXIDCmd | Process received XID Discovery command +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +UINT +ProcessDscvXIDCmd(PIRLAP_CB pIrlapCb, + IRLAP_XID_DSCV_FORMAT *pXIDFormat, + BYTE *pEndDscvInfoByte) +{ + if (!MyDevAddr(pIrlapCb, pXIDFormat->DestAddr)) + { +/* IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring XID addressed to:%02X%02X%02X%02X"), + EXPAND_ADDR(pXIDFormat->DestAddr)));*/ + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring XID addressed to %X"), + pXIDFormat->DestAddr)); + return SUCCESS; + } + + if (pXIDFormat->SlotNo == IRLAP_END_DSCV_SLOT_NO) + { + pIrlapCb->GenNewAddr = FALSE; + switch (pIrlapCb->State) + { + case DSCV_QUERY: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->SlotTimer); + + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = NULL; + IMsg.IRDA_MSG_DscvStatus = + IRLAP_REMOTE_DISCOVERY_IN_PROGRESS; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + // fall through. Send indication to LMP + + case DSCV_REPLY: + if (pIrlapCb->State == DSCV_REPLY) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->QueryTimer); + } + + // Place the device information in the control block + ExtractDeviceInfo(&pIrlapCb->RemoteDevice, pXIDFormat, + pEndDscvInfoByte); + + if (!DevInDevList(pXIDFormat->SrcAddr, &pIrlapCb->DevList)) + { + RetOnErr(AddDevToList(pIrlapCb, + pXIDFormat, + pEndDscvInfoByte)); + } + + // Notifiy LMP + IMsg.Prim = IRLAP_DISCOVERY_IND; + IMsg.IRDA_MSG_pDevList = &pIrlapCb->DevList; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring End XID in this state"))); + } + } + else // in middle of discovery process + { + switch (pIrlapCb->State) + { + case DSCV_MEDIA_SENSE: + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = NULL; + IMsg.IRDA_MSG_DscvStatus = + IRLAP_REMOTE_DISCOVERY_IN_PROGRESS; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + // fall through + + case NDM: + RetOnErr(InitDscvCmdProcessing(pIrlapCb, pXIDFormat)); + pIrlapCb->State = DSCV_REPLY; + break; + + case DSCV_QUERY: + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = NULL; + IMsg.IRDA_MSG_DscvStatus = IRLAP_DISCOVERY_COLLISION; + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->SlotTimer); + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + break; + + case DSCV_REPLY: + if (pXIDFormat->GenNewAddr) + { + pIrlapCb->GenNewAddr = TRUE; + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->QueryTimer); + RetOnErr(InitDscvCmdProcessing(pIrlapCb, pXIDFormat)); + } + else + { + if (pIrlapCb->RespSlot <= pXIDFormat->SlotNo && + !pIrlapCb->DscvRespSent) + { + RetOnErr(SendDscvXIDRsp(pIrlapCb)); + pIrlapCb->DscvRespSent = TRUE; + } + } + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +void +ExtractDeviceInfo(IRDA_DEVICE *pDevice, IRLAP_XID_DSCV_FORMAT *pXIDFormat, + BYTE *pEndDscvInfoByte) +{ + memcpy(pDevice->DevAddr, pXIDFormat->SrcAddr, IRDA_DEV_ADDR_LEN); + pDevice->IRLAP_Version = pXIDFormat->Version; + + // ??? what about DscvMethod + + pDevice->DscvInfoLen = pEndDscvInfoByte > &pXIDFormat->FirstDscvInfoByte ? + pEndDscvInfoByte-&pXIDFormat->FirstDscvInfoByte : + 0; + memcpy(pDevice->DscvInfo, &pXIDFormat->FirstDscvInfoByte, + pDevice->DscvInfoLen); +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +InitDscvCmdProcessing(PIRLAP_CB pIrlapCb, + IRLAP_XID_DSCV_FORMAT *pXIDFormat) +{ + pIrlapCb->RemoteMaxSlot = IRLAP_SlotTable[pXIDFormat->NoOfSlots]; + + pIrlapCb->RespSlot = IRLAP_RAND(pXIDFormat->SlotNo, + pIrlapCb->RemoteMaxSlot - 1); + + memcpy(pIrlapCb->RemoteDevice.DevAddr, pXIDFormat->SrcAddr, IRDA_DEV_ADDR_LEN); + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Responding in slot %d to device %02X%02X%02X%02X"), + pIrlapCb->RespSlot, + pIrlapCb->RemoteDevice.DevAddr[0], + pIrlapCb->RemoteDevice.DevAddr[1], + pIrlapCb->RemoteDevice.DevAddr[2], + pIrlapCb->RemoteDevice.DevAddr[3])); + + if (pIrlapCb->RespSlot == pXIDFormat->SlotNo) + { + RetOnErr(SendDscvXIDRsp(pIrlapCb)); + pIrlapCb->DscvRespSent = TRUE; + } + else + { + pIrlapCb->DscvRespSent = FALSE; + } + + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->QueryTimer); + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessDscvXIDRsp | Process received XID Discovery response +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +UINT +ProcessDscvXIDRsp(PIRLAP_CB pIrlapCb, + IRLAP_XID_DSCV_FORMAT *pXIDFormat, + BYTE *pEndDscvInfoByte) +{ + if (pIrlapCb->State == DSCV_QUERY) + { + + if (DevInDevList(pXIDFormat->SrcAddr, &pIrlapCb->DevList)) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->SlotTimer); + pIrlapCb->SlotCnt = 0; + pIrlapCb->GenNewAddr = TRUE; + ClearDevList(&pIrlapCb->DevList); + RetOnErr(SendDscvXIDCmd(pIrlapCb)); + + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_DSCV_SENSE_TIME; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (dscv sense)"))); + RetOnErr(IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); + } + else + { + RetOnErr(AddDevToList(pIrlapCb, pXIDFormat, pEndDscvInfoByte)); + } + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + + + return SUCCESS; +} +/***************************************************************************** +* +* @func BOOL | DevInDevList | Determines if given device is already in list +* +* @rdesc returns: +* @flag TRUE | if device is alreay in list +* @flag FALSE | if device is not in list +* +* @parm BYTE | DevAddr[] | Device address +* @parm IRDA_DEVICE * | pDevList | pointer to list of devices +* +*/ +BOOL +DevInDevList(BYTE DevAddr[], LIST_ENTRY *pDevList) +{ + IRDA_DEVICE *pDevice; + + pDevice = (IRDA_DEVICE *) pDevList->Flink; + + while (pDevList != (LIST_ENTRY *) pDevice) + { + if (memcmp(pDevice->DevAddr, DevAddr, IRDA_DEV_ADDR_LEN) == 0) + return (TRUE); + + pDevice = (IRDA_DEVICE *) pDevice->Linkage.Flink; + } + return (FALSE); +} +/***************************************************************************** +* +* @func void | AddDevToList | Adds elements in a device list +* +* @parm IRDA_DEVICE ** | ppDevList | address of pointer to an +* IRDA device list +* +*/ +UINT +AddDevToList(PIRLAP_CB pIrlapCb, + IRLAP_XID_DSCV_FORMAT *pXIDFormat, + BYTE *pEndDscvInfoByte) +{ + IRDA_DEVICE *pDevice; + + if (IRDA_ALLOC_MEM(pDevice, sizeof(IRDA_DEVICE), MT_IRLAP_DEVICE) == NULL) + { + return (IRLAP_MALLOC_FAILED); + } + else + { + ExtractDeviceInfo(pDevice, pXIDFormat, pEndDscvInfoByte); + + InsertTailList(&pIrlapCb->DevList, &(pDevice->Linkage)); + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("%02X%02X%02X%02X added to Device List"), + EXPAND_ADDR(pDevice->DevAddr))); + } + return SUCCESS; +} +/***************************************************************************** +* +* @func void | ClearDevList | Frees elements in a device list +* +* @parm IRDA_DEVICE ** | ppDevList | address of pointer to an +* IRDA device list +* +*/ +void +ClearDevList(LIST_ENTRY *pDevList) +{ + IRDA_DEVICE *pDevice; + + while (IsListEmpty(pDevList) == FALSE) + { + pDevice = (IRDA_DEVICE *) RemoveHeadList(pDevList); + IRDA_FREE_MEM(pDevice); + } + + //IRLAP_LOG_ACTION((pIrlapCb, TEXT("Device list cleared"))); +} +/***************************************************************************** +* +* @func UINT | ProcessSNRM | process received SNRM frame +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm IRLAP_SNRM_FORMAT * | pSNRMFormat | Pointer to SNRM frame +* Information Field +* BYTE * | pLastQosByte | Pointer to last byte in SNRM +* +* @comm +* comments +*/ +UINT +ProcessSNRM(PIRLAP_CB pIrlapCb, + IRLAP_SNRM_FORMAT *pSNRMFormat, + BYTE *pEndQosByte) +{ + BOOL Qos_InSNRM = &pSNRMFormat->FirstQosByte < pEndQosByte;// Is there Qos? + BOOL Addrs_InSNRM = (BYTE *)pSNRMFormat < pEndQosByte; + + if (Addrs_InSNRM) + { + if (!MyDevAddr(pIrlapCb, pSNRMFormat->DestAddr)) + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Ignoring SNRM addressed to:%02X%02X%02X%02X"), + EXPAND_ADDR(pSNRMFormat->DestAddr))); + return SUCCESS; + } + memcpy(pIrlapCb->RemoteDevice.DevAddr, + pSNRMFormat->SrcAddr, IRDA_DEV_ADDR_LEN); + } + + switch (pIrlapCb->State) + { + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + // In the middle of discovery... End discovery and reply to SNRM + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = NULL; + IMsg.IRDA_MSG_DscvStatus = IRLAP_REMOTE_CONNECTION_IN_PROGRESS; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + // fall through and send connect indication + case DSCV_REPLY: + case NDM: + if (Addrs_InSNRM) + { + pIrlapCb->SNRMConnAddr = (int)IRLAP_GET_ADDR(pSNRMFormat->ConnAddr); + } + if (Qos_InSNRM) + { + ExtractQosParms(&pIrlapCb->RemoteQos, &pSNRMFormat->FirstQosByte, + pEndQosByte); + + RetOnErr(NegotiateQosParms(pIrlapCb, &pIrlapCb->RemoteQos)); + } + + memcpy(IMsg.IRDA_MSG_RemoteDevAddr, + pIrlapCb->RemoteDevice.DevAddr, IRDA_DEV_ADDR_LEN); + IMsg.IRDA_MSG_pQOS = &pIrlapCb->NegotiatedQos; + IMsg.Prim = IRLAP_CONNECT_IND; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = SNRM_RECEIVED; + break; + + case BACKOFF_WAIT: // CROSSED SNRM + // if Remote address greater than mine we'll respond to SNRM + if (Addrs_InSNRM) + { + if (memcmp(pSNRMFormat->SrcAddr, + pIrlapCb->LocalDevice.DevAddr, IRDA_DEV_ADDR_LEN) > 0) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->BackoffTimer); + } + } + // fall through + case CONN_MEDIA_SENSE: // CROSSED SNRM + case SNRM_SENT: + // if Remote address greater than mine we'll respond to SNRM + if (Addrs_InSNRM && + memcmp(pSNRMFormat->SrcAddr, + pIrlapCb->LocalDevice.DevAddr, IRDA_DEV_ADDR_LEN) > 0) + { + if (pIrlapCb->State != BACKOFF_WAIT) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + InitializeState(pIrlapCb, SECONDARY); + + if (Qos_InSNRM) + { + ExtractQosParms(&pIrlapCb->RemoteQos, + &pSNRMFormat->FirstQosByte, pEndQosByte); + RetOnErr(NegotiateQosParms(pIrlapCb,&pIrlapCb->RemoteQos)); + } + + if (Addrs_InSNRM) + { + pIrlapCb->ConnAddr = (int)IRLAP_GET_ADDR(pSNRMFormat->ConnAddr); + } + + RetOnErr(SendUA(pIrlapCb, TRUE)); + + if (Qos_InSNRM) + { + RetOnErr(ApplyQosParms(pIrlapCb)); + } + + IMsg.IRDA_MSG_pQOS = &pIrlapCb->NegotiatedQos; + IMsg.Prim = IRLAP_CONNECT_CONF; + IMsg.IRDA_MSG_ConnStatus = IRLAP_CONNECTION_COMPLETED; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + pIrlapCb->State = S_NRM; + } + break; + + case P_RECV: + case P_DISCONNECT_PEND: + case P_CLOSE: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(StationConflict(pIrlapCb)); + RetOnErr(ReturnTxMsgs(pIrlapCb)); + if (pIrlapCb->State == P_CLOSE) + { + RetOnErr(GotoNDMThenDscvOrConn(pIrlapCb)); + } + else + { + pIrlapCb->State = NDM; + } + break; + + case S_NRM: + case S_CLOSE: + case S_DISCONNECT_PEND: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(SendDM(pIrlapCb)); + RetOnErr(ApplyDefaultParms(pIrlapCb)); + IMsg.Prim = IRLAP_DISCONNECT_IND; + if (pIrlapCb->State == S_NRM) + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_DECLINE_RESET; + } + else + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_DISCONNECT_COMPLETED; + } + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + break; + + case S_ERROR: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + pIrlapCb->State = S_NRM; + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("SNRM ignored in this state"))); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ProcessUA | process received UA frame +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm IRLAP_UA_FORMAT * | pUAFormat | Pointer to UA frame +* Information Field +* BYTE * | pLastQosByte | Pointer to last byte in SNRM +* +* @comm +* When &pUAFormat->FirstQosByte = pLastQosByte there is no Qos in UA +*/ +UINT +ProcessUA(PIRLAP_CB pIrlapCb, + IRLAP_UA_FORMAT *pUAFormat, + BYTE *pEndQosByte) +{ + BOOL Qos_InUA = &pUAFormat->FirstQosByte < pEndQosByte;// Is there QOS? + BOOL Addrs_InUA = (BYTE *)pUAFormat < pEndQosByte; + int Tmp; + + if (Addrs_InUA && !MyDevAddr(pIrlapCb, pUAFormat->DestAddr)) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring UA addressed to:%02X%02X%02X%02X"), + EXPAND_ADDR(pUAFormat->DestAddr))); + return SUCCESS; + } + + switch (pIrlapCb->State) + { + case BACKOFF_WAIT: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->BackoffTimer); + // fall through + case SNRM_SENT: + if (pIrlapCb->State != BACKOFF_WAIT) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + + InitializeState(pIrlapCb, PRIMARY); + + if (Qos_InUA) + { + ExtractQosParms(&pIrlapCb->RemoteQos, &pUAFormat->FirstQosByte, + pEndQosByte); + + RetOnErr(NegotiateQosParms(pIrlapCb,&pIrlapCb->RemoteQos)); + + RetOnErr(ApplyQosParms(pIrlapCb)); + } + + IMsg.IRDA_MSG_pQOS = &pIrlapCb->NegotiatedQos; + + IMsg.Prim = IRLAP_CONNECT_CONF; + IMsg.IRDA_MSG_ConnStatus = IRLAP_CONNECTION_COMPLETED; + + // notify LMP of connection + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + + // send RR (turn link), start FinalTimer/2 + RetOnErr(SendRR_RNR(pIrlapCb)); + + Tmp = pIrlapCb->FinalTimer.Timeout; + pIrlapCb->FinalTimer.Timeout = pIrlapCb->FinalTimer.Timeout/2; + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->FinalTimer.Timeout = Tmp; + + pIrlapCb->State = P_RECV; + break; + + case P_RECV: // Unsolicited UA, may want to do something else ??? + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->PollTimer); + pIrlapCb->State = P_XMIT; + break; + + case P_DISCONNECT_PEND: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(SendDISC(pIrlapCb)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->RetryCnt = 0; + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case P_CLOSE: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(ApplyDefaultParms(pIrlapCb)); + if (pIrlapCb->LocalDiscReq == TRUE) + { + pIrlapCb->LocalDiscReq = FALSE; + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_DISCONNECT_COMPLETED; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + } + RetOnErr(GotoNDMThenDscvOrConn(pIrlapCb)); + break; + + case S_NRM: + case S_DISCONNECT_PEND: + case S_ERROR: + case S_CLOSE: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("UA ignored in this state"))); + } + + return SUCCESS; +} + +BYTE * +GetPv(BYTE *pQosByte, + UINT *pBitField) +{ + int Pl = (int) *pQosByte++; + + *pBitField = 0; + + if (Pl == 1) + { + *pBitField = (UINT) *pQosByte; + } + else + { + *pBitField = ((UINT) *pQosByte)<<8; + *pBitField |= (UINT) *(pQosByte+1); + } + + return pQosByte + Pl; +} +/***************************************************************************** +* +* @func void | ExtractQosParms | Extracts Qos from SNRM/UA/XID and +* places in an IRDA_QOS_PARM struct +* +* @parm IRDA_QOS_PARMS * | pIRDA_QOSParms | Pointer to QOS parm struct +* BYTE * | pQOSByte | Pointer to first byte of +* QOS in frame +* BYTE * | pEndQOSByte | Pointer to last byte of +* QOS in frame +* @comm +* THIS WILL BREAK IF PARAMETER LENGTH (PL) IS GREATER THAN 2 +*/ +void +ExtractQosParms(IRDA_QOS_PARMS *pQos, + BYTE *pQosByte, + BYTE *pEndQosByte) +{ + while (pQosByte + 2 < pEndQosByte) + { + switch (*pQosByte) + { + case QOS_PI_BAUD: + pQosByte = GetPv(pQosByte, &pQos->bfBaud); + break; + + case QOS_PI_MAX_TAT: + pQosByte = GetPv(pQosByte, &pQos->bfMaxTurnTime); + break; + + case QOS_PI_DATA_SZ: + pQosByte = GetPv(pQosByte, &pQos->bfDataSize); + break; + + case QOS_PI_WIN_SZ: + pQosByte = GetPv(pQosByte, &pQos->bfWindowSize); + break; + + case QOS_PI_BOFS: + pQosByte = GetPv(pQosByte, &pQos->bfBofs); + break; + + case QOS_PI_MIN_TAT: + pQosByte = GetPv(pQosByte, &pQos->bfMinTurnTime); + break; + + case QOS_PI_DISC_THRESH: + pQosByte = GetPv(pQosByte, &pQos->bfDisconnectTime); + break; + + default: + pQosByte += (*(pQosByte+1)); + } + } +} +/***************************************************************************** +* +* @func UINT | NegotiateQosParms | Take the received Qos build +* negotiated Qos. +* +* @rdesc SUCCESS, otherwise one of the folowing: +* @flag IRLAP_BAUD_NEG_ERR | Failed to negotiate baud +* @flag IRLAP_DISC_NEG_ERR | Failed to negotiate disconnect time +* @flag IRLAP_MAXTAT_NEG_ERR | Failed to negotiate max turn time +* @flag IRLAP_DATASIZE_NEG_ERR | Failed to negotiate data size +* @flag IRLAP_WINSIZE_NEG_ERR | Failed to negotiate window size +* @flag IRLAP_BOFS_NEG_ERR | Failed to negotiate number of BOFS +* @flag IRLAP_WINSIZE_NEG_ERR | Failed to window size +* @flag IRLAP_LINECAP_ERR | Failed to determine valid line capacity +* +* @parm IRDA_QOS_PARMS * | pRemoteQos | Pointer to QOS parm struct +*/ +UINT +NegotiateQosParms(PIRLAP_CB pIrlapCb, + IRDA_QOS_PARMS *pRemoteQos) +{ + UINT BitSet; + BOOL ParmSet = FALSE; + UINT BOFSDivisor = 1; + UINT MaxLineCap = 0; + UINT LineCapacity; + UINT DataSizeBit = 0; + UINT WinSizeBit = 0; + UINT WSBit; + int RemoteDataSize = 0; + int RemoteWinSize = 0; + + // Baud rate is Type 0 parm + pIrlapCb->Baud = IrlapGetQosParmVal(vBaudTable, + (BYTE) (pIrlapCb->LocalQos.bfBaud & pRemoteQos->bfBaud), + &BitSet); + BOFSDivisor = IrlapGetQosParmVal(vBOFSDivTable, + (BYTE) (pIrlapCb->LocalQos.bfBaud & pRemoteQos->bfBaud), + &BitSet); + pIrlapCb->NegotiatedQos.bfBaud = BitSet; + + if (-1 == pIrlapCb->Baud) + { + return (IRLAP_BAUD_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Negotiated Baud:%d"), pIrlapCb->Baud)); + + // Disconnect/Threshold time is Type 0 parm + pIrlapCb->DisconnectTime = IrlapGetQosParmVal(vDiscTable, + (BYTE)(pIrlapCb->LocalQos.bfDisconnectTime & + pRemoteQos->bfDisconnectTime), &BitSet); + pIrlapCb->ThresholdTime = IrlapGetQosParmVal(vThreshTable, + (BYTE)(pIrlapCb->LocalQos.bfDisconnectTime & + pRemoteQos->bfDisconnectTime), &BitSet); + pIrlapCb->NegotiatedQos.bfDisconnectTime = BitSet; + + if (-1 == pIrlapCb->DisconnectTime) + { + return (IRLAP_DISC_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Negotiated Disconnect/Threshold time:%d/%d"), + pIrlapCb->DisconnectTime, pIrlapCb->ThresholdTime)); + + pIrlapCb->RemoteMaxTAT = IrlapGetQosParmVal(vMaxTATTable, + pRemoteQos->bfMaxTurnTime, + &BitSet); + pIrlapCb->NegotiatedQos.bfMaxTurnTime = BitSet; + if (-1 == pIrlapCb->RemoteMaxTAT) + { + return (IRLAP_MAXTAT_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Remote max turnaround time:%d"), + pIrlapCb->RemoteMaxTAT)); + + pIrlapCb->RemoteMinTAT = IrlapGetQosParmVal(vMinTATTable, + pRemoteQos->bfMinTurnTime, + &BitSet); + pIrlapCb->NegotiatedQos.bfMinTurnTime = BitSet; + if (-1 == pIrlapCb->RemoteMinTAT) + { + return (IRLAP_MINTAT_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Remote min turnaround time:%d"), + pIrlapCb->RemoteMinTAT)); + + // DataSize ISNOT A TYPE 0 PARAMETER. BUT WIN95's IRCOMM implementation + // ASSUMES THAT IT IS. SO FOR NOW, NEGOTIATE IT. grrrr.. + /* WIN95 out + pIrlapCb->RemoteDataSize = IrlapGetQosParmVal(vDataSizeTable, + (BYTE) (pIrlapCb->LocalQos.bfDataSize & + pRemoteQos->bfDataSize), &BitSet); + */ + pIrlapCb->RemoteDataSize = IrlapGetQosParmVal(vDataSizeTable, + pRemoteQos->bfDataSize, &BitSet); + DataSizeBit = BitSet; + pIrlapCb->NegotiatedQos.bfDataSize = BitSet; + if (-1 == pIrlapCb->RemoteDataSize) + { + return (IRLAP_DATASIZE_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Remote data size:%d"), pIrlapCb->RemoteDataSize)); + + pIrlapCb->RemoteWinSize = IrlapGetQosParmVal(vWinSizeTable, + pRemoteQos->bfWindowSize, &BitSet); + WinSizeBit = BitSet; + pIrlapCb->NegotiatedQos.bfWindowSize = BitSet; + if (-1 == pIrlapCb->RemoteWinSize) + { + return (IRLAP_WINSIZE_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Remote window size:%d"), pIrlapCb->RemoteWinSize)); + + pIrlapCb->RemoteNumBOFS=(IrlapGetQosParmVal(vBOFSTable, + pRemoteQos->bfBofs, &BitSet) + / BOFSDivisor)+1; + pIrlapCb->NegotiatedQos.bfBofs = BitSet; + if (-1 == pIrlapCb->RemoteNumBOFS) + { + return (IRLAP_BOFS_NEG_ERR); + } + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Remote number of BOFS:%d"), + pIrlapCb->RemoteNumBOFS)); + + // The maximum line capacity is in bytes and comes from a table in spec. + // (can't calc because table isn't linear). It is determined by the + // maximum line capacity and baud rate. + // + // Later note: Errata corrected table so values could be calculated. + // Could get rid of tables + switch (pIrlapCb->Baud) + { + case 9600: + MaxLineCap = IrlapGetQosParmVal(MAXCAP_9600, + pRemoteQos->bfMaxTurnTime, &BitSet); + break; + + case 19200: + MaxLineCap = IrlapGetQosParmVal(MAXCAP_19200, + pRemoteQos->bfMaxTurnTime, &BitSet); + break; + + case 38400: + MaxLineCap = IrlapGetQosParmVal(MAXCAP_38400, + pRemoteQos->bfMaxTurnTime, &BitSet); + break; + + case 57600: + MaxLineCap = IrlapGetQosParmVal(MAXCAP_57600, + pRemoteQos->bfMaxTurnTime, &BitSet); + break; + + case 115200: + MaxLineCap = IrlapGetQosParmVal(MAXCAP_115200, + pRemoteQos->bfMaxTurnTime, &BitSet); + break; + } + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Maximum line capacity:%d"), MaxLineCap)); + LineCapacity = LINE_CAPACITY(pIrlapCb); + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Requested line capacity:%d"), LineCapacity)); + + if (LineCapacity > MaxLineCap) + { + ParmSet = FALSE; + // Adjust data and window size to fit within the line capacity. + // Get largest possible datasize + for (; DataSizeBit != 0 && !ParmSet; DataSizeBit >>= 1) + { + pIrlapCb->RemoteDataSize = IrlapGetQosParmVal(vDataSizeTable, + DataSizeBit, NULL); + // Start with smallest window + for (WSBit=1; WSBit <= WinSizeBit; WSBit <<=1) + { + pIrlapCb->RemoteWinSize = IrlapGetQosParmVal(vWinSizeTable, + WSBit, NULL); + LineCapacity = LINE_CAPACITY(pIrlapCb); + + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("adjusted data size=%d, window size= %d, line cap=%d"), + pIrlapCb->RemoteDataSize, pIrlapCb->RemoteWinSize, + LineCapacity)); + + if (LineCapacity > MaxLineCap) + { + break; // Get a smaller data size (only if ParmSet is false) + } + ParmSet = TRUE; + // Save the last good one,then loop and try a larger window + RemoteDataSize = pIrlapCb->RemoteDataSize; + RemoteWinSize = pIrlapCb->RemoteWinSize; + pIrlapCb->NegotiatedQos.bfWindowSize = WSBit; + pIrlapCb->NegotiatedQos.bfDataSize = DataSizeBit; + } + } + if (!ParmSet) + { + return (IRLAP_LINECAP_ERR); + } + + pIrlapCb->RemoteDataSize = RemoteDataSize; + pIrlapCb->RemoteWinSize = RemoteWinSize; + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("final data size=%d, window size= %d, line cap=%d"), + pIrlapCb->RemoteDataSize, pIrlapCb->RemoteWinSize, + LINE_CAPACITY(pIrlapCb))); + } + + return (SUCCESS); +} +/***************************************************************************** +* +* @func UINT | ApplyQosParms | Apply negotiated Qos in control block +* +* @rdesc return status from IrmacDown() +*/ +UINT +ApplyQosParms(PIRLAP_CB pIrlapCb) +{ + // convert disconnect/threshold time to ms and divide by turn around time + // to get number of retries + pIrlapCb->N1 = pIrlapCb->ThresholdTime * 1000 / pIrlapCb->RemoteMaxTAT; + pIrlapCb->N2 = pIrlapCb->DisconnectTime * 1000 / pIrlapCb->RemoteMaxTAT; + + // hmmmm...??? + pIrlapCb->PollTimer.Timeout = pIrlapCb->RemoteMaxTAT; + pIrlapCb->FinalTimer.Timeout = pIrlapCb->LocalMaxTAT; + + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_RECONFIG_LINK; + IMsg.IRDA_MSG_Baud = pIrlapCb->Baud; + IMsg.IRDA_MSG_NumBOFs = pIrlapCb->RemoteNumBOFS; // Number of BOFS + // to add to tx + IMsg.IRDA_MSG_DataSize = pIrlapCb->RemoteDataSize; // Max rx size packet + // causes major heap + // problems later + IMsg.IRDA_MSG_MinTat = pIrlapCb->RemoteMinTAT; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Reconfig link for Baud:%d, Local data size:%d, Remote BOFS:%d"), pIrlapCb->Baud, pIrlapCb->LocalDataSize, pIrlapCb->RemoteNumBOFS)); + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Retry counts N1=%d, N2=%d"), pIrlapCb->N1, pIrlapCb->N2)); + return (IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); +} +/***************************************************************************** +* +* @func UINT | IrlapGetQosParmVal | +* retrieves the parameters value from table +* +* @rdesc value contained in parmeter value table, 0 if not found +* (0 is a valid parameter in some tables though) +* +* @parm UINT [] | PVTable | table containing parm values +* USHORT | BitField | contains bit indicating which parm to select +* +* @comm +*/ +UINT +IrlapGetQosParmVal(UINT PVTable[], UINT BitField, UINT *pBitSet) +{ + int i; + UINT Mask; + + for (i = PV_TABLE_MAX_BIT, Mask = (1< 0; i--, Mask = Mask >> 1) + { + if (Mask & BitField) + { + if (pBitSet != NULL) + { + *pBitSet = Mask; + } + return (PVTable[i]); + } + } + return (UINT) -1; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessTEST(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg, + IRLAP_UA_FORMAT *pTestFormat, + int CRBit, + int PFBit) +{ + BYTE TmpAddr[IRDA_DEV_ADDR_LEN]; + + if (!MyDevAddr(pIrlapCb, pTestFormat->DestAddr)) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring XID addressed to:%02X%02X%02X%02X"), + EXPAND_ADDR(pTestFormat->DestAddr))); + return SUCCESS; + } + + if (IRLAP_CMD == CRBit && IRLAP_PFBIT_SET == PFBit) + { + // bounce it back + memcpy(TmpAddr,pTestFormat->SrcAddr, IRDA_DEV_ADDR_LEN); + memcpy(pTestFormat->SrcAddr, pTestFormat->DestAddr, IRDA_DEV_ADDR_LEN); + memcpy(pTestFormat->DestAddr, TmpAddr, IRDA_DEV_ADDR_LEN); + *(pMsg->IRDA_MSG_pRead) ^= 1; // swap cr bit + return SendFrame(pIrlapCb, pMsg); + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring"))); + } + + // Not implementing TEST responses for now + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessUI(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg, + int CRBit, + int PFBit) +{ + BOOL LinkTurned = TRUE; + + pMsg->IRDA_MSG_pRead += 2; // chop the IRLAP header + + switch (pIrlapCb->State) + { + case NDM: + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + case DSCV_REPLY: + case CONN_MEDIA_SENSE: + case SNRM_SENT: + case BACKOFF_WAIT: + case SNRM_RECEIVED: + pMsg->Prim = IRLAP_UDATA_IND; + return (IrlmpUp(pIrlapCb->pIrdaLinkCb, pMsg)); + + case P_XMIT: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + return SUCCESS; + } + + if (PRIMARY == pIrlapCb->StationType) + { + // stop timers if PF bit set or invalid CRBit (matches mine) + if (IRLAP_PFBIT_SET == PFBit || pIrlapCb->CRBit == CRBit) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + } + + if (pIrlapCb->CRBit == CRBit) + { + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + // Send the Unnumber information to LMP + pMsg->Prim = IRLAP_UDATA_IND; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, pMsg)); + + if (IRLAP_PFBIT_SET == PFBit) + { + switch (pIrlapCb->State) + { + case P_RECV: + RetOnErr(XmitTxMsgList(pIrlapCb, FALSE, &LinkTurned)); + break; + + case P_DISCONNECT_PEND: + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case P_CLOSE: + RetOnErr(ResendDISC(pIrlapCb)); + break; + + case S_NRM: + RetOnErr(XmitTxMsgList(pIrlapCb, TRUE, NULL)); + break; + + case S_DISCONNECT_PEND: + RetOnErr(SendRD(pIrlapCb)); + pIrlapCb->State = S_CLOSE; + break; + + case S_ERROR: + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + pIrlapCb->State = S_NRM; + break; + + case S_CLOSE: + RetOnErr(SendRD(pIrlapCb)); + } + } + + if (PRIMARY == pIrlapCb->StationType) + { + if (IRLAP_PFBIT_SET == PFBit && pIrlapCb->State != NDM) + { + if (LinkTurned) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + else + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->PollTimer); + pIrlapCb->State = P_XMIT; + } + } + } + else + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessDM(PIRLAP_CB pIrlapCb) +{ + BOOL LinkTurned; + + switch (pIrlapCb->State) + { + case NDM: + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + case DSCV_REPLY: + case CONN_MEDIA_SENSE: + case BACKOFF_WAIT: + case SNRM_RECEIVED: + case P_XMIT: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + return TRUE; + } + + if (PRIMARY != pIrlapCb->StationType) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + + switch (pIrlapCb->State) + { + case P_RECV: // I'm not sure why I am doing this ??? + RetOnErr(XmitTxMsgList(pIrlapCb, FALSE, &LinkTurned)); + if (LinkTurned) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + else + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->PollTimer); + pIrlapCb->State = P_XMIT; + } + break; + + case P_DISCONNECT_PEND: + pIrlapCb->RetryCnt = 0; + RetOnErr(SendDISC(pIrlapCb)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case SNRM_SENT: + case P_CLOSE: + RetOnErr(ApplyDefaultParms(pIrlapCb)); + IMsg.Prim = IRLAP_DISCONNECT_IND; + if (pIrlapCb->State == P_CLOSE) + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_DISCONNECT_COMPLETED; + } + else + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_REMOTE_INITIATED; + } + if (pIrlapCb->LocalDiscReq || pIrlapCb->State == SNRM_SENT) + { + pIrlapCb->LocalDiscReq = FALSE; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + } + + if (pIrlapCb->State == P_CLOSE) + { + return GotoNDMThenDscvOrConn(pIrlapCb); + } + + pIrlapCb->State = NDM; + break; + } + + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessDISC(PIRLAP_CB pIrlapCb) +{ + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (SECONDARY != pIrlapCb->StationType) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + + // Acknowledge primary's disconnect request + RetOnErr(SendUA(pIrlapCb, FALSE /* No Qos */)); + RetOnErr(ApplyDefaultParms(pIrlapCb)); + + RetOnErr(ReturnTxMsgs(pIrlapCb)); + + // notify LMP of disconnect + IMsg.Prim = IRLAP_DISCONNECT_IND; + if (pIrlapCb->LocalDiscReq) + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_DISCONNECT_COMPLETED; + pIrlapCb->LocalDiscReq = FALSE; + } + else + { + IMsg.IRDA_MSG_DiscStatus = IRLAP_REMOTE_INITIATED; + } + + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + + pIrlapCb->State = NDM; + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessRD(PIRLAP_CB pIrlapCb) +{ + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (PRIMARY != pIrlapCb->StationType) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + + if (pIrlapCb->State == P_CLOSE) + { + RetOnErr(ResendDISC(pIrlapCb)); + } + else + { + RetOnErr(ReturnTxMsgs(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + RetOnErr(SendDISC(pIrlapCb)); + RetOnErr(GotoPCloseState(pIrlapCb)); + } + if (pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessFRMR(PIRLAP_CB pIrlapCb) +{ + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (PRIMARY != pIrlapCb->StationType) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + + switch (pIrlapCb->State) + { + case P_RECV: + RetOnErr(ReturnTxMsgs(pIrlapCb)); + // fall through + + case P_DISCONNECT_PEND: + pIrlapCb->RetryCnt = 0; + RetOnErr(SendDISC(pIrlapCb)); + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case P_CLOSE: + RetOnErr(ResendDISC(pIrlapCb)); + break; + } + + if (pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessRNRM(PIRLAP_CB pIrlapCb) +{ + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (PRIMARY != pIrlapCb->StationType) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + + switch (pIrlapCb->State) + { + case P_RECV: + case P_DISCONNECT_PEND: + pIrlapCb->RetryCnt = 0; + RetOnErr(SendDISC(pIrlapCb)); + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case P_CLOSE: + RetOnErr(ResendDISC(pIrlapCb)); + break; + } + + if (pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessREJ_SREJ(PIRLAP_CB pIrlapCb, + int FrameType, + PIRDA_MSG pMsg, + int CRBit, + int PFBit, + UINT Nr) +{ + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (PRIMARY == pIrlapCb->StationType) + { + // stop timers if PF bit set or invalid CRBit (matches mine) + if (IRLAP_PFBIT_SET == PFBit || pIrlapCb->CRBit == CRBit) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + } + + if (pIrlapCb->CRBit == CRBit) + { + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + switch (pIrlapCb->State) + { + case P_RECV: + case S_NRM: + if (IRLAP_PFBIT_SET == PFBit) + { + if (InvalidNr(pIrlapCb,Nr) || Nr == pIrlapCb->TxWin.End) + { + RetOnErr(ProcessInvalidNr(pIrlapCb, PFBit)); + } + else + { + RetOnErr(FreeAckedTxMsgs(pIrlapCb, Nr)); + if (FrameType == IRLAP_REJ) + { + RetOnErr(ResendRejects(pIrlapCb, Nr)); // link turned here + } + else // selective reject + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("RETRANSMISSION:"))); + RetOnErr(SendIFrame(pIrlapCb, + pIrlapCb->TxWin.pMsg[Nr], + Nr, IRLAP_PFBIT_SET)); + } + } + } + break; + + case P_DISCONNECT_PEND: + if (IRLAP_PFBIT_SET == PFBit) + { + pIrlapCb->RetryCnt = 0; + RetOnErr(SendDISC(pIrlapCb)); + RetOnErr(GotoPCloseState(pIrlapCb)); + } + break; + + case P_CLOSE: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(ResendDISC(pIrlapCb)); + } + break; + + case S_DISCONNECT_PEND: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendRD(pIrlapCb)); + pIrlapCb->State = S_CLOSE; + } + break; + + case S_ERROR: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + pIrlapCb->State = S_NRM; + } + break; + + case S_CLOSE: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendRD(pIrlapCb)); + } + break; + + } + if (PRIMARY == pIrlapCb->StationType) + { + if (IRLAP_PFBIT_SET == PFBit && pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessRR_RNR(PIRLAP_CB pIrlapCb, + int FrameType, + PIRDA_MSG pMsg, + int CRBit, + int PFBit, + UINT Nr) +{ + BOOL LinkTurned = TRUE; + + if (IgnoreState(pIrlapCb)) + { + return SUCCESS; + } + + if (PRIMARY == pIrlapCb->StationType) + { + // stop timers if PF bit set or invalid CRBit (matches mine) + if (IRLAP_PFBIT_SET == PFBit || pIrlapCb->CRBit == CRBit) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else // SECONDARY, restart WDog + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + if (pIrlapCb->CRBit != CRBit) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + } + + if (pIrlapCb->CRBit == CRBit) + { + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + if (FrameType == IRLAP_RR) + { + pIrlapCb->RemoteBusy = FALSE; + } + else // RNR + { + pIrlapCb->RemoteBusy = TRUE; + } + + switch (pIrlapCb->State) + { + case P_RECV: + case S_NRM: + if (PFBit == IRLAP_PFBIT_SET) + { + if (InvalidNr(pIrlapCb, Nr)) + { + RetOnErr(ProcessInvalidNr(pIrlapCb, PFBit)); + } + else + { + RetOnErr(FreeAckedTxMsgs(pIrlapCb,Nr)); + + if (Nr != pIrlapCb->Vs) // Implicit reject + { + if (PRIMARY == pIrlapCb->StationType && + IRLAP_RNR == FrameType) + { + LinkTurned = FALSE; + } + else + { + RetOnErr(ResendRejects(pIrlapCb, + Nr)); // always turns link + } + } + else + { + if (pIrlapCb->Vr != pIrlapCb->RxWin.End) + { + RetOnErr(MissingRxFrames(pIrlapCb)); // Send SREJ or REJ + } + else + { + if (PRIMARY == pIrlapCb->StationType) + { + LinkTurned = FALSE; + if (IRLAP_RR == FrameType) + { + RetOnErr(XmitTxMsgList(pIrlapCb, + FALSE, &LinkTurned)); + } + } + else + { + // Always turn link if secondary + // with data or an RR if remote is busy + if (IRLAP_RR == FrameType) + { + RetOnErr(XmitTxMsgList(pIrlapCb, TRUE, NULL)); + } + else + { + RetOnErr(SendRR_RNR(pIrlapCb)); + } + } + } + } + } + // If the link was turned, restart Final timer, + // else start the Poll timer and enter the transmit state + if (PRIMARY == pIrlapCb->StationType) + { + if (LinkTurned) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + else + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->PollTimer); + pIrlapCb->State = P_XMIT; + } + } + } + break; + + case P_DISCONNECT_PEND: + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(GotoPCloseState(pIrlapCb)); + break; + + case P_CLOSE: + RetOnErr(ResendDISC(pIrlapCb)); + if (pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + break; + + case S_DISCONNECT_PEND: + case S_CLOSE: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendRD(pIrlapCb)); + if (pIrlapCb->State != S_CLOSE) + pIrlapCb->State = S_CLOSE; + } + break; + + case S_ERROR: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + pIrlapCb->State = S_NRM; + } + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessInvalidNr(PIRLAP_CB pIrlapCb, + int PFBit) +{ + DEBUGMSG(DBG_ERROR, (TEXT("IRLAP: ERROR, Invalid Nr\r\n"))); + + RetOnErr(ReturnTxMsgs(pIrlapCb)); + + if (PRIMARY == pIrlapCb->StationType) + { + if (PFBit == IRLAP_PFBIT_SET) + { + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + // F-timer will be started by caller + RetOnErr(GotoPCloseState(pIrlapCb)); + } + else + { + pIrlapCb->State = P_DISCONNECT_PEND; + } + } + else // SECONDARY + { + if (PFBit == IRLAP_PFBIT_SET) + { + FrmRejFormat.Vs = pIrlapCb->Vs; + FrmRejFormat.Vr = pIrlapCb->Vr; + FrmRejFormat.W = 0; + FrmRejFormat.X = 0; + FrmRejFormat.Y = 0; + FrmRejFormat.Z = 1; // bad NR + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + } + } + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessIFrame(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg, + int CRBit, + int PFBit, + UINT Ns, + UINT Nr, + BOOL *pFreeMsg) +{ +#ifdef DEBUG + BYTE *p1, *p2; +#endif + + pMsg->IRDA_MSG_pRead += 2; // chop the IRLAP header + + switch (pIrlapCb->State) + { + case S_NRM: + case P_RECV: + // Stop Timers: if PFSet stop Final (I frame from secondary) + // Always stop WDog (I from primary) + if (PRIMARY == pIrlapCb->StationType) + { + if (PFBit == IRLAP_PFBIT_SET) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + } + + if (pIrlapCb->CRBit == CRBit) + { + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + return SUCCESS; + } + + if (InvalidNsOrNr(pIrlapCb, Ns, Nr)) + { +#ifdef DEBUG + p1 = pMsg->IRDA_MSG_pRead - 2; // Get header back + p2 = pMsg->IRDA_MSG_pWrite + 2; // and FCS + + while (p1 < p2) + DEBUGMSG(DBG_ERROR, (TEXT("%02X "), *p1++)); + DEBUGMSG(DBG_ERROR, (TEXT("\n"))); +#endif + +#ifdef TEMPERAMENTAL_SERIAL_DRIVER + if (pIrlapCb->RxWin.FCS[Ns] == pMsg->IRDA_MSG_FCS) + TossedDups++; + else + RetOnErr(ProcessInvalidNsOrNr(pIrlapCb, PFBit)); +#else + RetOnErr(ProcessInvalidNsOrNr(pIrlapCb, PFBit)); +#endif + } + else + { + if (PFBit == IRLAP_PFBIT_SET) + { + RetOnErr(InsertRxWinAndForward(pIrlapCb, + pMsg, Ns, pFreeMsg)); + + if (Nr != pIrlapCb->Vs) + { + RetOnErr(ResendRejects(pIrlapCb, Nr)); // always turns link + } + else // Nr == Vs, Good Nr + { + RetOnErr(FreeAckedTxMsgs(pIrlapCb, Nr)); + // Link will always be turned here + if (pIrlapCb->Vr != pIrlapCb->RxWin.End) + { + RetOnErr(MissingRxFrames(pIrlapCb)); + } + else + { + RetOnErr(XmitTxMsgList(pIrlapCb, TRUE, NULL)); + } + } + } + else // PF Bit not set + { + RetOnErr(InsertRxWinAndForward(pIrlapCb, + pMsg, Ns, pFreeMsg)); + RetOnErr(FreeAckedTxMsgs(pIrlapCb, Nr)); + } + } + // Start Timers: If PFBit set, link was turned so start final + // WDog is always stopped, so restart + if (PRIMARY == pIrlapCb->StationType) + { + if (PFBit == IRLAP_PFBIT_SET) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else // command from primary + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + break; + + default: + RetOnErr(IFrameOtherStates(pIrlapCb, CRBit, PFBit)); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* +* @ex +* example +*/ +BOOL +InvalidNsOrNr(PIRLAP_CB pIrlapCb, + UINT Ns, + UINT Nr) +{ + if (InvalidNr(pIrlapCb, Nr)) + { + return TRUE; + } + + // Valididate ns + if (!InWindow(pIrlapCb->Vr, + (pIrlapCb->RxWin.Start + pIrlapCb->LocalWinSize-1) % IRLAP_MOD, Ns) + || !InWindow(pIrlapCb->RxWin.Start, + (pIrlapCb->RxWin.Start + pIrlapCb->LocalWinSize-1) % IRLAP_MOD, Ns)) + { + DEBUGMSG(DBG_ERROR, + (TEXT("IRLAP: ERROR, Invalid Ns=%d! Vr=%d, RxStrt=%d Win=%d\r\n"), + Ns, pIrlapCb->Vr, pIrlapCb->RxWin.Start, pIrlapCb->LocalWinSize)); + IRLAP_LOG_ACTION((pIrlapCb, TEXT("** INVALID Ns **"))); + return TRUE; + } + return FALSE; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* +* @ex +* example +*/ +BOOL +InvalidNr(PIRLAP_CB pIrlapCb, + UINT Nr) +{ + if (!InWindow(pIrlapCb->TxWin.Start, pIrlapCb->Vs, Nr)) + { + DEBUGMSG(DBG_ERROR, + (TEXT("IRLAP: ERROR, Invalid Nr=%d! Vs=%d, TxStrt=%d\r\n"), + Nr, pIrlapCb->Vs, pIrlapCb->TxWin.Start)); + return TRUE; // Invalid Nr + } + return FALSE; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +BOOL +InWindow(UINT Start, UINT End, UINT i) +{ + if (Start <= End) + { + if (i >= Start && i <= End) + return TRUE; + } + else + { + if (i >= Start || i <= End) + return TRUE; + } + return FALSE; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ProcessInvalidNsOrNr(PIRLAP_CB pIrlapCb, + int PFBit) +{ + RetOnErr(ReturnTxMsgs(pIrlapCb)); + + if (PRIMARY == pIrlapCb->StationType) + { + if (PFBit == IRLAP_PFBIT_SET) + { + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + // F-timer will be started by caller + RetOnErr(GotoPCloseState(pIrlapCb)); + } + else + { + pIrlapCb->State = P_DISCONNECT_PEND; + } + } + else // SECONDARY + { + FrmRejFormat.Vs = pIrlapCb->Vs; + FrmRejFormat.Vr = pIrlapCb->Vr; + FrmRejFormat.W = 0; + FrmRejFormat.X = 0; + FrmRejFormat.Y = 0; + FrmRejFormat.Z = 1; // bad NR + if (PFBit == IRLAP_PFBIT_SET) + { + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + } + else + { + pIrlapCb->State = S_ERROR; + } + } + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +InsertRxWinAndForward(PIRLAP_CB pIrlapCb, + PIRDA_MSG pMsg, + UINT Ns, + BOOL *pFreeMsg) +{ + UINT rc = SUCCESS; + + // insert message into receive window + pIrlapCb->RxWin.pMsg[Ns] = pMsg; +#ifdef TEMPERAMENTAL_SERIAL_DRIVER + pIrlapCb->RxWin.FCS[Ns] = pMsg->IRDA_MSG_FCS; +#endif + + // Advance RxWin.End to Ns+1 if Ns is at or beyond RxWin.End + if (!InWindow(pIrlapCb->RxWin.Start, pIrlapCb->RxWin.End, Ns) || + Ns == pIrlapCb->RxWin.End) + { + pIrlapCb->RxWin.End = (Ns + 1) % IRLAP_MOD; + } + + // Forward in sequence frames starting from Vr + while (pIrlapCb->RxWin.pMsg[pIrlapCb->Vr] != NULL && !pIrlapCb->LocalBusy) + { + pIrlapCb->RxWin.pMsg[pIrlapCb->Vr]->Prim = IRLAP_DATA_IND; + + rc =IrlmpUp(pIrlapCb->pIrdaLinkCb, pIrlapCb->RxWin.pMsg[pIrlapCb->Vr]); + + if (rc == SUCCESS || rc == IRLMP_LOCAL_BUSY) + { + // Delivered successfully. Done with this message. Remove it from + // the RxWin and return message to rx free list. Update Vr + +/* !!! here it is again + RetOnErr(EnqueMsgList(&pIrlapCb->RxMsgFreeList, + pIrlapCb->RxWin.pMsg[pIrlapCb->Vr], + pIrlapCb->MaxRxMsgFreeListLen)); +*/ + pIrlapCb->RxWin.pMsg[pIrlapCb->Vr] = NULL; + pIrlapCb->Vr = (pIrlapCb->Vr + 1) % IRLAP_MOD; + + // LMP doesn't want anymore messages + if (rc == IRLMP_LOCAL_BUSY) + { + // The receive window will be cleaned out when RNR is sent + pIrlapCb->LocalBusy = TRUE; + } + } + else + { + return rc; + } + } + *pFreeMsg = FALSE; // we either already freed it or placed it in the window + // i.e. the caller should not free the message + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ResendRejects(PIRLAP_CB pIrlapCb, UINT Nr) +{ + if (!pIrlapCb->RemoteBusy) + { + // Set Vs back + + for (pIrlapCb->Vs = Nr;pIrlapCb->Vs != (pIrlapCb->TxWin.End-1)%IRLAP_MOD; + pIrlapCb->Vs = (pIrlapCb->Vs + 1) % IRLAP_MOD) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("RETRANSMISSION:"))); + RetOnErr(SendIFrame(pIrlapCb, + pIrlapCb->TxWin.pMsg[pIrlapCb->Vs], + pIrlapCb->Vs, + IRLAP_PFBIT_CLEAR)); + } + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("RETRANSMISSION:"))); + // Send last one with PFBit set + RetOnErr(SendIFrame(pIrlapCb, pIrlapCb->TxWin.pMsg[pIrlapCb->Vs], + pIrlapCb->Vs, IRLAP_PFBIT_SET)); + + pIrlapCb->Vs = (pIrlapCb->Vs + 1) % IRLAP_MOD; // Vs == TxWin.End + } + else + { + RetOnErr(SendRR_RNR(pIrlapCb)); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +FreeAckedTxMsgs(PIRLAP_CB pIrlapCb, + UINT Nr) +{ + UINT i = pIrlapCb->TxWin.Start; + + while (i != Nr) + { + if (pIrlapCb->TxWin.pMsg[i] != NULL) + { + pIrlapCb->TxWin.pMsg[i]->Prim = IRLAP_DATA_CONF; + pIrlapCb->TxWin.pMsg[i]->IRDA_MSG_DataStatus = + IRLAP_DATA_REQUEST_COMPLETED; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, pIrlapCb->TxWin.pMsg[i])); + + pIrlapCb->TxWin.pMsg[i] = NULL; + } + i = (i + 1) % IRLAP_MOD; + } + pIrlapCb->TxWin.Start = i; + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +MissingRxFrames(PIRLAP_CB pIrlapCb) +{ + int MissingFrameCnt = 0; + int MissingFrame = -1; + UINT i; + + i = pIrlapCb->Vr; + + // Count missing frame, determine first missing frame + + for (i = pIrlapCb->Vr; (i + 1) % IRLAP_MOD != pIrlapCb->RxWin.End; + i = (i+1) % IRLAP_MOD) + { + if (pIrlapCb->RxWin.pMsg[i] == NULL) + { + MissingFrameCnt++; + if (MissingFrame == -1) + { + MissingFrame = i; + } + } + } + + // if there are missing frames send SREJ (1) or RR (more than 1) + // and turn link around + if (MissingFrameCnt == 1 && !pIrlapCb->LocalBusy) + { + // we don't want to send the SREJ when local is busy because + // peer *MAY* interpret it as a clearing of the local busy condition + RetOnErr(SendSREJ(pIrlapCb, MissingFrame)); + } + else + { + // The RR/RNR will serve as an implicit REJ + RetOnErr(SendRR_RNR(pIrlapCb)); + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +IFrameOtherStates(PIRLAP_CB pIrlapCb, + int CRBit, + int PFBit) +{ + switch (pIrlapCb->State) + { + case NDM: + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + case DSCV_REPLY: + case CONN_MEDIA_SENSE: + case SNRM_SENT: + case BACKOFF_WAIT: + case SNRM_RECEIVED: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + return SUCCESS; + } + + if (pIrlapCb->CRBit == CRBit) // should be opposite of mine + { + if (pIrlapCb->StationType == PRIMARY) + { + if (pIrlapCb->State == P_XMIT) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->PollTimer); + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + } + RetOnErr(StationConflict(pIrlapCb)); + pIrlapCb->State = NDM; + + return SUCCESS; + } + + if (pIrlapCb->StationType == PRIMARY) // I'm PRIMARY, this is a + { // response from secondary + switch (pIrlapCb->State) + { + case P_DISCONNECT_PEND: + if (PFBit == IRLAP_PFBIT_CLEAR) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(GotoPCloseState(pIrlapCb)); + } + break; + + case P_CLOSE: + if (PFBit == IRLAP_PFBIT_CLEAR) + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->FinalTimer); + RetOnErr(ResendDISC(pIrlapCb)); + if (pIrlapCb->State != NDM) + { + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + } + break; + + case S_CLOSE: + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + } + else + { + switch (pIrlapCb->State) + { + case S_DISCONNECT_PEND: + if (IRLAP_PFBIT_SET == PFBit) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(SendRD(pIrlapCb)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + pIrlapCb->State = S_CLOSE; + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + } + break; + + case S_ERROR: + if (IRLAP_PFBIT_SET == PFBit) + { + RetOnErr(SendFRMR(pIrlapCb, &FrmRejFormat)); + pIrlapCb->State = S_NRM; + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + break; + + case S_CLOSE: + if (IRLAP_PFBIT_SET == PFBit) + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + RetOnErr(SendRD(pIrlapCb)); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + else + { + IRLAP_TimerStop(pIrlapCb, &pIrlapCb->WDogTimer); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignore in this state"))); + } + } + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | StationConflict | Sends disconnect due to receipt of +* by primary of frame with Poll +* +* @rdesc SUCCESS otherwise one of the following errors: +* @flag val | desc +* +* @comm +* comments +*/ +UINT +StationConflict(PIRLAP_CB pIrlapCb) +{ + InitializeState(pIrlapCb, PRIMARY); // Primary doesn't mean anything here + + RetOnErr(ApplyDefaultParms(pIrlapCb)); + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_PRIMARY_CONFLICT; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + + return SUCCESS; +} +/***************************************************************************** +* +* @func UINT | ApplyDefaultParms | Apply default parameters and +* reinitalize MAC +* +* @rdesc SUCCESS otherwise one of the following errors: +* @flag val | desc +* +*/ +UINT +ApplyDefaultParms(PIRLAP_CB pIrlapCb) +{ + pIrlapCb->Baud = IRLAP_DEFAULT_BAUD; + + pIrlapCb->RemoteMaxTAT = IRLAP_DEFAULT_MAX_TAT; + + pIrlapCb->RemoteDataSize = IRLAP_DEFAULT_DATA_SIZE; + + pIrlapCb->RemoteWinSize = IRLAP_DEFAULT_WIN_SIZE; + + pIrlapCb->RemoteNumBOFS = IRLAP_DEFAULT_BOFS; + + pIrlapCb->ConnAddr = IRLAP_BROADCAST_CONN_ADDR; + + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_RECONFIG_LINK; + IMsg.IRDA_MSG_Baud = IRLAP_DEFAULT_BAUD; + IMsg.IRDA_MSG_NumBOFs = IRLAP_DEFAULT_BOFS; + IMsg.IRDA_MSG_DataSize = IRLAP_DEFAULT_DATA_SIZE; + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ - reconfig link"))); + + return (IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg)); +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +ResendDISC(PIRLAP_CB pIrlapCb) +{ + if (pIrlapCb->RetryCnt >= pIrlapCb->N3) + { + RetOnErr(ApplyDefaultParms(pIrlapCb)); + pIrlapCb->RetryCnt = 0; + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + RetOnErr(IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg)); + pIrlapCb->State = NDM; + } + else + { + RetOnErr(SendDISC(pIrlapCb)); + pIrlapCb->RetryCnt++; + } + return SUCCESS; +} + +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +BOOL +IgnoreState(PIRLAP_CB pIrlapCb) +{ + switch (pIrlapCb->State) + { + case NDM: + case DSCV_MEDIA_SENSE: + case DSCV_QUERY: + case DSCV_REPLY: + case CONN_MEDIA_SENSE: + case SNRM_SENT: + case BACKOFF_WAIT: + case SNRM_RECEIVED: + case P_XMIT: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring in this state"))); + return TRUE; + } + return FALSE; +} + +VOID +QueryTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "Query timer expired")); + + if (pIrlapCb->State == DSCV_REPLY) + { + pIrlapCb->State = NDM; + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Ignoring QueryTimer Expriation in state %s"), + IRLAP_StateStr[pIrlapCb->State])); + } + + IRLAP_LOG_COMPLETE(pIrlapCb); + + return; +} + +VOID +SlotTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "Slot timer expired")); + + if (pIrlapCb->State == DSCV_QUERY) + { + pIrlapCb->SlotCnt++; + SendDscvXIDCmd(pIrlapCb); + if (pIrlapCb->SlotCnt < pIrlapCb->MaxSlot) + { + IMsg.Prim = MAC_CONTROL_REQ; + IMsg.IRDA_MSG_Op = MAC_MEDIA_SENSE; + IMsg.IRDA_MSG_SenseTime = IRLAP_DSCV_SENSE_TIME; + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_CONTROL_REQ (dscv sense)"))); + IrmacDown(pIrlapCb->pIrdaLinkCb,&IMsg); + } + else + { + pIrlapCb->GenNewAddr = FALSE; + + IMsg.Prim = IRLAP_DISCOVERY_CONF; + IMsg.IRDA_MSG_pDevList = &pIrlapCb->DevList; + IMsg.IRDA_MSG_DscvStatus = IRLAP_DISCOVERY_COMPLETED; + + // Change state now so IRLMP can do DISCOVERY_REQ on this thread + pIrlapCb->State = NDM; + + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + } + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring SlotTimer Expriation in state %s"), + IRLAP_StateStr[pIrlapCb->State])); + ; // maybe return bad state ??? + } + IRLAP_LOG_COMPLETE(pIrlapCb); + return; +} + +VOID +FinalTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "Final timer expired")); + + pIrlapCb->NoResponse = TRUE; + + switch (pIrlapCb->State) + { + case SNRM_SENT: + if (pIrlapCb->RetryCnt < pIrlapCb->N3) + { + pIrlapCb->BackoffTimer.Timeout = IRLAP_BACKOFF_TIME(); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->BackoffTimer); + pIrlapCb->State = BACKOFF_WAIT; + } + else + { + ApplyDefaultParms(pIrlapCb); + + pIrlapCb->RetryCnt = 0; + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + pIrlapCb->State = NDM; + } + break; + + case P_RECV: + if (pIrlapCb->RetryCnt == pIrlapCb->N2) + { + ReturnTxMsgs(pIrlapCb); + ApplyDefaultParms(pIrlapCb); + + pIrlapCb->RetryCnt = 0; // Don't have to, do it for logger + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + pIrlapCb->State = NDM; + } + else + { + pIrlapCb->RetryCnt++; + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + SendRR_RNR(pIrlapCb); + if (pIrlapCb->RetryCnt == pIrlapCb->N1) + { + IMsg.Prim = IRLAP_STATUS_IND; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + } + } + break; + + case P_DISCONNECT_PEND: + SendDISC(pIrlapCb); + pIrlapCb->RetryCnt = 0; + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + GotoPCloseState(pIrlapCb); + break; + + case P_CLOSE: + if (pIrlapCb->RetryCnt >= pIrlapCb->N3) + { + ApplyDefaultParms(pIrlapCb); + + pIrlapCb->RetryCnt = 0; // Don't have to, do it for logger + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + GotoNDMThenDscvOrConn(pIrlapCb); + } + else + { + pIrlapCb->RetryCnt++; + SendDISC(pIrlapCb); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + } + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring Final Expriation in state %s"), + IRLAP_StateStr[pIrlapCb->State])); + } + + IRLAP_LOG_COMPLETE(pIrlapCb); + return; +} + +VOID +PollTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "Poll timer expired")); + + if (pIrlapCb->State == P_XMIT) + { + SendRR_RNR(pIrlapCb); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->State = P_RECV; + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignoring Poll Expriation in state %s"), + IRLAP_StateStr[pIrlapCb->State])); + } + + IRLAP_LOG_COMPLETE(pIrlapCb); + return; +} + +VOID +BackoffTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "Backoff timer expired")); + + if (pIrlapCb->State == BACKOFF_WAIT) + { + SendSNRM(pIrlapCb, TRUE); + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->FinalTimer); + pIrlapCb->RetryCnt += 1; + pIrlapCb->State = SNRM_SENT; + } + else + { + IRLAP_LOG_ACTION((pIrlapCb, + TEXT("Ignoring BackoffTimer Expriation in this state "))); + } + IRLAP_LOG_COMPLETE(pIrlapCb); + return; +} + +VOID +WDogTimerExp(PVOID Context) +{ + PIRLAP_CB pIrlapCb = (PIRLAP_CB) Context; + + IRLAP_LOG_START((pIrlapCb, "WDog timer expired")); + + pIrlapCb->NoResponse = TRUE; + + switch (pIrlapCb->State) + { + case S_DISCONNECT_PEND: + case S_NRM: + pIrlapCb->WDogExpCnt++; + // Disconnect/threshold time is in seconds + if (pIrlapCb->WDogExpCnt * (int)pIrlapCb->WDogTimer.Timeout >= + pIrlapCb->DisconnectTime * 1000) + { + ReturnTxMsgs(pIrlapCb); + ApplyDefaultParms(pIrlapCb); + + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + pIrlapCb->State = NDM; + } + else + { + if ((pIrlapCb->WDogExpCnt * (int) pIrlapCb->WDogTimer.Timeout >= + pIrlapCb->ThresholdTime * 1000) && !pIrlapCb->StatusSent) + { + IMsg.Prim = IRLAP_STATUS_IND; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + pIrlapCb->StatusSent = TRUE; + } + IRLAP_TimerStart(pIrlapCb, &pIrlapCb->WDogTimer); + } + break; + + case S_CLOSE: + ApplyDefaultParms(pIrlapCb); + + IMsg.Prim = IRLAP_DISCONNECT_IND; + IMsg.IRDA_MSG_DiscStatus = IRLAP_NO_RESPONSE; + IrlmpUp(pIrlapCb->pIrdaLinkCb, &IMsg); + pIrlapCb->State = NDM; + break; + + default: + IRLAP_LOG_ACTION((pIrlapCb, TEXT("Ignore WDogTimer expiration in state %s"), + IRLAP_StateStr[pIrlapCb->State])); + } + IRLAP_LOG_COMPLETE(pIrlapCb); + return; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +UINT +DequeMsgList(IRDA_MSG_LIST *pList, IRDA_MSG **ppMsg) +{ + if (pList->Len != 0) + { + *ppMsg = (IRDA_MSG *) RemoveHeadList(&pList->ListHead); +/** + { + IRDA_MSG *pAMsg = pList->ListHead.Flink; + + printf(TEXT("\nDEQUE: %x\n"), *ppMsg); + + while (pAMsg != &(pList->ListHead)) + { + printf(TEXT("%x->"),pAMsg); + pAMsg = pAMsg->Linkage.Flink; + } + printf(TEXT("\n")); + } +**/ + pList->Len--; + return SUCCESS; + } + return IRLAP_MSG_LIST_EMPTY; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* + + +* @ex +* example +*/ +UINT +EnqueMsgList(IRDA_MSG_LIST *pList, IRDA_MSG *pMsg, int MaxLen) +{ + if (MaxLen == -1 || pList->Len < MaxLen) + { + InsertTailList(&pList->ListHead, &(pMsg->Linkage)); + pList->Len++; +/** + { + IRDA_MSG *pAMsg = pList->ListHead.Flink; + + printf(TEXT("\nENQUE: %x\n"), pMsg); + while (pAMsg != &(pList->ListHead)) + { + printf(TEXT("%x->"),pAMsg); + pAMsg = pAMsg->Linkage.Flink; + } + printf(TEXT("\n")); + } +**/ + return SUCCESS; + } + return IRLAP_MSG_LIST_FULL; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +void +InitMsgList(IRDA_MSG_LIST *pList) +{ + InitializeListHead(&pList->ListHead); + pList->Len = 0; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc return desc +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +* +* @ex +* example +*/ +/* !!! +void +IRLAP_PrintState() +{ +#ifdef DEBUG + DEBUGMSG(1, (TEXT("IRLAP State %s\n"), IRLAP_StateStr[pIrlapCb->State])); +#else + DEBUGMSG(1, (TEXT("IRLAP State %d\n"), pIrlapCb->State)); +#endif + DEBUGMSG(1, + (TEXT(" Vs=%d Vr=%d RxWin(%d,%d) TxWin(%d,%d) TxMsgListLen=%d RxMsgFreeListLen=%d\r\n"), + pIrlapCb->Vs, pIrlapCb->Vr, + pIrlapCb->RxWin.Start, pIrlapCb->RxWin.End, + pIrlapCb->TxWin.Start, pIrlapCb->TxWin.End, + pIrlapCb->TxMsgList.Len, pIrlapCb->RxMsgFreeList.Len)); + +#ifdef TEMPERAMENTAL_SERIAL_DRIVER + DEBUGMSG(1, (TEXT(" Tossed duplicates %d\n"), TossedDups)); +#endif + + IRMAC_PrintState(); + + return; +} +*/ +int +GetMyDevAddr(BOOL New) +{ +#ifdef PEG + HKEY hKey; + LONG hRes; + TCHAR KeyName[32]; +#endif + int DevAddr, NewDevAddr; + DWORD RegDevAddr = 0; + TCHAR ValName[] = TEXT("DevAddr"); + LARGE_INTEGER li; + + KeQueryTickCount(&li); + + NewDevAddr = (int) li.LowPart; + + // Get the device address from the registry. If the key exists and the + // value is 0, store a new random address. If no key, then return + // a random address. +#ifdef PEG + _tcscpy (KeyName, COMM_REG_KEY); + _tcscat (KeyName, TEXT("IrDA")); + + hRes = RegOpenKeyEx (HKEY_LOCAL_MACHINE, KeyName, 0, 0, &hKey); + + if (hRes == ERROR_SUCCESS && + GetRegDWORDValue(hKey, ValName, &RegDevAddr)) + { + if (RegDevAddr == 0) + { + RegDevAddr = KeQueryTickCount(); + SetRegDWORDValue(hKey, ValName, RegDevAddr); + } + RegCloseKey(hKey); + + DevAddr = (int) RegDevAddr; + } +#else + DevAddr = NewDevAddr; +#endif + return DevAddr; +} + diff --git a/private/ntos/tdi/irda/irlap/irlapio.c b/private/ntos/tdi/irda/irlap/irlapio.c new file mode 100644 index 000000000..0c4a8361d --- /dev/null +++ b/private/ntos/tdi/irda/irlap/irlapio.c @@ -0,0 +1,900 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlapio.c +* +* Description: IRLAP I/O routines +* +* Author: mbert +* +* Date: 4/25/95 +* +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +extern BYTE IRLAP_BroadcastDevAddr[]; + +// The largest MAC message is the XID Frame consisting of address, +// control, XID Format ID, XID format, + Discovery Information +#define _MAC_MSG_LEN 3+sizeof(IRLAP_XID_DSCV_FORMAT)+IRLAP_DSCV_INFO_LEN + +//static BYTE IRLAP_MAC_MsgData[_MAC_MSG_LEN]; +//static IRDA_MSG MAC_Message; +//static PIRDA_MSG pMACMsg = &MAC_Message; + +UINT +SendFrame(PIRLAP_CB, PIRDA_MSG ); + +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +UINT +ClearRxWindow(PIRLAP_CB pIrlapCb) +{ + UINT i, rc; + + // Remove everything from Rx window + for (i = pIrlapCb->Vr; i != pIrlapCb->RxWin.End; i = (i+1) % IRLAP_MOD) + { + if (pIrlapCb->RxWin.pMsg[i] != NULL) + { + /* !!! fix this OH SHIT + if((rc = EnqueMsgList(&pIrlapCb->RxMsgFreeList, + pIrlapCb->RxWin.pMsg[i], + pIrlapCb->MaxRxMsgFreeListLen)) != SUCCESS) + { + return rc; + } + */ + pIrlapCb->RxWin.pMsg[i] = NULL; + } + pIrlapCb->RxWin.End = pIrlapCb->Vr; + } + return SUCCESS; +} +/***************************************************************************** +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +UINT +SendDscvXIDCmd(PIRLAP_CB pIrlapCb) +{ + UINT rc = SUCCESS; + IRLAP_XID_DSCV_FORMAT XIDFormat; + CHAR *DscvInfo; + int DscvInfoLen; + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + memcpy(XIDFormat.SrcAddr, pIrlapCb->LocalDevice.DevAddr, IRDA_DEV_ADDR_LEN); + memcpy(XIDFormat.DestAddr, IRLAP_BroadcastDevAddr, IRDA_DEV_ADDR_LEN); + + XIDFormat.NoOfSlots = IRLAP_SLOT_FLAG(pIrlapCb->MaxSlot); + XIDFormat.GenNewAddr = pIrlapCb->GenNewAddr; + XIDFormat.Reserved = 0; + + if (pIrlapCb->SlotCnt == pIrlapCb->MaxSlot) + { + DscvInfo = pIrlapCb->LocalDevice.DscvInfo; + DscvInfoLen = pIrlapCb->LocalDevice.DscvInfoLen; + XIDFormat.SlotNo = IRLAP_END_DSCV_SLOT_NO; + } + else + { + DscvInfo = NULL; + DscvInfoLen = 0; + XIDFormat.SlotNo = pIrlapCb->SlotCnt; + } + XIDFormat.Version = pIrlapCb->LocalDevice.IRLAP_Version; + + pIMsg->IRDA_MSG_pWrite = Format_DscvXID(pIMsg, + IRLAP_BROADCAST_CONN_ADDR, + IRLAP_CMD, IRLAP_PFBIT_SET, + &XIDFormat, DscvInfo, + DscvInfoLen); + return SendFrame(pIrlapCb, pIMsg); +} +/****************************************************************************S* +* +* @func ret_type | func_name | funcdesc +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm data_type | parm_name | description +* +* @comm +* comments +*/ +UINT +SendDscvXIDRsp(PIRLAP_CB pIrlapCb) +{ + UINT rc = SUCCESS; + IRLAP_XID_DSCV_FORMAT XIDFormat; + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + XIDFormat.GenNewAddr = pIrlapCb->GenNewAddr; + if (pIrlapCb->GenNewAddr) + { + StoreULAddr(pIrlapCb->LocalDevice.DevAddr, GetMyDevAddr(TRUE)); + pIrlapCb->GenNewAddr = FALSE; + } + memcpy(XIDFormat.SrcAddr, pIrlapCb->LocalDevice.DevAddr, IRDA_DEV_ADDR_LEN); + memcpy(XIDFormat.DestAddr, pIrlapCb->RemoteDevice.DevAddr, IRDA_DEV_ADDR_LEN); + XIDFormat.NoOfSlots = IRLAP_SLOT_FLAG(pIrlapCb->RemoteMaxSlot); + XIDFormat.Reserved = 0; + XIDFormat.SlotNo = pIrlapCb->RespSlot; + XIDFormat.Version = pIrlapCb->LocalDevice.IRLAP_Version; + + pIMsg->IRDA_MSG_pWrite = Format_DscvXID(pIMsg, + IRLAP_BROADCAST_CONN_ADDR, + IRLAP_RSP, IRLAP_PFBIT_SET, + &XIDFormat, + pIrlapCb->LocalDevice.DscvInfo, + pIrlapCb->LocalDevice.DscvInfoLen); + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendSNRM | formats a SNRM frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm BYTE | ConnAddr | Connection address +* +* @comm +* The ConnAddr can be different than that in the control block. +* For reset, its the same, but set to broadcast for initial +* connection. +*/ +UINT +SendSNRM(PIRLAP_CB pIrlapCb, BOOL SendQos) +{ + IRDA_QOS_PARMS *pQos = NULL; + int ConnAddr = pIrlapCb->ConnAddr; + IRDA_MSG *pIMsg; + + if (SendQos) + { + ConnAddr = IRLAP_BROADCAST_CONN_ADDR; + pQos = &pIrlapCb->LocalQos; + } + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_SNRM(pIMsg, ConnAddr, + IRLAP_CMD, + IRLAP_PFBIT_SET, + pIrlapCb->LocalDevice.DevAddr, + pIrlapCb->RemoteDevice.DevAddr, + pIrlapCb->ConnAddr, + pQos); + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendUA | formats a UA frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm BOOL | SendQos | Send the Qos +* +* @comm +* comments +*/ +UINT +SendUA(PIRLAP_CB pIrlapCb, BOOL SendQos) +{ + IRDA_QOS_PARMS NegQos; + IRDA_QOS_PARMS *pNegQos = NULL; + BYTE *pSrcAddr = NULL; + BYTE *pDestAddr = NULL; + IRDA_MSG *pIMsg; + + if (SendQos) + { + // Put all parms (type 0 and 1) in NegQos + memcpy(&NegQos, &pIrlapCb->LocalQos, sizeof(IRDA_QOS_PARMS)); + // Overwrite type 0 parameters that have already been negotiated + NegQos.bfBaud = pIrlapCb->NegotiatedQos.bfBaud; + NegQos.bfDisconnectTime = pIrlapCb->NegotiatedQos.bfDisconnectTime; + pNegQos = &NegQos; + } + + // This will be moved into the "if" above when the spec is clarified + pSrcAddr = pIrlapCb->LocalDevice.DevAddr; + pDestAddr = pIrlapCb->RemoteDevice.DevAddr; + //------------------------------------------------------------------ + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_UA(pIMsg, + pIrlapCb->ConnAddr, + IRLAP_RSP, + IRLAP_PFBIT_SET, + pSrcAddr, pDestAddr, pNegQos); + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendDM | formats a DM frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendDM(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_DM(pIMsg, + pIrlapCb->ConnAddr, + IRLAP_RSP, + IRLAP_PFBIT_SET); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendRD | formats a RD frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendRD(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_RD(pIMsg, + pIrlapCb->ConnAddr, + IRLAP_RSP, + IRLAP_PFBIT_SET); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendRR | formats a RR frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendRR(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + ClearRxWindow(pIrlapCb); + + pIrlapCb->RxWin.Start = pIrlapCb->Vr; // RxWin.Start = what we've acked + + pIMsg->IRDA_MSG_pWrite = Format_RR(pIMsg, pIrlapCb->ConnAddr, + pIrlapCb->CRBit, IRLAP_PFBIT_SET, + pIrlapCb->Vr); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendRNR | formats a RNR frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendRR_RNR(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + BYTE *(*pFormatRR_RNR)(); + + if (pIrlapCb->LocalBusy) + { + pFormatRR_RNR = Format_RNR; + } + else + { + pFormatRR_RNR = Format_RR; + } + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + ClearRxWindow(pIrlapCb); + + + pIrlapCb->RxWin.Start = pIrlapCb->Vr; // RxWin.Start = what we've acked + + + pIMsg->IRDA_MSG_pWrite = (*pFormatRR_RNR)(pIMsg, pIrlapCb->ConnAddr, + pIrlapCb->CRBit, IRLAP_PFBIT_SET, + pIrlapCb->Vr); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendDISC | formats a DISC frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendDISC(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_DISC(pIMsg, pIrlapCb->ConnAddr, + IRLAP_CMD, IRLAP_PFBIT_SET); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendRNRM | formats a RNRM frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendRNRM(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_RNRM(pIMsg, pIrlapCb->ConnAddr, + IRLAP_RSP, IRLAP_PFBIT_SET); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendREJ | formats a REJ frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* +* @comm +* comments +*/ +UINT +SendREJ(PIRLAP_CB pIrlapCb) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + ClearRxWindow(pIrlapCb); + + pIrlapCb->RxWin.Start = pIrlapCb->Vr; // RxWin.Start = what we've acked + + pIMsg->IRDA_MSG_pWrite = Format_REJ(pIMsg, pIrlapCb->ConnAddr, + pIrlapCb->CRBit, IRLAP_PFBIT_SET, + pIrlapCb->Vr); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendSREJ | formats a SREJ frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm int | Nr | Nr to be placed in SREJ frame +* +* @comm +* comments +*/ +UINT +SendSREJ(PIRLAP_CB pIrlapCb, int Nr) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_SREJ(pIMsg, pIrlapCb->ConnAddr, + pIrlapCb->CRBit, IRLAP_PFBIT_SET, Nr); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendFRMR | formats a FRMR frame and sends it +* +* @rdesc SUCCESS, otherwise one of the following errors: +* @flag val | desc +* +* @parm int | Nr | Nr to be placed in SREJ frame +* +* @comm +* comments +*/ +UINT +SendFRMR(PIRLAP_CB pIrlapCb, IRLAP_FRMR_FORMAT *pFRMRFormat) +{ + IRDA_MSG *pIMsg; + + pIMsg = AllocMacIMsg(pIrlapCb->pIrdaLinkCb); + + pIMsg->IRDA_MSG_pWrite = Format_FRMR(pIMsg, pIrlapCb->ConnAddr, + pIrlapCb->CRBit, IRLAP_PFBIT_SET, + pFRMRFormat); + + return SendFrame(pIrlapCb, pIMsg); +} +/***************************************************************************** +* +* @func UINT | SendIFrame | Builds and sends an I frame to MAC +* +* @rdesc SUCCESS otherwise one of the following errors: +* @flag val | desc +* +* @parm | | +* +* @comm +* comments +*/ +UINT +SendIFrame(PIRLAP_CB pIrlapCb, PIRDA_MSG pMsg, int Ns, int PFBit) +{ + UINT rc; + + if (NULL == pMsg) + { + return IRLAP_NULL_MSG; + } + + ClearRxWindow(pIrlapCb); + + pIrlapCb->RxWin.Start = pIrlapCb->Vr; // RxWin.Start = what we've acked + + (void) Format_I(pMsg, pIrlapCb->ConnAddr, pIrlapCb->CRBit, PFBit, + pIrlapCb->Vr, Ns); + + rc = SendFrame(pIrlapCb, pMsg); + + pMsg->IRDA_MSG_pHdrRead +=2; // uglyness.. chop header in case frame + // requires retransmission + return rc; +} +/***************************************************************************** +* +* @func UINT | SendUIFrame | Builds and sends an UI frame to MAC +* +* @rdesc SUCCESS otherwise one of the following errors: +* @flag val | desc +* +* @parm | | +* +* @comm +* comments +*/ +UINT +SendUIFrame(PIRLAP_CB pIrlapCb, PIRDA_MSG pMsg) +{ + if (NULL == pMsg) + { + return IRLAP_NULL_MSG; + } + (void) Format_UI(pMsg, pIrlapCb->ConnAddr, pIrlapCb->CRBit,IRLAP_PFBIT_SET); + + return SendFrame(pIrlapCb, pMsg); +} +/***************************************************************************** +* +* @func UINT | SendFrame | Builds and sends an Unnumbered frame to MAC +* +* @rdesc SUCCESS otherwise one of the following errors: +* @flag val | desc +* +* @comm +* comments +*/ +UINT +SendFrame(PIRLAP_CB pIrlapCb, PIRDA_MSG pMsg) +{ + UINT rc = SUCCESS; + + pMsg->Prim = MAC_DATA_REQ; + + rc = IrmacDown(pIrlapCb->pIrdaLinkCb, pMsg); + + IRLAP_LOG_ACTION((pIrlapCb, TEXT("MAC_DATA_REQ: %s"), FrameToStr(pMsg))); + + return rc; +} +/***************************************************************************** +* +* @func UINT | _IRLMP_Up | Adds logging to the IRLMP_Up +* +* @rdesc returns of IRLMP_Up +* +* @parm PIRDA_MSG | pMsg | pointer to IRDA message +* +* @comm +* comments +*/ +/* +UINT +_IRLMP_Up(PIRDA_MSG pMsg) +{ + IRLAP_LOG_ACTION((TEXT("%s%s"), IRDA_PrimStr[pMsg->Prim], + pMsg->Prim == IRLAP_DISCOVERY_CONF ? + IRDA_StatStr[pMsg->IRDA_MSG_DscvStatus] : + pMsg->Prim == IRLAP_CONNECT_CONF ? + IRDA_StatStr[pMsg->IRDA_MSG_ConnStatus] : + pMsg->Prim == IRLAP_DISCONNECT_IND ? + IRDA_StatStr[pMsg->IRDA_MSG_DiscStatus] : + pMsg->Prim == IRLAP_DATA_CONF || pMsg->Prim == IRLAP_UDATA_CONF ? + IRDA_StatStr[pMsg->IRDA_MSG_DataStatus] : TEXT(""))); + + return (IRLMP_Up(pMsg)); +} +*/ + +BYTE * +BuildTuple(BYTE *pBuf, BYTE Pi, UINT BitField) +{ + *pBuf++ = Pi; + + if (BitField > 0xFF) + { + *pBuf++ = 2; // Pl + *pBuf++ = (BYTE) (BitField >> 8); + *pBuf++ = (BYTE) (BitField); + } + else + { + *pBuf++ = 1; // Pl + *pBuf++ = (BYTE) (BitField); + } + return pBuf; +} + +BYTE * +BuildNegParms(BYTE *pBuf, IRDA_QOS_PARMS *pQos) +{ + pBuf = BuildTuple(pBuf, QOS_PI_BAUD, pQos->bfBaud); + pBuf = BuildTuple(pBuf, QOS_PI_MAX_TAT, pQos->bfMaxTurnTime); + pBuf = BuildTuple(pBuf, QOS_PI_DATA_SZ, pQos->bfDataSize); + pBuf = BuildTuple(pBuf, QOS_PI_WIN_SZ, pQos->bfWindowSize); + pBuf = BuildTuple(pBuf, QOS_PI_BOFS, pQos->bfBofs); + pBuf = BuildTuple(pBuf, QOS_PI_MIN_TAT, pQos->bfMinTurnTime); + pBuf = BuildTuple(pBuf, QOS_PI_DISC_THRESH, pQos->bfDisconnectTime); + + return pBuf; +} + +void +StoreULAddr(BYTE Addr[], ULONG ULAddr) +{ + Addr[0] = (BYTE) ( 0xFF & ULAddr); + Addr[1] = (BYTE) ((0xFF00 & ULAddr) >> 8); + Addr[2] = (BYTE) ((0xFF0000 & ULAddr) >> 16); + Addr[3] = (BYTE) ((0xFF000000 & ULAddr) >> 24); +} + +BYTE * +_PutAddr(BYTE *pBuf, BYTE Addr[]) +{ + *pBuf++ = Addr[0]; + *pBuf++ = Addr[1]; + *pBuf++ = Addr[2]; + *pBuf++ = Addr[3]; + + return (pBuf); +} + +void +BuildUHdr(IRDA_MSG *pMsg, int FrameType, int Addr, int CRBit, int PFBit) +{ + if (pMsg->IRDA_MSG_pHdrRead != NULL) + { + pMsg->IRDA_MSG_pHdrRead -= 2; + + ASSERT(pMsg->IRDA_MSG_pHdrRead >= pMsg->IRDA_MSG_Header); + + *(pMsg->IRDA_MSG_pHdrRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pHdrRead+1) = (BYTE) _MAKE_UCNTL(FrameType, PFBit); + } + else + { + pMsg->IRDA_MSG_pRead -= 2; + *(pMsg->IRDA_MSG_pRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pRead+1) = (BYTE) _MAKE_UCNTL(FrameType, PFBit); + } + return; +} + +void +BuildSHdr(IRDA_MSG *pMsg, int FrameType, int Addr, int CRBit, int PFBit, int Nr) +{ + if (pMsg->IRDA_MSG_pHdrRead != NULL) + { + pMsg->IRDA_MSG_pHdrRead -= 2; + + ASSERT(pMsg->IRDA_MSG_pHdrRead >= pMsg->IRDA_MSG_Header); + + *(pMsg->IRDA_MSG_pHdrRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pHdrRead+1) = (BYTE) _MAKE_SCNTL(FrameType, PFBit, Nr); + } + else + { + pMsg->IRDA_MSG_pRead -= 2; + *(pMsg->IRDA_MSG_pRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pRead+1) = (BYTE) _MAKE_SCNTL(FrameType, PFBit, Nr); + } + return; +} + +BYTE * +Format_SNRM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, BYTE SAddr[], + BYTE DAddr[], int CAddr, IRDA_QOS_PARMS *pQos) +{ + BuildUHdr(pMsg, IRLAP_SNRM, Addr, CRBit, PFBit); + + if (pQos != NULL) + { + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, SAddr); + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, DAddr); + *pMsg->IRDA_MSG_pWrite++ = CAddr << 1; // Thats what the f'n spec says + pMsg->IRDA_MSG_pWrite = BuildNegParms(pMsg->IRDA_MSG_pWrite, pQos); + } + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_DISC(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit) +{ + BuildUHdr(pMsg, IRLAP_DISC, Addr, CRBit, PFBit); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_UI(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit) +{ + BuildUHdr(pMsg, IRLAP_UI, Addr, CRBit, PFBit); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_DscvXID(IRDA_MSG *pMsg, int ConnAddr, int CRBit, int PFBit, + IRLAP_XID_DSCV_FORMAT *pXIDFormat, + CHAR DscvInfo[], int DscvInfoLen) +{ + if (pMsg->IRDA_MSG_pHdrRead != NULL) + { + pMsg->IRDA_MSG_pHdrRead -= 2; + + ASSERT(pMsg->IRDA_MSG_pHdrRead >= pMsg->IRDA_MSG_Header); + + *(pMsg->IRDA_MSG_pHdrRead) = (BYTE) _MAKE_ADDR(ConnAddr, CRBit); + if (CRBit) + *(pMsg->IRDA_MSG_pHdrRead+1)= + (BYTE) _MAKE_UCNTL(IRLAP_XID_CMD, PFBit); + else + *(pMsg->IRDA_MSG_pHdrRead+1)= + (BYTE) _MAKE_UCNTL(IRLAP_XID_RSP, PFBit); + } + else + { + pMsg->IRDA_MSG_pRead -= 2; + *(pMsg->IRDA_MSG_pRead) = (BYTE) _MAKE_ADDR(ConnAddr, CRBit); + if (CRBit) + *(pMsg->IRDA_MSG_pRead+1)= + (BYTE) _MAKE_UCNTL(IRLAP_XID_CMD, PFBit); + else + *(pMsg->IRDA_MSG_pRead+1)= + (BYTE) _MAKE_UCNTL(IRLAP_XID_RSP, PFBit); + } + + *pMsg->IRDA_MSG_pWrite++ = IRLAP_XID_DSCV_FORMAT_ID; + + memcpy(pMsg->IRDA_MSG_pWrite, (CHAR *) pXIDFormat, + sizeof(IRLAP_XID_DSCV_FORMAT) - 1); // Subtract for FirstDscvByte + // in structure + pMsg->IRDA_MSG_pWrite += sizeof(IRLAP_XID_DSCV_FORMAT) - 1; + + if (DscvInfo != NULL) + { + memcpy(pMsg->IRDA_MSG_pWrite, DscvInfo, DscvInfoLen); + pMsg->IRDA_MSG_pWrite += DscvInfoLen; + } + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_TEST(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + BYTE SAddr[], BYTE DAddr[]) +{ + BuildUHdr(pMsg, IRLAP_TEST, Addr, CRBit, PFBit); + + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, SAddr); + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, DAddr); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_RNRM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit) +{ + BuildUHdr(pMsg, IRLAP_RNRM, Addr, CRBit, PFBit); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_UA(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, BYTE SAddr[], + BYTE DAddr[], IRDA_QOS_PARMS *pQos) +{ + BuildUHdr(pMsg, IRLAP_UA, Addr, CRBit, PFBit); + + if (SAddr != NULL) + { + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, SAddr); + } + if (DAddr != NULL) + { + pMsg->IRDA_MSG_pWrite = _PutAddr(pMsg->IRDA_MSG_pWrite, DAddr); + } + + if (pQos != NULL) + pMsg->IRDA_MSG_pWrite = BuildNegParms(pMsg->IRDA_MSG_pWrite, pQos); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_FRMR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + IRLAP_FRMR_FORMAT *pFormat) +{ + BuildUHdr(pMsg, IRLAP_FRMR, Addr, CRBit, PFBit); + + memcpy(pMsg->IRDA_MSG_pWrite, (CHAR *)pFormat,sizeof(IRLAP_FRMR_FORMAT)); + pMsg->IRDA_MSG_pWrite += sizeof(IRLAP_FRMR_FORMAT); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_DM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit) +{ + BuildUHdr(pMsg, IRLAP_DM, Addr, CRBit, PFBit); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_RD(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit) +{ + BuildUHdr(pMsg, IRLAP_RD, Addr, CRBit, PFBit); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_RR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr) +{ + BuildSHdr(pMsg, IRLAP_RR, Addr, CRBit, PFBit, Nr); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_RNR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr) +{ + BuildSHdr(pMsg, IRLAP_RNR, Addr, CRBit, PFBit, Nr); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_REJ(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr) +{ + BuildSHdr(pMsg, IRLAP_REJ, Addr, CRBit, PFBit, Nr); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_SREJ(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr) +{ + BuildSHdr(pMsg, IRLAP_SREJ, Addr, CRBit, PFBit, Nr); + + return (pMsg->IRDA_MSG_pWrite); +} + +BYTE * +Format_I(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr, int Ns) +{ + if (pMsg->IRDA_MSG_pHdrRead != NULL) + { + pMsg->IRDA_MSG_pHdrRead -= 2; + + ASSERT(pMsg->IRDA_MSG_pHdrRead >= pMsg->IRDA_MSG_Header); + + *(pMsg->IRDA_MSG_pHdrRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pHdrRead+1) = (BYTE) (((Ns & 7) << 1) + + ((PFBit & 1)<< 4) + (Nr <<5)); + } + else + { + pMsg->IRDA_MSG_pRead -= 2; + *(pMsg->IRDA_MSG_pRead) = (BYTE) _MAKE_ADDR(Addr, CRBit); + *(pMsg->IRDA_MSG_pRead+1) = (BYTE) (((Ns & 7) << 1) + + ((PFBit & 1)<< 4) + (Nr <<5)); + } + return (pMsg->IRDA_MSG_pWrite); +} + + +// TEMP +UINT IrlmpUp(PVOID IrlmpContext, PIRDA_MSG pIMsg) +{ + return 0; +} diff --git a/private/ntos/tdi/irda/irlap/irlapio.h b/private/ntos/tdi/irda/irlap/irlapio.h new file mode 100644 index 000000000..f65d390eb --- /dev/null +++ b/private/ntos/tdi/irda/irlap/irlapio.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlapio.h +* +* Description: prototypes for IRLAP I/O routines +* +* Author: mbert +* +* Date: 4/25/95 +* +*/ +void SetMsgPointers(PIRLAP_CB, PIRDA_MSG); +UINT SendDscvXIDCmd(PIRLAP_CB); +UINT SendDscvXIDRsp(PIRLAP_CB); +UINT SendSNRM(PIRLAP_CB, BOOL); +UINT SendUA(PIRLAP_CB, BOOL); +UINT SendDM(PIRLAP_CB); +UINT SendRD(PIRLAP_CB); +UINT SendRR(PIRLAP_CB); +UINT SendRR_RNR(PIRLAP_CB); +UINT SendDISC(PIRLAP_CB); +UINT SendRNRM(PIRLAP_CB); +UINT SendIFrame(PIRLAP_CB, PIRDA_MSG, int, int); +UINT SendSREJ(PIRLAP_CB, int); +UINT SendREJ(PIRLAP_CB); +UINT SendFRMR(PIRLAP_CB, IRLAP_FRMR_FORMAT *); +UINT SendUIFrame(PIRLAP_CB, PIRDA_MSG); +UINT SendFrame(PIRLAP_CB, PIRDA_MSG); +UINT _IRLMP_Up(PIRDA_MSG); + diff --git a/private/ntos/tdi/irda/irlap/irlaplog.c b/private/ntos/tdi/irda/irlap/irlaplog.c new file mode 100644 index 000000000..ae95fa8c0 --- /dev/null +++ b/private/ntos/tdi/irda/irlap/irlaplog.c @@ -0,0 +1,265 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlaplog.c +* +* Description: IRLAP state machine logging and errors +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _IRLAPLOG_C_ + +#ifdef DEBUG + + +TCHAR *DecodeIRDA(int *pFrameType,// returned frame type (-1=bad frame) + BYTE *pFrameBuf, // pointer to buffer containing IRLAP frame + UINT FrameLen, // length of buffer + TCHAR *pOutStr, // string where decoded packet is placed + UINT DecodeLayer,// 1,LAP only, 2 LAP/LMP, 3, LAP/LMP/TTP + BOOL fNoConnAddr,// TRUE->Don't show connection address in str + int DispMode ) +{ + *pOutStr = TEXT('\0'); + + return pOutStr; +} +TCHAR _ABuf[512]; + +#ifndef PEG +TCHAR _DecodeStr1[1000]; +TCHAR _DecodeStr2[1000]; +#else +TCHAR _DecodeStr1[] = TEXT("(null)"); +TCHAR _DecodeStr2[] = TEXT("(null)"); +#endif + +int _FrameType; + +int ActCnt[10]; + +int vNestedEvent = 0; + +// Keep in sync with IRLAP_STATE in irlap.h +TCHAR *IRLAP_StateStr[] = { + TEXT("NDM"), + TEXT("DSCV_MEDIA_SENSE"), + TEXT("DSCV_QUERY"), + TEXT("DSCV_REPLY"), + TEXT("CONN_MEDIA_SENSE"), + TEXT("SNRM_SENT"), + TEXT("BACKOFF_WAIT"), + TEXT("SNRM_RECEIVED"), + TEXT("P_XMIT"), + TEXT("P_RECV"), + TEXT("P_DISCONNECT_PEND"), + TEXT("P_CLOSE"), + TEXT("S_NRM"), + TEXT("S_DISCONNECT_PEND"), + TEXT("S_ERROR"), + TEXT("S_CLOSE") +}; + +// Keep in sync with IRDA_SERVICE_PRIM in irda.h +TCHAR *IRDA_PrimStr[] = +{ + TEXT("MAC_DATA_REQ"), + TEXT("MAC_DATA_IND"), + TEXT("MAC_CONTROL_REQ"), + TEXT("MAC_CONTROL_CONF"), + TEXT("IRLAP_DISCOVERY_REQ"), + TEXT("IRLAP_DISCOVERY_IND"), + TEXT("IRLAP_DISCOVERY_CONF"), + TEXT("IRLAP_CONNECT_REQ"), + TEXT("IRLAP_CONNECT_IND"), + TEXT("IRLAP_CONNECT_RESP"), + TEXT("IRLAP_CONNECT_CONF"), + TEXT("IRLAP_DISCONNECT_REQ"), + TEXT("IRLAP_DISCONNECT_IND"), + TEXT("IRLAP_DATA_REQ"), + TEXT("IRLAP_DATA_IND"), + TEXT("IRLAP_DATA_CONF"), + TEXT("IRLAP_UDATA_REQ"), + TEXT("IRLAP_UDATA_IND"), + TEXT("IRLAP_UDATA_CONF"), + TEXT("IRLAP_STATUS_IND"), + TEXT("IRLAP_FLOWON_REQ"), + TEXT("IRLAP_FLOWON_IND") + TEXT("IRLMP_DISCOVERY_REQ"), + TEXT("IRLMP_DISCOVERY_CONF"), + TEXT("IRLMP_CONNECT_REQ"), + TEXT("IRLMP_CONNECT_IND"), + TEXT("IRLMP_CONNECT_RESP"), + TEXT("IRLMP_CONNECT_CONF"), + TEXT("IRLMP_DISCONNECT_REQ"), + TEXT("IRLMP_DISCONNECT_IND"), + TEXT("IRLMP_DATA_REQ"), + TEXT("IRLMP_DATA_IND"), + TEXT("IRLMP_DATA_CONF"), + TEXT("IRLMP_UDATA_REQ"), + TEXT("IRLMP_UDATA_IND"), + TEXT("IRLMP_UDATA_CONF"), + TEXT("IRLMP_ACCESSMODE_REQ"), + TEXT("IRLMP_ACCESSMODE_IND"), + TEXT("IRLMP_ACCESSMODE_CONF"), + TEXT("IRLMP_FLOWON_REQ"), + TEXT("IRLMP_FLOWON_IND") +}; + +// keep in sync with IRDA_ServiceStatus in irda.h +TCHAR *IRDA_StatStr[] = +{ + TEXT(" - MEDIA_BUSY"), + TEXT(" - MEDIA_CLEAR"), + TEXT(" - DISCOVERY_COLLISION"), + TEXT(" - REMOTE_DISCOVERY_IN_PROGRESS"), + TEXT(" - REMOTE_CONNECT_IN_PROGRSS"), + TEXT(" - DISCOVERY_COMPLETED"), + TEXT(" - REMOTE_CONNECTION_IN_PROGRESS"), + TEXT(" - CONNECTION_COMPLETED"), + TEXT(" - REMOTE_INITIATED"), + TEXT(" - PRIMARY_CONFLICT"), + TEXT(" - DISCONNECT_COMPLETE"), + TEXT(" - NO_RESPONSE"), + TEXT(" - IRLAP_DECLINE_RESET"), + TEXT(" - DATA_REQUEST_COMPLETED"), + TEXT(" - DATA_REQUEST_FAILED_LINK_RESET"), + TEXT(" - DATA_REQUEST_FAILED_REMOTE_BUSY") +}; + +// Keep in sync with MAC_CONTROL_OPERATION in irda.h +TCHAR *MAC_OpStr[] = +{ + TEXT("initialize link"), + TEXT("shutdown link"), + TEXT("reconfig link"), + TEXT("media sense") +}; + +TCHAR +*FrameToStr(IRDA_MSG *pMsg) +{ +#ifndef PEG + BYTE *ptr; + int i = 0; + int j; + TCHAR *pD1 = _DecodeStr1; + TCHAR *pD2 = _DecodeStr2; + + // copy the frame to a contiguous buffer + + ptr = pMsg->IRDA_MSG_pHdrRead; + while (ptr != pMsg->IRDA_MSG_pHdrWrite) + { + _ABuf[i++] = *ptr++; + } + ptr = pMsg->IRDA_MSG_pRead; + while (ptr != pMsg->IRDA_MSG_pWrite) + { + _ABuf[i++] = *ptr++; + } + + DecodeIRDA(&_FrameType, (char *)_ABuf, i, _DecodeStr1, 2, FALSE, 1); + + // insert spaces and break-up into multiple lines + i = 0; + do + { + if (i++%69 == 0) + { + *pD2++ = TEXT('\r'); + *pD2++ = TEXT('\n'); + for (j = 0; j<7;j++) + { + *pD2++ = TEXT(' '); + } + } + *pD2++ = *pD1++; + } while (*pD1 != TEXT('\0')); + + *pD2 = TEXT('\0'); +#endif + return (_DecodeStr2); +} +void +IRLAP_EventLogStart(PIRLAP_CB pIrlapCb, TCHAR *pFormat, ...) +{ + va_list ArgList; + + va_start (ArgList, pFormat); + + if (++vNestedEvent == 1) + { + DEBUGMSG(DBG_IRLAPLOG, (TEXT("----------------\r\n"))); + } + else + { + DEBUGMSG(DBG_IRLAPLOG, (TEXT("!!!!!!!!!!!!!!!!\r\n"))); + } + + DEBUGMSG(DBG_IRLAPLOG, (TEXT("Ev%d: "), vNestedEvent)); + + ActCnt[vNestedEvent] = 0; + + vsprintf(_ABuf, pFormat, ArgList); + DEBUGMSG(DBG_IRLAPLOG, (_ABuf)); + + DEBUGMSG(DBG_IRLAPLOG, (TEXT("\r\nStart State: %s\r\nActions:\r\n"), + IRLAP_StateStr[pIrlapCb->State])); + + va_end (ArgList); +} + +void __cdecl +IRLAP_LogAction(PIRLAP_CB pIrlapCb, TCHAR *pFormat, ...) +{ + va_list ArgList; + + va_start (ArgList, pFormat); + + DEBUGMSG(DBG_IRLAPLOG, (TEXT(" %d. "), ++ActCnt[vNestedEvent])); + + vsprintf(_ABuf, pFormat, ArgList); + DEBUGMSG(DBG_IRLAPLOG, (_ABuf)); + + DEBUGMSG(DBG_IRLAPLOG, (TEXT("\n"))); + + va_end (ArgList); +} + +#define PRINT_IF_TRUE(bool, str) (bool == TRUE ? str : TEXT("")) + +void +IRLAP_EventLogComplete(PIRLAP_CB pIrlapCb) +{ + DEBUGMSG(DBG_IRLAPLOG, + (TEXT("Vs=%d Vr=%d RxWin(%d,%d) TxWin(%d,%d) TxListLen=%d\r\n"), + pIrlapCb->Vs, pIrlapCb->Vr, + pIrlapCb->RxWin.Start, pIrlapCb->RxWin.End, + pIrlapCb->TxWin.Start, pIrlapCb->TxWin.End, + pIrlapCb->TxMsgList.Len)); + + DEBUGMSG(DBG_IRLAPLOG, (TEXT("Ev%d End St: %s\r\n"), + vNestedEvent--, IRLAP_StateStr[pIrlapCb->State])); + + if (vNestedEvent > 0) + { + DEBUGMSG(DBG_IRLAPLOG, (TEXT("!!!!!!!!!!!!!!!!\r\n"))); + } +} + +#endif diff --git a/private/ntos/tdi/irda/irlap/irlapp.h b/private/ntos/tdi/irda/irlap/irlapp.h new file mode 100644 index 000000000..bd2498263 --- /dev/null +++ b/private/ntos/tdi/irda/irlap/irlapp.h @@ -0,0 +1,299 @@ +/***************************************************************************** +* +* Copyright (c) 1995 Microsoft Corporation +* +* File: irlap.h +* +* Description: IRLAP Protocol and control block definitions +* +* Author: mbert +* +* Date: 4/15/95 +* +*/ + +#define IRLAP_MEDIA_SENSE_TIME 500 +#define IRLAP_SLOT_TIMEOUT 50 +#define IRLAP_DSCV_SENSE_TIME 30 + +// XID Format +#define IRLAP_XID_DSCV_FORMAT_ID 0x01 +#define IRLAP_XID_NEGPARMS_FORMAT_ID 0x02 +typedef struct +{ + BYTE SrcAddr[IRDA_DEV_ADDR_LEN]; + BYTE DestAddr[IRDA_DEV_ADDR_LEN]; + BYTE NoOfSlots:2; + BYTE GenNewAddr:1; + BYTE Reserved:5; + BYTE SlotNo; + BYTE Version; + BYTE FirstDscvInfoByte; +} IRLAP_XID_DSCV_FORMAT; + +// Frame Reject Format +typedef struct +{ + BYTE CntlField; + BYTE Fill1:1; + BYTE Vs:3; + BYTE CRBit:1; + BYTE Vr:3; + BYTE W:1; + BYTE X:1; + BYTE Y:1; + BYTE Z:1; + BYTE Fill2:4; +} IRLAP_FRMR_FORMAT; + +// SNRM Frame Format +typedef struct +{ + BYTE SrcAddr[IRDA_DEV_ADDR_LEN]; + BYTE DestAddr[IRDA_DEV_ADDR_LEN]; + BYTE ConnAddr; // actually shifted for CRBit -> STUPID !! + BYTE FirstQosByte; +} IRLAP_SNRM_FORMAT; + +// UA Frame Format +typedef struct +{ + BYTE SrcAddr[IRDA_DEV_ADDR_LEN]; + BYTE DestAddr[IRDA_DEV_ADDR_LEN]; + BYTE FirstQosByte; +} IRLAP_UA_FORMAT; + +#define IRLAP_MAX_TX_MSG_LIST_LEN 8 +#define IRLAP_MAX_DATA_SIZE 4096 +#define IRLAP_MAX_SLOTS 16 +#define IRLAP_DEFAULT_BAUD 9600 +#define IRLAP_DEFAULT_DATA_SIZE 64 +#define IRLAP_DEFAULT_MAX_TAT 500 +#define IRLAP_DEFAULT_BOFS 11 +#define IRLAP_DEFAULT_WIN_SIZE 1 + +// Macros for extracting various fields +#define IRLAP_GET_ADDR(addr) (addr >> 1) +#define IRLAP_GET_CRBIT(addr) (addr & 1) +#define IRLAP_GET_PFBIT(cntl) ((cntl >>4) & 1) +#define IRLAP_GET_UCNTL(cntl) (cntl & 0xEF) +#define IRLAP_GET_SCNTL(cntl) (cntl & 0x0F) +#define IRLAP_FRAME_TYPE(cntl) (cntl & 0x01 ? (cntl & 3) : 0) +#define IRLAP_GET_NR(cntl) ((cntl & 0xE0) >> 5) +#define IRLAP_GET_NS(cntl) ((cntl & 0xE) >> 1) + +// IRLAP constants +#define IRLAP_BROADCAST_CONN_ADDR 0x7F +#define IRLAP_END_DSCV_SLOT_NO 0xFF +#define IRLAP_CMD 1 +#define IRLAP_RSP 0 +#define IRLAP_PFBIT_SET 1 +#define IRLAP_PFBIT_CLEAR 0 +#define IRLAP_GEN_NEW_ADDR 1 +#define IRLAP_NO_NEW_ADDR 0 + +// Macro for creating Number of Slots of Discovery Flags Field of XID Format +// if S(Slots) <= 1 return 0, <= 6 return 1, <= 8 return 2, else return 3 +#define IRLAP_SLOT_FLAG(S) (S <= 1 ? 0 : (S <= 6 ? 1 : (S <= 8 ? 2 : 3))) + +int _inline IRLAP_RAND(int Min, int Max) +{ + LARGE_INTEGER li; + + KeQueryTickCount(&li); + + return ((li.LowPart % (Max+1-Min)) + Min); +} + +// Backoff time is a random time between 0.5 and 1.5 times the time to +// send a SNRM. _SNRM_TIME() is actually half (1000ms/2) time to send +// SNRM_LEN of characters at 9600 (9600/10 bits per char). +#define _SNRM_LEN 32 +#define _SNRM_TIME() (_SNRM_LEN*500/960) +#define IRLAP_BACKOFF_TIME() IRLAP_RAND(_SNRM_TIME(), 3*_SNRM_TIME()) + +#define QOS_PI_BAUD 0x01 +#define QOS_PI_MAX_TAT 0x82 +#define QOS_PI_DATA_SZ 0x83 +#define QOS_PI_WIN_SZ 0x84 +#define QOS_PI_BOFS 0x85 +#define QOS_PI_MIN_TAT 0x86 +#define QOS_PI_DISC_THRESH 0x08 + + +#define IRLAP_I_FRAME 0x00 +#define IRLAP_S_FRAME 0x01 +#define IRLAP_U_FRAME 0x03 + +// Unnumbered Frame types with P/F bit set to 0 +#define IRLAP_UI 0x03 +#define IRLAP_XID_CMD 0x2f +#define IRLAP_TEST 0xe3 +#define IRLAP_SNRM 0x83 +#define IRLAP_RNRM 0x83 +#define IRLAP_DISC 0x43 +#define IRLAP_RD 0x43 +#define IRLAP_UA 0x63 +#define IRLAP_FRMR 0x87 +#define IRLAP_DM 0x0f +#define IRLAP_XID_RSP 0xaf + +// Supervisory Frames +#define IRLAP_RR 0x01 +#define IRLAP_RNR 0x05 +#define IRLAP_REJ 0x09 +#define IRLAP_SREJ 0x0d + +#define _MAKE_ADDR(Addr, CRBit) ((Addr << 1) + (CRBit & 1)) +#define _MAKE_UCNTL(Cntl, PFBit) (Cntl + ((PFBit & 1)<< 4)) +#define _MAKE_SCNTL(Cntl, PFBit, Nr) (Cntl + ((PFBit & 1)<< 4) + (Nr <<5)) + +#define IRLAP_CB_SIG 0x7f2a364bUL + +// IRLAP Control Block +typedef struct +{ + IRDA_MSG *pMsg[IRLAP_MOD]; + UINT Start; + UINT End; +#ifdef TEMPERAMENTAL_SERIAL_DRIVER + int FCS[IRLAP_MOD]; +#endif +} IRLAP_WINDOW; + +typedef enum +{ + PRIMARY, + SECONDARY +} IRLAP_STN_TYPE; + +typedef enum // KEEP IN SYNC with IRLAP_StateStr in irlaplog.c +{ + NDM, // Normal Disconnect Mode + DSCV_MEDIA_SENSE, // Discovery Media Sense (Before Discovery) + DSCV_QUERY, // Discovery Query (Discovery initiated) + DSCV_REPLY, // Discovery Reply (Received DSCV XID cmd from Remote) + CONN_MEDIA_SENSE, // Connect Media Sense (Before connection estab) + SNRM_SENT, // SNRM sent - waiting for UA or DM from Remote + BACKOFF_WAIT, // Waiting random backoff before sending next SNRM + SNRM_RECEIVED, // SNRM rcvd - waiting for response from upper layer + P_XMIT, // Primary transmit + P_RECV, // Primary receive + P_DISCONNECT_PEND, // Upper layer request disconnect while in P_RECV + P_CLOSE, // Sent DISC, waiting for response + S_NRM, // Secondary Normal Response Mode XMIT/RECV + S_DISCONNECT_PEND, // Upper layer request disconnect while in S_NRM + S_ERROR, // Waiting for PF bit then send a FRMR + S_CLOSE, // Requested disconnect (RD) waiting for DISC command +} IRLAP_STATE; + +typedef struct IrlapControlBlock +{ + IRLAP_STATE State; + IRDA_DEVICE LocalDevice; + IRDA_DEVICE RemoteDevice; + PIRDA_LINK_CB pIrdaLinkCb; + IRDA_QOS_PARMS LocalQos; // QOS from LMP + IRDA_QOS_PARMS RemoteQos; // QOS of remote taken from SNRM/UA + IRDA_QOS_PARMS NegotiatedQos; // Union of remote and local QOS + int Baud; // Type 0 negotiation parm + int DisconnectTime;// Type 0 negotiation parm + int ThresholdTime; // Type 0 negotiotion parm + int LocalMaxTAT; // Type 1 negotiation parm + int LocalDataSize; // Type 1 negotiation parm + int LocalWinSize; // Type 1 negotiation parm + int LocalNumBOFS; // Type 1 negotiation parm + int RemoteMaxTAT; // Type 1 negotiation parm + int RemoteDataSize;// Type 1 negotiation parm + int RemoteWinSize; // Type 1 negotiation parm + int RemoteNumBOFS; // Type 1 negotiation parm + int RemoteMinTAT; // Type 1 negotiation parm + IRLAP_STN_TYPE StationType; // PRIMARY or SECONDARY + int ConnAddr; // Connection Address + int SNRMConnAddr; // Connection address contained in SNRM + // save it until get CONNECT_RESP + int CRBit; // Primary = 1, Secondary = 0 + int RespSlot; // Secondary. Slot to respond in + int SlotCnt; // Primary. Current slot number + int MaxSlot; // Maximum slots to send in Dscv + int RemoteMaxSlot; // Number of Dscv's remote will send + LIST_ENTRY DevList; // Discovered device list + UINT Vs; // send state variable + UINT Vr; // receive state variable + IRLAP_WINDOW RxWin; // Holds out of sequence rxd frames + IRLAP_WINDOW TxWin; // Holds unacked txd frames + IRDA_MSG_LIST TxMsgList; // DATA_REQ, UDATA_REQ queued here + int RetryCnt; // Count of number of retrans of DSCV,SNRM + int N1; // const# retries before sending status up + int N2; // const# retries before disconnecting + int N3; // const# of connection retries + IRDA_TIMER SlotTimer; + IRDA_TIMER QueryTimer; + IRDA_TIMER PollTimer; + IRDA_TIMER FinalTimer; + IRDA_TIMER WDogTimer; + IRDA_TIMER BackoffTimer; + int WDogExpCnt; // Count of WDog expirations + int StatusSent; // Status ind has been sent + CRITICAL_SECTION CS; // Control block synchronization + BOOL GenNewAddr; // Flag indicating whether to set new addr + BOOL DscvRespSent; // Secondary. Sent XID Discv response + BOOL RemoteBusy; // Remote has sent a RNR + BOOL LocalBusy; // Local busy condition, we sent RNR + BOOL ClrLocalBusy; // Send RR + BOOL LocalDiscReq; // why 2ndary got DISC + BOOL ConnAfterClose;// Conn req while in p_close + BOOL DscvAfterClose;// Dscv_req while in p_close + BOOL NoResponse; // Final/WD timer exp'd, used with RetryCnt + UINT Sig; // Signature +} IRLAP_CB, *PIRLAP_CB; + +#define LINE_CAPACITY(icb) (icb->RemoteWinSize * \ + (icb->RemoteDataSize + \ + 6+icb->RemoteNumBOFS)) + +BYTE *BuildNegParms(BYTE *pBuf, IRDA_QOS_PARMS *pQos); + +void StoreULAddr(BYTE Addr[], ULONG ULAddr); + +BYTE *Format_SNRM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + BYTE SAddr[], BYTE DAddr[], int CAddr, + IRDA_QOS_PARMS *pQos); + +BYTE *Format_DISC(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit); + +BYTE *Format_UI(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit); + +BYTE *Format_DscvXID(IRDA_MSG *pMsg, int ConnAddr, int CRBit, int PFBit, + IRLAP_XID_DSCV_FORMAT *pXidFormat, CHAR DscvInfo[], + int Len); + +BYTE *Format_TEST(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + BYTE SAddr[], BYTE DAddr[]); + +BYTE *Format_RNRM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit); + +BYTE *Format_UA(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + BYTE SAddr[], BYTE DAddr[], IRDA_QOS_PARMS *pQos); + +BYTE *Format_FRMR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, + IRLAP_FRMR_FORMAT *pFormat); + +BYTE *Format_DM(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit); + +BYTE *Format_RD(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit); + +BYTE *Format_RR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr); + +BYTE *Format_RNR(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr); + +BYTE *Format_REJ(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr); + +BYTE *Format_SREJ(IRDA_MSG *pMsg, int Addr, int CRBit, int PFBit, int Nr); + +BYTE * Format_I(IRDA_MSG *pMsg, int Addr, int CRBit, + int PFBit, int Nr, int Ns); + +int GetMyDevAddr(BOOL New); + + diff --git a/private/ntos/tdi/irda/irlap/makefile b/private/ntos/tdi/irda/irlap/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/irda/irlap/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/irda/irlap/sources b/private/ntos/tdi/irda/irlap/sources new file mode 100644 index 000000000..0f1ba5b0c --- /dev/null +++ b/private/ntos/tdi/irda/irlap/sources @@ -0,0 +1,23 @@ +MAJORCOMP=ntos +MINORCOMP=irda + +NTPROFILEINPUT=yes + +TARGETNAME=irlap +TARGETTYPE=LIBRARY +TARGETPATH=..\lib +TARGETLIBS= + + +INCLUDES=..\inc;..\..\..\inc;..\..\..\..\inc; + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER -DSECFLTR + +MSC_WARNING_LEVEL=/W3 /WX + + +SOURCES= irlap.c \ + irlaplog.c \ + irlapio.c + + diff --git a/private/ntos/tdi/isn/dirs b/private/ntos/tdi/isn/dirs new file mode 100644 index 000000000..8c61c4b61 --- /dev/null +++ b/private/ntos/tdi/isn/dirs @@ -0,0 +1,34 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + ipx \ + spx \ + nb \ + fwd \ + rip \ + flt \ + sockhelp \ + ipxroute +# isnext + +OPTIONAL_DIRS= diff --git a/private/ntos/tdi/isn/flt/debug.c b/private/ntos/tdi/isn/flt/debug.c new file mode 100644 index 000000000..35ee3565f --- /dev/null +++ b/private/ntos/tdi/isn/flt/debug.c @@ -0,0 +1,5 @@ +#include "precomp.h" + +#if DBG +ULONG DbgLevel = DEF_DBG_LEVEL; +#endif diff --git a/private/ntos/tdi/isn/flt/debug.h b/private/ntos/tdi/isn/flt/debug.h new file mode 100644 index 000000000..c69bf51a9 --- /dev/null +++ b/private/ntos/tdi/isn/flt/debug.h @@ -0,0 +1,42 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: debug.h +// +// Description: Debug macros definitions +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _IPXFLT_DEBUG_ +#define _IPXFLT_DEBUG_ + +#if DBG +extern ULONG DbgLevel; +#define DBG_IOCTLS 0x00000001 +#define DBG_FWDIF 0x00000002 +#define DBG_IFHASH 0x00000010 +#define DBG_PKTCACHE 0x00000020 +#define DBG_PKTLOGS 0x00000100 +#define DBG_ERRORS 0x10000000 + +#define DEF_DBG_LEVEL (DBG_IOCTLS|DBG_FWDIF|DBG_ERRORS|DBG_IFHASH) + +#define IpxFltDbgPrint(LEVEL,ARGS) \ + do { \ + if (DbgLevel & (LEVEL)) { \ + DbgPrint ARGS; \ + } \ + } while (0) + +#else +#define IpxFltDbgPrint(LEVEL,ARGS) do {NOTHING;} while (0) +#endif + +#endif diff --git a/private/ntos/tdi/isn/flt/dirs b/private/ntos/tdi/isn/flt/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/flt/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/flt/driver.c b/private/ntos/tdi/isn/flt/driver.c new file mode 100644 index 000000000..b4b8dde9b --- /dev/null +++ b/private/ntos/tdi/isn/flt/driver.c @@ -0,0 +1,443 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\flt\driver.c + +Abstract: + IPX Filter driver dispatch routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + +PFILE_OBJECT RouterFile; + +NTSTATUS +IpxFltDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +IpxFltUnload( + IN PDRIVER_OBJECT DriverObject + ); + +VOID +IpxFltCancel ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP irp + ); + +/*++ + D r i v e r E n t r y + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path + to driver-specific key in the registry + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) { + + PDEVICE_OBJECT deviceObject = NULL; + NTSTATUS status; + UNICODE_STRING deviceNameUnicodeString; + + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: Driver Entry.\n")); + + RtlInitUnicodeString (&deviceNameUnicodeString, + IPXFLT_NAME); + + status = IoCreateDevice (DriverObject, + 0, + &deviceNameUnicodeString, + FILE_DEVICE_IPXFLT, + 0, + FALSE, // Non-Exclusive + &deviceObject + ); + + if (NT_SUCCESS(status)) { + // + // Create dispatch points for device control, create, close. + // + DriverObject->MajorFunction[IRP_MJ_CREATE] + = DriverObject->MajorFunction[IRP_MJ_CLEANUP] + = DriverObject->MajorFunction[IRP_MJ_CLOSE] + = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] + = IpxFltDispatch; + DriverObject->DriverUnload = IpxFltUnload; + status = BindToFwdDriver (KernelMode); + if (NT_SUCCESS (status)) { + RouterFile = NULL; + return STATUS_SUCCESS; + } + else { + IoDeleteDevice (DriverObject->DeviceObject); + } + } + else + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Could not create device object.\n")); + + return status; +} + + + +/*++ + +Routine Description: + + Process the IRPs sent to this device. + +Arguments: + + DeviceObject - pointer to a device object + + Irp - pointer to an I/O Request Packet + +Return Value: + + +--*/ +NTSTATUS +IpxFltDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) { + PIO_STACK_LOCATION IrpStack; + PVOID inBuffer, outBuffer; + ULONG inpBufLength; + ULONG outBufLength; + NTSTATUS status; + KIRQL cancelIRQL; + + + Irp->IoStatus.Information = 0; + status = STATUS_SUCCESS; + + // + // Get a pointer to the current location in the Irp. This is where + // the function codes and parameters are located. + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + + switch (IrpStack->MajorFunction) { + case IRP_MJ_CREATE: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IRP_MJ_CREATE.\n")); + break; + + case IRP_MJ_CLOSE: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IRP_MJ_CLOSE.\n")); + if (IrpStack->FileObject == RouterFile) { + DeleteTables (); + RouterFile = NULL; + } + break; + + case IRP_MJ_CLEANUP: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IRP_MJ_CLEANUP.\n")); + if (IrpStack->FileObject==RouterFile) { + IoAcquireCancelSpinLock (&cancelIRQL); + while (!IsListEmpty (&LogIrpQueue)) { + PIRP irp = CONTAINING_RECORD (LogIrpQueue.Blink, + IRP, Tail.Overlay.ListEntry); + irp->Cancel = TRUE; + irp->CancelIrql = cancelIRQL; + irp->CancelRoutine = NULL; + IpxFltCancel(DeviceObject, irp); + IoAcquireCancelSpinLock (&cancelIRQL); + } + IoReleaseCancelSpinLock(cancelIRQL); + } + break; + + case IRP_MJ_DEVICE_CONTROL: + // + // Get the pointer to the input/output buffer and it's length + // + status = STATUS_INVALID_PARAMETER; + inpBufLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + outBufLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + switch (IrpStack->Parameters.DeviceIoControl.IoControlCode&3) { + case METHOD_BUFFERED: + inBuffer = outBuffer = Irp->AssociatedIrp.SystemBuffer; + break; + + case METHOD_IN_DIRECT: + case METHOD_OUT_DIRECT: + inBuffer = Irp->AssociatedIrp.SystemBuffer; + if (outBufLength>0) { + outBuffer = MmGetSystemAddressForMdl (Irp->MdlAddress); + } + else { + outBuffer = NULL; + } + break; + default: + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, ("IpxFlt: Unsupported io method.\n")); + goto DispatchExit; + } + + + if (IrpStack->FileObject==RouterFile) { + switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_FLT_IF_SET_IN_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_SET_IN_FILTERS.\n")); + if ((inpBufLength==sizeof (FLT_IF_SET_PARAMS)) + && (((PFLT_IF_SET_PARAMS)inBuffer)->FilterSize + ==sizeof (IPX_TRAFFIC_FILTER_INFO))) + status = SetInFilters ( + ((PFLT_IF_SET_PARAMS)inBuffer)->InterfaceIndex, + ((PFLT_IF_SET_PARAMS)inBuffer)->FilterAction, + outBufLength, + (PIPX_TRAFFIC_FILTER_INFO)outBuffer); + + break; + + case IOCTL_FLT_IF_SET_OUT_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_SET_OUT_FILTERS.\n")); + if ((inpBufLength==sizeof (FLT_IF_SET_PARAMS)) + && (((PFLT_IF_SET_PARAMS)inBuffer)->FilterSize + ==sizeof (IPX_TRAFFIC_FILTER_INFO))) + status = SetOutFilters ( + ((PFLT_IF_SET_PARAMS)inBuffer)->InterfaceIndex, + ((PFLT_IF_SET_PARAMS)inBuffer)->FilterAction, + outBufLength, + (PIPX_TRAFFIC_FILTER_INFO)outBuffer); + + break; + case IOCTL_FLT_IF_RESET_IN_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_RESET_IN_FILTERS.\n")); + if ((inpBufLength==sizeof (ULONG)) + && (outBufLength==0)) + status = SetInFilters ( + *((PULONG)inBuffer), + IPX_TRAFFIC_FILTER_ACTION_DENY, + 0, NULL); + + break; + case IOCTL_FLT_IF_RESET_OUT_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_RESET_OUT_FILTERS.\n")); + if ((inpBufLength==sizeof (ULONG)) + && (outBufLength==0)) + status = SetOutFilters ( + *((PULONG)inBuffer), + IPX_TRAFFIC_FILTER_ACTION_DENY, + 0, NULL); + + break; + case IOCTL_FLT_IF_GET_IN_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_GET_IN_FILTERS.\n")); + if ((inpBufLength==sizeof (ULONG)) + && (outBufLength>=sizeof (FLT_IF_GET_PARAMS))) { + Irp->IoStatus.Information + = outBufLength-sizeof (FLT_IF_GET_PARAMS); + status = GetInFilters ( + *((PULONG)inBuffer), + &((PFLT_IF_GET_PARAMS)outBuffer)->FilterAction, + &((PFLT_IF_GET_PARAMS)outBuffer)->TotalSize, + (PIPX_TRAFFIC_FILTER_INFO) + ((PUCHAR)outBuffer+sizeof (FLT_IF_GET_PARAMS)), + &Irp->IoStatus.Information); + if (NT_SUCCESS (status)) { + Irp->IoStatus.Information += sizeof (FLT_IF_GET_PARAMS); + ((PFLT_IF_GET_PARAMS)outBuffer)->FilterSize + = sizeof (IPX_TRAFFIC_FILTER_INFO); + } + else + Irp->IoStatus.Information = 0; + } + break; + case IOCTL_FLT_IF_GET_OUT_FILTERS: + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_IF_GET_OUT_FILTERS.\n")); + if ((inpBufLength==sizeof (ULONG)) + && (outBufLength>=sizeof (FLT_IF_GET_PARAMS))) { + Irp->IoStatus.Information + = outBufLength-sizeof (FLT_IF_GET_PARAMS); + status = GetOutFilters ( + *((PULONG)inBuffer), + &((PFLT_IF_GET_PARAMS)outBuffer)->FilterAction, + &((PFLT_IF_GET_PARAMS)outBuffer)->TotalSize, + (PIPX_TRAFFIC_FILTER_INFO) + ((PUCHAR)outBuffer+sizeof (FLT_IF_GET_PARAMS)), + &Irp->IoStatus.Information); + if (NT_SUCCESS (status)) { + Irp->IoStatus.Information += sizeof (FLT_IF_GET_PARAMS); + ((PFLT_IF_GET_PARAMS)outBuffer)->FilterSize + = sizeof (IPX_TRAFFIC_FILTER_INFO); + } + else + Irp->IoStatus.Information = 0; + } + + break; + case IOCTL_FLT_GET_LOGGED_PACKETS: + IpxFltDbgPrint (DBG_PKTLOGS, ("IpxFlt: IOCTL_FLT_GET_LOGGED_PACKETS.\n")); + Irp->IoStatus.Status = status = STATUS_PENDING; + IoMarkIrpPending (Irp); + IoAcquireCancelSpinLock (&cancelIRQL); + InsertTailList (&LogIrpQueue, + &Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine (Irp, IpxFltCancel); + if (LogIrpQueue.Flink!=&Irp->Tail.Overlay.ListEntry) { + PIRP irp = CONTAINING_RECORD ( + LogIrpQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + if (irp->IoStatus.Information>0) { + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine (irp, NULL); + irp->IoStatus.Status = STATUS_SUCCESS; + IoReleaseCancelSpinLock (cancelIRQL); + IpxFltDbgPrint (DBG_PKTLOGS, + ("IpxFlt: completing logging request" + " with %d bytes of data.\n", + irp->IoStatus.Information)); + IoCompleteRequest (irp, IO_NO_INCREMENT); + break; + } + } + IoReleaseCancelSpinLock (cancelIRQL); + break; + default: + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Unsupported IOCTL %lx.\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode)); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + else if (RouterFile==NULL) { + if (IrpStack->Parameters.DeviceIoControl.IoControlCode + ==IOCTL_FLT_START) { + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: IOCTL_FLT_START.\n")); + status = InitializeTables (); + if (NT_SUCCESS (status)) { + RouterFile = IrpStack->FileObject; + status = STATUS_SUCCESS; + } + } + else { + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Unsupported IOCTL %lx (driver is not started yet)).\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode)); + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + else { + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Unsupported IOCTL %lx from non-router client.\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode)); + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + default: + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Unsupported function %lx.\n", + IrpStack->MajorFunction)); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + +DispatchExit: + if (status!=STATUS_PENDING) { + Irp->IoStatus.Status = status; + if (NT_SUCCESS (status)) + ; + else + IpxFltDbgPrint (DBG_IOCTLS|DBG_ERRORS, + ("IpxFlt: Failed call with status %lx.\n", + status)); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return status; +} + + + +/*++ + +Routine Description: + Cleans up on driver unload + +Arguments: + + DriverObject - pointer to a driver object + +Return Value: + + +--*/ +VOID +IpxFltUnload( + IN PDRIVER_OBJECT DriverObject + ) { + IpxFltDbgPrint (DBG_IOCTLS, ("IpxFlt: Unloading\n")); + if (RouterFile!=NULL) { + DeleteTables (); + RouterFile = NULL; + } + UnbindFromFwdDriver (KernelMode); + IoDeleteDevice (DriverObject->DeviceObject); +} + + +/*++ + I p x F l t C a n c e l + +Routine Description: + Cancels specified IRP + +Arguments: + DeviceObject - forwarder device object + irp - irp to cancel + +Return Value: + None +--*/ +VOID +IpxFltCancel ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP irp + ) { + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + IoReleaseCancelSpinLock (irp->CancelIrql); + + irp->IoStatus.Status = STATUS_CANCELLED; + IpxFltDbgPrint(DBG_IOCTLS, ("IpxFlt: completing cancelled irp.\n")); + IoCompleteRequest(irp, IO_NO_INCREMENT); +} + diff --git a/private/ntos/tdi/isn/flt/filter.c b/private/ntos/tdi/isn/flt/filter.c new file mode 100644 index 000000000..198f4addf --- /dev/null +++ b/private/ntos/tdi/isn/flt/filter.c @@ -0,0 +1,856 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\flt\filter.c + +Abstract: + IPX Filter driver filtering and maintanance routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + // Masks to test components of the filter descriptor + // (Have to use globals in lue of constants to get correct + // byte ordering) +const union { + struct { + UCHAR Src[4]; + UCHAR Dst[4]; + } FD_Network; + ULONGLONG FD_NetworkSrcDst; + } FltSrcNetMask = {{{0xFF, 0xFF, 0xFF, 0xFF}, {0, 0, 0, 0}}}; +#define FLT_SRC_NET_MASK FltSrcNetMask.FD_NetworkSrcDst + +const union { + struct { + UCHAR Src[4]; + UCHAR Dst[4]; + } FD_Network; + ULONGLONG FD_NetworkSrcDst; + } FltDstNetMask = {{{0, 0, 0, 0}, {0xFF, 0xFF, 0xFF, 0xFF}}}; +#define FLT_DST_NET_MASK FltDstNetMask.FD_NetworkSrcDst + +const union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_NS; + ULONGLONG FD_NodeSocket; + } FltNodeMask = {{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0, 0}}}; +#define FLT_NODE_MASK FltNodeMask.FD_NodeSocket + +const union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_NS; + ULONGLONG FD_NodeSocket; + } FltSocketMask = {{{0, 0, 0, 0, 0, 0}, {0xFF, 0xFF}}}; +#define FLT_SOCKET_MASK FltSocketMask.FD_NodeSocket + + // Hash tables of interface control blocks with filter descriptions + // Input filters +LIST_ENTRY InterfaceInHash[FLT_INTERFACE_HASH_SIZE]; + // Output filters +LIST_ENTRY InterfaceOutHash[FLT_INTERFACE_HASH_SIZE]; + // Serializes access to interface table +FAST_MUTEX InterfaceTableLock; +LIST_ENTRY LogIrpQueue; +USHORT LogSeqNum; + + + // Hash function for interface hash tables +#define InterfaceIndexHash(Index) (Index%FLT_INTERFACE_HASH_SIZE) + + // Packet descriptor block +typedef struct _PACKET_DESCR { + union { + struct { + ULONG Src; // Source network + ULONG Dst; // Destination network + } PD_Network; + ULONGLONG PD_NetworkSrcDst; // Combined field + }; + ULONGLONG PD_SrcNodeSocket; // Source node & socket + ULONGLONG PD_DstNodeSocket; // Destination node & socket + LONG PD_ReferenceCount; // Filter reference count + UCHAR PD_PacketType; // Packet type + BOOLEAN PD_LogMatches; +} PACKET_DESCR, *PPACKET_DESCR; + + // Packet cache (only though that pass the filter) +PPACKET_DESCR PacketCache[FLT_PACKET_CACHE_SIZE]; +KSPIN_LOCK PacketCacheLock; + + +/*++ + A c q u i r e P a c k e t R e f e r e n c e + +Routine Description: + + Returns reference to the packet descriptor in the cache + +Arguments: + idx - cache index + pd - pointer to packet descriptor to be returned + +Return Value: + None + +--*/ +//VOID +//AcquirePacketReference ( +// IN UINT idx, +// OUT PPACKET_DESCR pd +// ); +#define AcquirePacketReference(idx,pd) { \ + KIRQL oldIRQL; \ + KeAcquireSpinLock (&PacketCacheLock, &oldIRQL); \ + if ((pd=PacketCache[idx])!=NULL) \ + InterlockedIncrement (&pd->PD_ReferenceCount); \ + KeReleaseSpinLock (&PacketCacheLock, oldIRQL); \ +} + +/*++ + R e l e a s e P a c k e t R e f e r e n c e + +Routine Description: + + Releases reference to the cached packet descriptor + +Arguments: + pd - pointer to packet descriptor to release + +Return Value: + None + +--*/ +//VOID +//ReleasePacketReference ( +// IN PPACKET_DESCR pd +// ); +#define ReleasePacketReference(pd) { \ + if (InterlockedDecrement (&pd->PD_ReferenceCount)>=0) \ + NOTHING; \ + else \ + ExFreePool (pd); \ +} + +/*++ + R e p l a c e P a c k e t R e f e r e n c e + +Routine Description: + + Replaces packet cache entry + +Arguments: + idx - cache index + pd - pointer to packet descriptor to be installed in the cache + +Return Value: + None + +--*/ +//VOID +//ReplacePacket ( +// IN UINT idx, +// IN PPACKET_DESCR pd +// ); +#define ReplacePacket(idx,pd) { \ + KIRQL oldIRQL; \ + PPACKET_DESCR oldPD; \ + KeAcquireSpinLock (&PacketCacheLock, &oldIRQL); \ + oldPD = PacketCache[idx]; \ + PacketCache[idx] = pd; \ + KeReleaseSpinLock (&PacketCacheLock, oldIRQL); \ + IpxFltDbgPrint (DBG_PKTCACHE, \ + ("IpxFlt: Replaced packet descriptor %08lx" \ + " with %08lx in cache at index %ld.\n", \ + oldPD, pd, idx)); \ + if (oldPD!=NULL) { \ + ReleasePacketReference(oldPD); \ + } \ +} + + // Defined below +VOID +FlushPacketCache ( + VOID + ); + +/*++ + I n i t i a l i z e T a b l e s + +Routine Description: + + Initializes hash and cash tables and protection stuff +Arguments: + None +Return Value: + STATUS_SUCCESS + +--*/ +NTSTATUS +InitializeTables ( + VOID + ) { + UINT i; + for (i=0; iICB_Index, NULL); + ASSERT (status==STATUS_SUCCESS); + RemoveEntryList (&ifCB->ICB_Link); + ExFreePool (ifCB); + } + while (!IsListEmpty (&InterfaceOutHash[i])) { + NTSTATUS status; + PINTERFACE_CB ifCB = CONTAINING_RECORD (InterfaceOutHash[i].Flink, + INTERFACE_CB, ICB_Link); + status = FwdSetFilterOutContext (ifCB->ICB_Index, NULL); + ASSERT (status==STATUS_SUCCESS); + RemoveEntryList (&ifCB->ICB_Link); + ExFreePool (ifCB); + } + } + for (i=0; i0) { + ifCB = ExAllocatePoolWithTag ( + NonPagedPool, + FIELD_OFFSET (INTERFACE_CB, ICB_Filters[FilterCount]), + IPX_FLT_TAG + ); + if (ifCB==NULL) { + IpxFltDbgPrint (DBG_IFHASH|DBG_ERRORS, + ("IpxFlt: Could not allocate interface CB for if %ld.\n", + Index)); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ifCB->ICB_Index = Index; + ifCB->ICB_FilterAction = (FilterAction==IPX_TRAFFIC_FILTER_ACTION_PERMIT) + ? FILTER_PERMIT : FILTER_DENY; + ifCB->ICB_FilterCount = FilterCount; + // Copy/Map UI filters to the internal format + for (i=0, fd = ifCB->ICB_Filters; iFilterDefinition&IPX_TRAFFIC_FILTER_ON_SRCNET) { + memcpy (fd->FD_Network.Src, FilterInfo->SourceNetwork, 4); + memcpy (fd->FD_NetworkMask.Src, FilterInfo->SourceNetworkMask, 4); + } + else { + memset (fd->FD_Network.Src, 0, 4); + memset (fd->FD_NetworkMask.Src, 0, 4); + } + + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_DSTNET) { + memcpy (fd->FD_Network.Dst, FilterInfo->DestinationNetwork, 4); + memcpy (fd->FD_NetworkMask.Dst, FilterInfo->DestinationNetworkMask, 4); + } + else { + memset (fd->FD_Network.Dst, 0, 4); + memset (fd->FD_NetworkMask.Dst, 0, 4); + } + + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_SRCNODE) { + memcpy (fd->FD_SrcNS.Node, FilterInfo->SourceNode, 6); + memset (fd->FD_SrcNSMask.Node, 0xFF, 6); + } + else { + memset (fd->FD_SrcNS.Node, 0, 6); + memset (fd->FD_SrcNSMask.Node, 0, 6); + } + + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_SRCSOCKET) { + memcpy (fd->FD_SrcNS.Socket, FilterInfo->SourceSocket, 2); + memset (fd->FD_SrcNSMask.Socket, 0xFF, 2); + } + else { + memset (fd->FD_SrcNS.Socket, 0, 2); + memset (fd->FD_SrcNSMask.Socket, 0, 2); + } + + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_DSTNODE) { + memcpy (fd->FD_DstNS.Node, FilterInfo->DestinationNode, 6); + memset (fd->FD_DstNSMask.Node, 0xFF, 6); + } + else { + memset (fd->FD_DstNS.Node, 0, 6); + memset (fd->FD_DstNSMask.Node, 0, 6); + } + + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_DSTSOCKET) { + memcpy (fd->FD_DstNS.Socket, FilterInfo->DestinationSocket, 2); + memset (fd->FD_DstNSMask.Socket, 0xFF, 2); + } + else { + memset (fd->FD_DstNS.Socket, 0, 2); + memset (fd->FD_DstNSMask.Socket, 0, 2); + } + if (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_ON_PKTTYPE) { + fd->FD_PacketType = FilterInfo->PacketType; + fd->FD_PacketTypeMask = 0xFF; + } + else { + fd->FD_PacketType = 0; + fd->FD_PacketTypeMask = 0; + } + + fd->FD_LogMatches = (FilterInfo->FilterDefinition&IPX_TRAFFIC_FILTER_LOG_MATCHES)!=0; + } + } + + ExAcquireFastMutex (&InterfaceTableLock); + + // Find the old block and/or a place for a new one + cur = HashBucket->Flink; + while (cur!=HashBucket) { + oldCB = CONTAINING_RECORD (cur, INTERFACE_CB, ICB_Link); + if (oldCB->ICB_Index==Index) { + // Found the old one, place new after it + cur = cur->Flink; + break; + } + else if (oldCB->ICB_Index>Index) { + // No chance to see the old one anymore, place where + // we are now + oldCB = NULL; + break; + } + cur = cur->Flink; + } + + // Set context in forwarder + if (HashTable==InterfaceInHash) { + status = FwdSetFilterInContext (Index, ifCB); + } + else { + ASSERT (HashTable==InterfaceOutHash); + status = FwdSetFilterOutContext (Index, ifCB); + } + + if (NT_SUCCESS (status)) { + // Update table if we succeded + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: Set filters for if %ld (ifCB:%08lx).\n", + Index, ifCB)); + + if (oldCB!=NULL) { + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: Deleting replaced filters for if %ld (ifCB:%08lx).\n", + Index, oldCB)); + RemoveEntryList (&oldCB->ICB_Link); + ExFreePool (oldCB); + } + + + + if (ifCB!=NULL) { + InsertTailList (cur, &ifCB->ICB_Link); + } + + FlushPacketCache (); + } + else { + IpxFltDbgPrint (DBG_IFHASH|DBG_ERRORS, + ("IpxFlt: Failed to set context for if %ld (ifCB:%08lx).\n", + Index, ifCB)); + } + + ExReleaseFastMutex (&InterfaceTableLock); + return status; +} + +/*++ + G e t F i l t e r s + +Routine Description: + + Gets filter information for an interface +Arguments: + HashTable - input or output hash table + Index - interface index + FilterAction - default action if there is no filter match + TotalSize - total memory required to hold all filter descriptions + FilterInfo - array of filter descriptions (UI format) + FilterInfoSize - on input: size of the info array + on output: size of the info placed in the array +Return Value: + STATUS_SUCCESS - filter info was returned ok + STATUS_BUFFER_OVERFLOW - array is not big enough to hold all + filter info, only placed the info that fit + +--*/ +NTSTATUS +GetFilters ( + IN PLIST_ENTRY HashTable, + IN ULONG Index, + OUT ULONG *FilterAction, + OUT ULONG *TotalSize, + OUT PIPX_TRAFFIC_FILTER_INFO FilterInfo, + IN OUT ULONG *FilterInfoSize + ) { + PINTERFACE_CB oldCB = NULL; + ULONG i, AvailBufCount = + (*FilterInfoSize)/sizeof (IPX_TRAFFIC_FILTER_INFO); + PFILTER_DESCR fd; + PLIST_ENTRY HashBucket = &HashTable[InterfaceIndexHash(Index)], cur; + NTSTATUS status = STATUS_SUCCESS; + + // Locate interface filters block + ExAcquireFastMutex (&InterfaceTableLock); + cur = HashBucket->Flink; + while (cur!=HashBucket) { + oldCB = CONTAINING_RECORD (cur, INTERFACE_CB, ICB_Link); + if (oldCB->ICB_Index==Index) { + cur = cur->Flink; + break; + } + else if (oldCB->ICB_Index>Index) { + oldCB = NULL; + break; + } + cur = cur->Flink; + } + + if (oldCB!=NULL) { + *FilterAction = IS_FILTERED(oldCB->ICB_FilterAction) + ? IPX_TRAFFIC_FILTER_ACTION_DENY + : IPX_TRAFFIC_FILTER_ACTION_PERMIT; + *TotalSize = oldCB->ICB_FilterCount*sizeof (IPX_TRAFFIC_FILTER_INFO); + // Copy/Map as many descriptors as fit + for (i=0, fd = oldCB->ICB_Filters; + (iICB_FilterCount) && (iFilterDefinition = 0; + if (fd->FD_NetworkMaskSrcDst&FLT_SRC_NET_MASK) { + memcpy (FilterInfo->SourceNetwork, fd->FD_Network.Src, 4); + memcpy (FilterInfo->SourceNetworkMask, fd->FD_NetworkMask.Src, 4); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_SRCNET; + } + + if (fd->FD_NetworkMaskSrcDst&FLT_DST_NET_MASK) { + memcpy (FilterInfo->DestinationNetwork, fd->FD_Network.Dst, 4); + memcpy (FilterInfo->DestinationNetworkMask, fd->FD_NetworkMask.Dst, 4); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_DSTNET; + } + + if (fd->FD_SrcNodeSocketMask&FLT_NODE_MASK) { + memcpy (FilterInfo->SourceNode, fd->FD_SrcNS.Node, 6); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_SRCNODE; + } + + if (fd->FD_DstNodeSocketMask&FLT_NODE_MASK) { + memcpy (FilterInfo->DestinationNode, fd->FD_DstNS.Node, 6); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_DSTNODE; + } + + if (fd->FD_SrcNodeSocketMask&FLT_SOCKET_MASK) { + memcpy (FilterInfo->SourceSocket, fd->FD_SrcNS.Socket, 2); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_SRCSOCKET; + } + if (fd->FD_DstNodeSocketMask&FLT_SOCKET_MASK) { + memcpy (FilterInfo->DestinationSocket, fd->FD_DstNS.Socket, 2); + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_DSTSOCKET; + } + if (fd->FD_PacketTypeMask&0xFF) { + FilterInfo->PacketType = fd->FD_PacketType; + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_ON_PKTTYPE; + } + if (fd->FD_LogMatches) + FilterInfo->FilterDefinition |= IPX_TRAFFIC_FILTER_LOG_MATCHES; + } + + *FilterInfoSize = i*sizeof (IPX_TRAFFIC_FILTER_INFO); + + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: Returning %d filters (%d available)" + " for interface %d (ifCB: %08lx).\n", + i, oldCB->ICB_FilterCount, Index)); + if (iICB_FilterCount) + status = STATUS_BUFFER_OVERFLOW; + ExReleaseFastMutex (&InterfaceTableLock); + } + else { + // No interface block -> we are passing all the packets + // unfiltered + ExReleaseFastMutex (&InterfaceTableLock); + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: No filters for interface %d.\n", Index)); + *FilterAction = IPX_TRAFFIC_FILTER_ACTION_PERMIT; + *TotalSize = 0; + *FilterInfoSize = 0; + } + return status; +} + + +VOID +LogPacket ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContext + ) { + PIRP irp; + PIO_STACK_LOCATION irpStack; + ULONG outBufLength; + PUCHAR outBuffer; + ULONG offset; + KIRQL cancelIRQL; + + IoAcquireCancelSpinLock (&cancelIRQL); + LogSeqNum += 1; + while (!IsListEmpty (&LogIrpQueue)) { + irp = CONTAINING_RECORD (LogIrpQueue.Flink,IRP,Tail.Overlay.ListEntry); + irpStack = IoGetCurrentIrpStackLocation(irp); + outBufLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; + outBuffer = (PUCHAR)MmGetSystemAddressForMdl (irp->MdlAddress); + offset = (PUCHAR)ALIGN_UP (outBuffer+irp->IoStatus.Information, ULONG)-outBuffer; + if (offset+ipxHdrLength+FIELD_OFFSET (FLT_PACKET_LOG, Header)SrcIfIdx = ifInContext + ? ((PINTERFACE_CB)ifInContext)->ICB_Index + : -1; + pLog->DstIfIdx = ifOutContext + ? ((PINTERFACE_CB)ifOutContext)->ICB_Index + : -1; + pLog->DataSize = (USHORT)ipxHdrLength; + pLog->SeqNum = LogSeqNum; + memcpy (pLog->Header, ipxHdr, ipxHdrLength); + irp->IoStatus.Information = offset+FIELD_OFFSET (FLT_PACKET_LOG, Header[ipxHdrLength]); + if (irp->Tail.Overlay.ListEntry.Flink!=&LogIrpQueue) { + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine (irp, NULL); + irp->IoStatus.Status = STATUS_SUCCESS; + IoReleaseCancelSpinLock (cancelIRQL); + IpxFltDbgPrint (DBG_PKTLOGS, + ("IpxFlt: completing logging request" + " with %d bytes of data.\n", + irp->IoStatus.Information)); + IoCompleteRequest (irp, IO_NO_INCREMENT); + return; + } + else + break; + } + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine (irp, NULL); + irp->IoStatus.Status = STATUS_SUCCESS; + IoReleaseCancelSpinLock (cancelIRQL); + IpxFltDbgPrint (DBG_ERRORS|DBG_PKTLOGS, + ("IpxFlt: completing logging request" + " with %d bytes of data (not enough space).\n", + irp->IoStatus.Information)); + IoCompleteRequest (irp, IO_NO_INCREMENT); + IoAcquireCancelSpinLock (&cancelIRQL); + } + IoReleaseCancelSpinLock (cancelIRQL); +} + + +/*++ + F i l t e r + +Routine Description: + + Filters the packet supplied by the forwarder + +Arguments: + ipxHdr - pointer to packet header + ipxHdrLength - size of the header buffer (must be at least 30) + ifInContext - context associated with interface on which packet + was received + ifOutContext - context associated with interface on which packet + will be sent +Return Value: + FILTER_PERMIT - packet should be passed on by the forwarder + FILTER_DENY_IN - packet should be dropped because of input filter + FILTER_DENY_OUT - packet should be dropped because of output filter +--*/ +FILTER_ACTION +Filter ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContext + ) { + PACKET_DESCR pd; + FILTER_ACTION res = FILTER_PERMIT; + UINT idx; + + ASSERT (ipxHdrLength>=IPXH_HDRSIZE); + // Copy packet to aligned buffer + pd.PD_Network.Dst = *((UNALIGNED ULONG *)(ipxHdr+IPXH_DESTNET)); + pd.PD_Network.Src = *((UNALIGNED ULONG *)(ipxHdr+IPXH_SRCNET)); + pd.PD_DstNodeSocket = *((UNALIGNED ULONGLONG *)(ipxHdr+IPXH_DESTNODE)); + pd.PD_SrcNodeSocket = *((UNALIGNED ULONGLONG *)(ipxHdr+IPXH_SRCNODE)); + pd.PD_PacketType = *(ipxHdr+IPXH_PKTTYPE); + pd.PD_LogMatches = FALSE; + // We do not cache netbios broadcast + if (pd.PD_PacketType!=IPX_NETBIOS_TYPE) { + PPACKET_DESCR cachedPD; + // Get cached packet + idx = (UINT)((pd.PD_Network.Dst + +pd.PD_DstNodeSocket + +pd.PD_PacketType) + %FLT_PACKET_CACHE_SIZE); + AcquirePacketReference (idx, cachedPD); + if (cachedPD!=NULL) { + // Fast path: packet in the cache matches + if ((pd.PD_NetworkSrcDst==cachedPD->PD_NetworkSrcDst) + && (pd.PD_SrcNodeSocket==cachedPD->PD_SrcNodeSocket) + && (pd.PD_DstNodeSocket==cachedPD->PD_DstNodeSocket) + && (pd.PD_PacketType==cachedPD->PD_PacketType)) { + if (cachedPD->PD_LogMatches) + LogPacket (ipxHdr,ipxHdrLength,ifInContext,ifOutContext); + ReleasePacketReference (cachedPD); + return FILTER_PERMIT; + } + // Do not need cached packet anymore + ReleasePacketReference (cachedPD); + } + } + // Slow path: check all filters + if (ifInContext!=NO_FILTER_CONTEXT) { + PFILTER_DESCR fd, fdEnd; + // Read default result (no filter match) + res = NOT_FILTER_ACTION(((PINTERFACE_CB)ifInContext)->ICB_FilterAction); + fd = ((PINTERFACE_CB)ifInContext)->ICB_Filters; + fdEnd = &((PINTERFACE_CB)ifInContext)->ICB_Filters + [((PINTERFACE_CB)ifInContext)->ICB_FilterCount]; + while (fdFD_NetworkMaskSrcDst) + == fd->FD_NetworkSrcDst) + && ((pd.PD_SrcNodeSocket & fd->FD_SrcNodeSocketMask) + == fd->FD_SrcNodeSocket) + && ((pd.PD_DstNodeSocket & fd->FD_DstNodeSocketMask) + == fd->FD_DstNodeSocket) + && ((pd.PD_PacketType & fd->FD_PacketTypeMask) + == fd->FD_PacketType) ) { + // Filter match: reverse the result + res = NOT_FILTER_ACTION(res); + if (fd->FD_LogMatches) { + pd.PD_LogMatches = TRUE; + LogPacket (ipxHdr,ipxHdrLength,ifInContext,ifOutContext); + } + break; + } + fd++; + } + // Return right away if told to drop + if (IS_FILTERED(res)) + return FILTER_DENY_IN; + } + + if (ifOutContext!=NO_FILTER_CONTEXT) { + PFILTER_DESCR fd, fdEnd; + // Read default result (no filter match) + res = NOT_FILTER_ACTION(((PINTERFACE_CB)ifOutContext)->ICB_FilterAction); + fd = ((PINTERFACE_CB)ifOutContext)->ICB_Filters; + fdEnd = &((PINTERFACE_CB)ifOutContext)->ICB_Filters + [((PINTERFACE_CB)ifOutContext)->ICB_FilterCount]; + while (fdFD_NetworkMaskSrcDst) + == fd->FD_NetworkSrcDst) + && ((pd.PD_SrcNodeSocket & fd->FD_SrcNodeSocketMask) + == fd->FD_SrcNodeSocket) + && ((pd.PD_DstNodeSocket & fd->FD_DstNodeSocketMask) + == fd->FD_DstNodeSocket) + && ((pd.PD_PacketType & fd->FD_PacketTypeMask) + == fd->FD_PacketType) ) { + // Filter match: reverse the result + res = NOT_FILTER_ACTION(res); + if (fd->FD_LogMatches&&!pd.PD_LogMatches) { + pd.PD_LogMatches = TRUE; + LogPacket (ipxHdr,ipxHdrLength,ifInContext,ifOutContext); + } + break; + } + fd++; + } + // Return right away if told to drop + if (IS_FILTERED(res)) + return FILTER_DENY_OUT; + } + + // Cache the packet (we know that it is a pass + // because we would have returned if it was a drop) + if (pd.PD_PacketType!=IPX_NETBIOS_TYPE) { + PPACKET_DESCR cachedPD; + cachedPD = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (PACKET_DESCR), + IPX_FLT_TAG + ); + if (cachedPD!=NULL) { + *cachedPD = pd; + cachedPD->PD_ReferenceCount = 0; + ReplacePacket (idx, cachedPD); + } + } + + return res; +} + + +/*++ + I n t e r f a c e D e l e t e d + +Routine Description: + + Frees interface filters blocks when forwarder indicates that + interface is deleted +Arguments: + ifInContext - context associated with input filters block + ifOutContext - context associated with output filters block +Return Value: + None + +--*/ +VOID +InterfaceDeleted ( + IN PVOID ifInContext, + IN PVOID ifOutContext + ) { + IpxFltDbgPrint (DBG_FWDIF, + ("IpxFlt: InterfaceDeleted indication," + "(inContext: %08lx, outContext: %08lx).\n", + ifInContext, ifOutContext)); + ExAcquireFastMutex (&InterfaceTableLock); + if (ifInContext!=NULL) { + PINTERFACE_CB ifCB = (PINTERFACE_CB)ifInContext; + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: Deleting filters for if %ld (ifCB:%08lx)" + " on InterfaceDeleted indication from forwarder.\n", + ifCB->ICB_Index, ifCB)); + RemoveEntryList (&ifCB->ICB_Link); + ExFreePool (ifCB); + } + + if (ifOutContext!=NULL) { + PINTERFACE_CB ifCB = (PINTERFACE_CB)ifOutContext; + IpxFltDbgPrint (DBG_IFHASH, + ("IpxFlt: Deleting filters for if %ld (ifCB:%08lx)" + " on InterfaceDeleted indication from forwarder.\n", + ifCB->ICB_Index, ifCB)); + RemoveEntryList (&ifCB->ICB_Link); + ExFreePool (ifCB); + } + ExReleaseFastMutex (&InterfaceTableLock); + FlushPacketCache (); + return ; +} + +/*++ + F l u s h P a c k e t C a c h e + +Routine Description: + + Deletes all cached packet descriptions +Arguments: + None +Return Value: + None + +--*/ +VOID +FlushPacketCache ( + VOID + ) { + UINT i; + IpxFltDbgPrint (DBG_PKTCACHE, ("IpxFlt: Flushing packet chache.\n")); + for (i=0; i>8)); \ + ((UCHAR *)dst)[1] = ((UCHAR)src); \ +} + +#define PUTULONG(src,dst) { \ + ((UCHAR *)dst)[0] = ((UCHAR)(src>>24)); \ + ((UCHAR *)dst)[1] = ((UCHAR)(src>>16)); \ + ((UCHAR *)dst)[2] = ((UCHAR)(src>>8)); \ + ((UCHAR *)dst)[3] = ((UCHAR)src); \ +} + + // Other important constatns +#define FLT_INTERFACE_HASH_SIZE 257 +#define FLT_PACKET_CACHE_SIZE 257 +#define IPX_FLT_TAG 'lFwN' + + + // Filter description +typedef struct _FILTER_DESCR { + union { + struct { + UCHAR Src[4]; + UCHAR Dst[4]; + } FD_Network; + ULONGLONG FD_NetworkSrcDst; + }; + union { + struct { + UCHAR Src[4]; + UCHAR Dst[4]; + } FD_NetworkMask; + ULONGLONG FD_NetworkMaskSrcDst; + }; + union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_SrcNS; + ULONGLONG FD_SrcNodeSocket; + }; + union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_SrcNSMask; + ULONGLONG FD_SrcNodeSocketMask; + }; + union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_DstNS; + ULONGLONG FD_DstNodeSocket; + }; + union { + struct { + UCHAR Node[6]; + UCHAR Socket[2]; + } FD_DstNSMask; + ULONGLONG FD_DstNodeSocketMask; + }; + UCHAR FD_PacketType; + UCHAR FD_PacketTypeMask; + BOOLEAN FD_LogMatches; +} FILTER_DESCR, *PFILTER_DESCR; + + // Interface filters block +typedef struct _INTERFACE_CB { + LIST_ENTRY ICB_Link; + ULONG ICB_Index; + ULONG ICB_FilterAction; + ULONG ICB_FilterCount; + FILTER_DESCR ICB_Filters[1]; +} INTERFACE_CB, *PINTERFACE_CB; + + // Interface hash tables +extern LIST_ENTRY InterfaceInHash[FLT_INTERFACE_HASH_SIZE]; +extern LIST_ENTRY InterfaceOutHash[FLT_INTERFACE_HASH_SIZE]; +extern LIST_ENTRY LogIrpQueue; + +/*++ + I n i t i a l i z e T a b l e s + +Routine Description: + + Initializes hash and cash tables and protection stuff +Arguments: + None +Return Value: + STATUS_SUCCESS + +--*/ +NTSTATUS +InitializeTables ( + VOID + ); + +/*++ + D e l e t e T a b l e s + +Routine Description: + + Deletes hash and cash tables +Arguments: + None +Return Value: + None + +--*/ +VOID +DeleteTables ( + VOID + ); + +/*++ + S e t F i l t e r s + +Routine Description: + + Sets/replaces filter information for an interface +Arguments: + HashTable - input or output hash table + Index - interface index + FilterAction - default action if there is no filter match + FilterInfoSize - size of the info array + FilterInfo - array of filter descriptions (UI format) +Return Value: + STATUS_SUCCESS - filter info was set/replaced ok + STATUS_UNSUCCESSFUL - could not set filter context in forwarder + STATUS_INSUFFICIENT_RESOURCES - not enough memory to allocate + filter info block for interface + +--*/ +NTSTATUS +SetFilters ( + IN PLIST_ENTRY HashTable, + IN ULONG InterfaceIndex, + IN ULONG FilterAction, + IN ULONG FilterInfoSize, + IN PIPX_TRAFFIC_FILTER_INFO FilterInfo + ); +#define SetInFilters(Index,Action,InfoSize,Info) \ + SetFilters(InterfaceInHash,Index,Action,InfoSize,Info) +#define SetOutFilters(Index,Action,InfoSize,Info) \ + SetFilters(InterfaceOutHash,Index,Action,InfoSize,Info) + + +/*++ + G e t F i l t e r s + +Routine Description: + + Gets filter information for an interface +Arguments: + HashTable - input or output hash table + Index - interface index + FilterAction - default action if there is no filter match + TotalSize - total memory required to hold all filter descriptions + FilterInfo - array of filter descriptions (UI format) + FilterInfoSize - on input: size of the info array + on output: size of the info placed in the array +Return Value: + STATUS_SUCCESS - filter info was returned ok + STATUS_BUFFER_OVERFLOW - array is not big enough to hold all + filter info, only placed the info that fit + +--*/ +NTSTATUS +GetFilters ( + IN PLIST_ENTRY HashTable, + IN ULONG InterfaceIndex, + OUT ULONG *FilterAction, + OUT ULONG *TotalSize, + OUT PIPX_TRAFFIC_FILTER_INFO FilterInfo, + IN OUT ULONG *FilterInfoSize + ); +#define GetInFilters(Index,Action,TotalSize,Info,InfoSize) \ + GetFilters(InterfaceInHash,Index,Action,TotalSize,Info,InfoSize) +#define GetOutFilters(Index,Action,TotalSize,Info,InfoSize) \ + GetFilters(InterfaceOutHash,Index,Action,TotalSize,Info,InfoSize) + +/*++ + F i l t e r + +Routine Description: + + Filters the packet supplied by the forwarder + +Arguments: + ipxHdr - pointer to packet header + ipxHdrLength - size of the header buffer (must be at least 30) + ifInContext - context associated with interface on which packet + was received + ifOutContext - context associated with interface on which packet + will be sent +Return Value: + FILTER_PERMIT - packet should be passed on by the forwarder + FILTER_DEDY - packet should be dropped +--*/ +FILTER_ACTION +Filter ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContext + ); + +/*++ + I n t e r f a c e D e l e t e d + +Routine Description: + + Frees interface filters blocks when forwarder indicates that + interface is deleted +Arguments: + ifInContext - context associated with input filters block + ifOutContext - context associated with output filters block +Return Value: + None + +--*/ +VOID +InterfaceDeleted ( + IN PVOID ifInContext, + IN PVOID ifOutContext + ); + + +#endif + diff --git a/private/ntos/tdi/isn/flt/flt.mak b/private/ntos/tdi/isn/flt/flt.mak new file mode 100644 index 000000000..760380500 --- /dev/null +++ b/private/ntos/tdi/isn/flt/flt.mak @@ -0,0 +1,497 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=flt - Win32 Release +!MESSAGE No configuration specified. Defaulting to flt - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "flt - Win32 Release" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "flt.mak" CFG="flt - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "flt - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "flt - Win32 Release" +RSC=rc.exe +CPP=cl.exe +MTL=mktyplib.exe +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Target_Dir "" +OUTDIR=. +INTDIR=. + +ALL : "$(OUTDIR)\flt.dll" + +CLEAN : + -@erase "$(INTDIR)\debug.obj" + -@erase "$(INTDIR)\driver.obj" + -@erase "$(INTDIR)\filter.obj" + -@erase "$(INTDIR)\fwdbind.obj" + -@erase "$(INTDIR)\nwlnkflt.res" + -@erase "$(OUTDIR)\flt.dll" + -@erase "$(OUTDIR)\flt.exp" + -@erase "$(OUTDIR)\flt.lib" + +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /Gz /MD /W3 /WX /Z7 /Oi /Gy /I "." /I "..\inc" /I "..\..\..\inc" /I "..\..\..\..\inc" /I "..\..\..\..\net\routing\inc" /FI"C:\NT\public\sdk\inc\warning.h" /D _X86_=1 /D i386=1 /D "STD_CALL" /D CONDITION_HANDLING=1 /D NT_INST=0 /D WIN32=100 /D _NT1X_=100 /D WINNT=1 /D WIN32_LEAN_AND_MEAN=1 /D DBG=1 /D DEVL=1 /D FPO=0 /D "_NTDRIVER_" /Fp"obj\i386\precomp.pch" /Yu"precomp.h" /Zel -cbstring /QIfdiv- /QI6 /QIf /GF /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/flt.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo\ + /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)/flt.pdb" /machine:I386\ + /out:"$(OUTDIR)/flt.dll" /implib:"$(OUTDIR)/flt.lib" +LINK32_OBJS= \ + "$(INTDIR)\debug.obj" \ + "$(INTDIR)\driver.obj" \ + "$(INTDIR)\filter.obj" \ + "$(INTDIR)\fwdbind.obj" \ + "$(INTDIR)\nwlnkflt.res" + +"$(OUTDIR)\flt.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +RSC_PROJ=/l 0x409 /fo"$(INTDIR)/nwlnkflt.res" /d "NDEBUG" +CPP_PROJ=/nologo /Gz /MD /W3 /WX /Z7 /Oi /Gy /I "." /I "..\inc" /I\ + "..\..\..\inc" /I "..\..\..\..\inc" /I "..\..\..\..\net\routing\inc"\ + /FI"C:\NT\public\sdk\inc\warning.h" /D _X86_=1 /D i386=1 /D "STD_CALL" /D\ + CONDITION_HANDLING=1 /D NT_INST=0 /D WIN32=100 /D _NT1X_=100 /D WINNT=1 /D\ + WIN32_LEAN_AND_MEAN=1 /D DBG=1 /D DEVL=1 /D FPO=0 /D "_NTDRIVER_"\ + /Fp"$(INTDIR)/obj\i386\precomp.pch" /Yu"precomp.h" /Zel -cbstring /QIfdiv- /QI6\ + /QIf /GF /c + +.c.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx.obj: + $(CPP) $(CPP_PROJ) $< + +.c.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL_PROJ=/nologo /D "NDEBUG" /win32 +################################################################################ +# Begin Target + +# Name "flt - Win32 Release" +################################################################################ +# Begin Source File + +SOURCE=.\debug.c +DEP_CPP_DEBUG=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\ipxtfflt.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\ipxfltif.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\debug.h"\ + ".\filter.h"\ + ".\fwdbind.h"\ + ".\precomp.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\mipsinst.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntalpha.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\nti386.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmips.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntppc.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\ppcinst.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\debug.obj" : $(SOURCE) $(DEP_CPP_DEBUG) "$(INTDIR)"\ + "$(INTDIR)\obj\i386\precomp.pch" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\driver.c +DEP_CPP_DRIVE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\ipxtfflt.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\ipxfltif.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\debug.h"\ + ".\filter.h"\ + ".\fwdbind.h"\ + ".\precomp.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\mipsinst.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntalpha.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\nti386.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmips.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntppc.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\ppcinst.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\driver.obj" : $(SOURCE) $(DEP_CPP_DRIVE) "$(INTDIR)"\ + "$(INTDIR)\obj\i386\precomp.pch" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sources.inc +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\nwlnkflt.rc +DEP_RSC_NWLNK=\ + {$(INCLUDE)}"\common.ver"\ + {$(INCLUDE)}"\ntverp.h"\ + + +"$(INTDIR)\nwlnkflt.res" : $(SOURCE) $(DEP_RSC_NWLNK) "$(INTDIR)" + $(RSC) $(RSC_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\filter.c +DEP_CPP_FILTE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\ipxtfflt.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\ipxfltif.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\debug.h"\ + ".\filter.h"\ + ".\fwdbind.h"\ + ".\precomp.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\mipsinst.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntalpha.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\nti386.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmips.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntppc.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\ppcinst.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\filter.obj" : $(SOURCE) $(DEP_CPP_FILTE) "$(INTDIR)"\ + "$(INTDIR)\obj\i386\precomp.pch" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fwdbind.c +DEP_CPP_FWDBI=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\ipxtfflt.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\ipxfltif.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\debug.h"\ + ".\filter.h"\ + ".\fwdbind.h"\ + ".\precomp.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\mipsinst.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntalpha.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\nti386.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmips.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntppc.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\ppcinst.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\fwdbind.obj" : $(SOURCE) $(DEP_CPP_FWDBI) "$(INTDIR)"\ + "$(INTDIR)\obj\i386\precomp.pch" + + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/private/ntos/tdi/isn/flt/fwdbind.c b/private/ntos/tdi/isn/flt/fwdbind.c new file mode 100644 index 000000000..a1ec0ad5a --- /dev/null +++ b/private/ntos/tdi/isn/flt/fwdbind.c @@ -0,0 +1,161 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\flt\fwdbind.c + +Abstract: + IPX Filter driver binding with forwarder routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + // Buffer to keep forwarder entry points +IPX_FLT_BIND_OUTPUT FltBindOutput; +// global handle of the FWD driver +HANDLE HdlFwdFile = NULL; + + +/*++ + B i n d T o F w d D r i v e r + +Routine Description: + + Opens forwarder driver and exchages entry points +Arguments: + None +Return Value: + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +NTSTATUS +BindToFwdDriver ( + KPROCESSOR_MODE requestorMode + ) { + NTSTATUS status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING UstrFwdFileName; + IPX_FLT_BIND_INPUT FltBindInput = {Filter, InterfaceDeleted}; + + ASSERT (HdlFwdFile == NULL); + + RtlInitUnicodeString (&UstrFwdFileName, IPXFWD_NAME); + InitializeObjectAttributes( + &ObjectAttributes, + &UstrFwdFileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (requestorMode==UserMode) + status = ZwCreateFile(&HdlFwdFile, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + else + status = NtCreateFile(&HdlFwdFile, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (NT_SUCCESS(status)) { + + if (requestorMode==UserMode) + status = ZwDeviceIoControlFile( + HdlFwdFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_FWD_INTERNAL_BIND_FILTER, // IoControlCode + &FltBindInput, // Input Buffer + sizeof(FltBindInput), // Input Buffer Length + &FltBindOutput, // Output Buffer + sizeof(FltBindOutput));// Output Buffer Length + else + status = NtDeviceIoControlFile( + HdlFwdFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_FWD_INTERNAL_BIND_FILTER, // IoControlCode + &FltBindInput, // Input Buffer + sizeof(FltBindInput), // Input Buffer Length + &FltBindOutput, // Output Buffer + sizeof(FltBindOutput));// Output Buffer Length + if (NT_SUCCESS (status)) + return STATUS_SUCCESS; + else + IpxFltDbgPrint (DBG_ERRORS, + ("IpxFlt: Failed to bind to forwarder %08lx.\n", status)); + if (requestorMode==KernelMode) + ZwClose (HdlFwdFile); + else + NtClose (HdlFwdFile); + + } + else + IpxFltDbgPrint (DBG_ERRORS, + ("IpxFlt: Failed create forwarder file %08lx.\n", status)); + HdlFwdFile = NULL; + return status; +} + + + +/*++ + U n i n d T o F w d D r i v e r + +Routine Description: + + Closes forwarder driver +Arguments: + None +Return Value: + None + +--*/ +VOID +UnbindFromFwdDriver ( + KPROCESSOR_MODE requestorMode + ) { + NTSTATUS status; + + ASSERT (HdlFwdFile != NULL); + + if (requestorMode==UserMode) + status = ZwClose (HdlFwdFile); + else + status = NtClose (HdlFwdFile); + ASSERT (NT_SUCCESS (status)); + HdlFwdFile = NULL; +} + diff --git a/private/ntos/tdi/isn/flt/fwdbind.h b/private/ntos/tdi/isn/flt/fwdbind.h new file mode 100644 index 000000000..36f79f383 --- /dev/null +++ b/private/ntos/tdi/isn/flt/fwdbind.h @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\flt\fwdbind.h + +Abstract: + IPX Filter driver binding with forwarder routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFLT_FWDBIND_ +#define _IPXFLT_FWDBIND_ + + + // Buffer to keep forwarder entry points +extern IPX_FLT_BIND_OUTPUT FltBindOutput; + + // Forwarder entry points macros +#define FwdSetFilterInContext (FltBindOutput.SetIfInContextHandler) +#define FwdSetFilterOutContext (FltBindOutput.SetIfOutContextHandler) + + +/*++ + B i n d T o F w d D r i v e r + +Routine Description: + + Opens forwarder driver and exchages entry points +Arguments: + None +Return Value: + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +NTSTATUS +BindToFwdDriver ( + KPROCESSOR_MODE requestorMode + ); + +/*++ + U n i n d T o F w d D r i v e r + +Routine Description: + + Closes forwarder driver +Arguments: + None +Return Value: + None + +--*/ +VOID +UnbindFromFwdDriver ( + KPROCESSOR_MODE requestorMode + ); + +#endif + diff --git a/private/ntos/tdi/isn/flt/mp/makefile b/private/ntos/tdi/isn/flt/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/flt/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/flt/mp/sources b/private/ntos/tdi/isn/flt/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/flt/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/flt/nwlnkflt.rc b/private/ntos/tdi/isn/flt/nwlnkflt.rc new file mode 100644 index 000000000..2de2483f2 --- /dev/null +++ b/private/ntos/tdi/isn/flt/nwlnkflt.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 Traffic Filter Driver" +#define VER_INTERNALNAME_STR "nwlnkflt.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkflt.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/flt/precomp.h b/private/ntos/tdi/isn/flt/precomp.h new file mode 100644 index 000000000..9a9d9d54d --- /dev/null +++ b/private/ntos/tdi/isn/flt/precomp.h @@ -0,0 +1,50 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\precomp.h + +Abstract: + IPX Forwarder driver precompiled header file + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#define ISN_NT 1 +#define NT 1 + +#if DBG +#define DEBUG 1 +#endif + +// System includes +#include +#include +#include +#include + +// Routing includes +#include "ipxtfflt.h" + +// IPX shared includes +#include "ipxfwd.h" +#include "ipxfltif.h" + +// Internal module prototypes +#include "filter.h" +#include "fwdbind.h" +#include "debug.h" + + +#pragma hdrstop + + + diff --git a/private/ntos/tdi/isn/flt/sources.inc b/private/ntos/tdi/isn/flt/sources.inc new file mode 100644 index 000000000..4204e970c --- /dev/null +++ b/private/ntos/tdi/isn/flt/sources.inc @@ -0,0 +1,51 @@ +!IF 0 + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: Vadim Eydelman + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ndis + +TARGETNAME=nwlnkflt +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\driver.c \ + ..\filter.c \ + ..\fwdbind.c \ + ..\debug.c \ + ..\nwlnkflt.rc + +RELATIVE_DEPTH=..\.. + +ALT_PROJECT_TARGET=Routing + diff --git a/private/ntos/tdi/isn/flt/up/makefile b/private/ntos/tdi/isn/flt/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/flt/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/flt/up/sources b/private/ntos/tdi/isn/flt/up/sources new file mode 100644 index 000000000..229bd8e34 --- /dev/null +++ b/private/ntos/tdi/isn/flt/up/sources @@ -0,0 +1,31 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +BINPLACE_FLAGS=$(BINPLACE_FLAGS) -d dump\up + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/fwd/ddreqs.c b/private/ntos/tdi/isn/fwd/ddreqs.c new file mode 100644 index 000000000..76ab93ea4 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/ddreqs.c @@ -0,0 +1,220 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\ddreqs.c + +Abstract: + Management of demand dial request queues + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + +LIST_ENTRY ConnectionIrpQueue; +LIST_ENTRY ConnectionRequestQueue; + +/*++ + Q u e u e C o n n e c t i o n R e q u e s t + +Routine Description: + Adds request to connected the interface to the queue + +Arguments: + ifCB - control block of the interface that needs to be + connected + packet - packet that prompted the connection request + data - pointer to actual data in the packet + oldIRQL - IRQL at which interface lock was acquired + +Return Value: + None + + Note that interface lock must be acquired before calling this + routine which will release it + +--*/ +VOID +QueueConnectionRequest ( + PINTERFACE_CB ifCB, + PNDIS_PACKET packet, + PUCHAR data, + KIRQL oldIRQL + ) { + KIRQL cancelIRQL; + + IoAcquireCancelSpinLock (&cancelIRQL); + SET_IF_CONNECTING (ifCB); + if (!IsListEmpty (&ConnectionIrpQueue)) { + PIRP irp = CONTAINING_RECORD ( + ConnectionIrpQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + PIO_STACK_LOCATION irpStack=IoGetCurrentIrpStackLocation(irp); + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + ASSERT (irpStack->Parameters.DeviceIoControl.IoControlCode + ==IOCTL_FWD_GET_DIAL_REQUEST); + ASSERT ((irpStack->Parameters.DeviceIoControl.IoControlCode&3) + ==METHOD_BUFFERED); + IoSetCancelRoutine (irp, NULL); + IoReleaseCancelSpinLock (cancelIRQL); + + FillConnectionRequest ( + ifCB->ICB_Index, + packet, + data, + (PFWD_DIAL_REQUEST)irp->AssociatedIrp.SystemBuffer, + irpStack->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus.Information); + irp->IoStatus.Status = STATUS_SUCCESS; + + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_DIALREQS, DBG_WARNING, + ("IpxFwd: Passing dial request for if %ld (icb:%08lx) with %d bytes of data.\n", + ifCB->ICB_Index, ifCB, irp->IoStatus.Information)); + IoCompleteRequest (irp, IO_NO_INCREMENT); + } + else { + InsertTailList (&ConnectionRequestQueue, &ifCB->ICB_ConnectionLink); + IoReleaseCancelSpinLock (cancelIRQL); + ifCB->ICB_ConnectionPacket = packet; + ifCB->ICB_ConnectionData = data; + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + } +} + +/*++ + D e q u e u e C o n n e c t i o n R e q u e s t + +Routine Description: + Removes conection requset for the interface from the queue + +Arguments: + ifCB - control block of the interface that needs to be + removed + +Return Value: + None + +--*/ +VOID +DequeueConnectionRequest ( + PINTERFACE_CB ifCB + ) { + KIRQL cancelIRQL; + IoAcquireCancelSpinLock (&cancelIRQL); + if (IsListEntry (&ifCB->ICB_ConnectionLink)) { + RemoveEntryList (&ifCB->ICB_ConnectionLink); + InitializeListEntry (&ifCB->ICB_ConnectionLink); + } + IoReleaseCancelSpinLock (cancelIRQL); +} + +/*++ + F i l l C o n n e c t i o n R e q u e s t + +Routine Description: + Fills the provided buffer with index of interface that needs + to be connected and packet that prompted the request + +Arguments: + index - if index + packet - packet that prompted the request + data - pointer to IPX data (IPX header) inside of the packet + request - request buffer to fill + reqSize - size of request buffer + bytesCopied - bytesCopied into the request buffer + +Return Value: + STATUS_SUCCESS - array was filled successfully + This routine assumes that there it is called only when there + are outstanding requests in the request queue + +--*/ +VOID +FillConnectionRequest ( + IN ULONG index, + IN PNDIS_PACKET packet, + IN PUCHAR data, + IN OUT PFWD_DIAL_REQUEST request, + IN ULONG reqSize, + OUT PULONG bytesCopied + ) { + PNDIS_BUFFER buf; + + *bytesCopied = 0; + request->IfIndex = index; + NdisQueryPacket (packet, NULL, NULL, &buf, NULL); + do { + PVOID va; + UINT length; + + NdisQueryBuffer (buf, &va, &length); + if (((PUCHAR)va<=data) + && ((PUCHAR)va+length>data)) { + TdiCopyMdlToBuffer (buf, + data-(PUCHAR)va, + request, + FIELD_OFFSET (FWD_DIAL_REQUEST, Packet), + reqSize, + bytesCopied); + *bytesCopied += FIELD_OFFSET (FWD_DIAL_REQUEST, Packet); + break; + } + NdisGetNextBuffer (buf, &buf); + } + while (buf!=NULL); +} + +/*++ + F a i l C o n n e c t i o n R e q u e s t s + +Routine Description: + Cleans up on connection request failure + +Arguments: + InterfaceIndex - index of interface that could not be connected + +Return Value: + STATUS_SUCCESS - clean up was successfull + STATUS_UNSUCCESSFUL - interface with this index does not exist + +--*/ +NTSTATUS +FailConnectionRequest ( + IN ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + + ASSERT (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX); + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + IpxFwdDbgPrint (DBG_DIALREQS, DBG_WARNING, + ("IpxFwd: Dial request failed for if %ld (icb:%08lx).\n", + ifCB->ICB_Index, ifCB)); + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + if (IS_IF_CONNECTING (ifCB)) { + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + } + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + diff --git a/private/ntos/tdi/isn/fwd/ddreqs.h b/private/ntos/tdi/isn/fwd/ddreqs.h new file mode 100644 index 000000000..416091c92 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/ddreqs.h @@ -0,0 +1,149 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\ddreqs.h + +Abstract: + Management of demand dial request queues + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ +#ifndef _IPXFWD_DDREQS_ +#define _IPXFWD_DDREQS_ + +// Connection requests to DIM +// Queue of request that need to be satisfied by DIM +extern LIST_ENTRY ConnectionRequestQueue; +// Queue of request IRPs posted by the router manager +extern LIST_ENTRY ConnectionIrpQueue; + +/*++ + I n i t i a l i z e C o n n e c t i o n Q u e u e s + +Routine Description: + Initializes connection request and irp queues + +Arguments: + None + +Return Value: + None + +--*/ +//VOID +//InitializeConnectionQueues ( +// void +// ); +#define InitializeConnectionQueues() { \ + InitializeListHead (&ConnectionIrpQueue); \ + InitializeListHead (&ConnectionRequestQueue); \ +} + +/*++ + F i l l C o n n e c t i o n R e q u e s t + +Routine Description: + Fills the provided buffer with index of interface that needs + to be connected and packet that prompted the request + +Arguments: + index - if index + packet - packet that prompted the request + data - pointer to IPX data (IPX header) inside of the packet + request - request buffer to fill + reqSize - size of request buffer + bytesCopied - bytesCopied into the request buffer + +Return Value: + STATUS_SUCCESS - array was filled successfully + This routine assumes that there it is called only when there + are outstanding requests in the request queue + +--*/ +VOID +FillConnectionRequest ( + IN ULONG index, + IN PNDIS_PACKET packet, + IN PUCHAR data, + IN OUT PFWD_DIAL_REQUEST request, + IN ULONG reqSize, + OUT PULONG bytesCopied + ); + +/*++ + F a i l C o n n e c t i o n R e q u e s t s + +Routine Description: + Cleans up on connection request failure + +Arguments: + InterfaceIndex - index of interface that could not be connected + +Return Value: + STATUS_SUCCESS - clean up was successfull + STATUS_UNSUCCESSFUL - interface with this index does not exist + +--*/ +NTSTATUS +FailConnectionRequest ( + IN ULONG InterfaceIndex + ); + +/*++ + Q u e u e C o n n e c t i o n R e q u e s t + +Routine Description: + Adds request to connected the interface to the queue + +Arguments: + ifCB - control block of the interface that needs to be + connected + packet - packet that prompted the connection request + data - pointer to actual data in the packet + oldIRQL - IRQL at which interface lock was acquired + +Return Value: + None + + Note that interface lock must be acquired before calling this + routine which will release it + +--*/ +VOID +QueueConnectionRequest ( + PINTERFACE_CB ifCB, + PNDIS_PACKET packet, + PUCHAR data, + KIRQL oldIRQL + ); + +/*++ + D e q u e u e C o n n e c t i o n R e q u e s t + +Routine Description: + Removes conection requset for the interface from the queue + +Arguments: + ifCB - control block of the interface that needs to be + removed + +Return Value: + None + +--*/ +VOID +DequeueConnectionRequest ( + PINTERFACE_CB ifCB + ); + +#endif + diff --git a/private/ntos/tdi/isn/fwd/debug.c b/private/ntos/tdi/isn/fwd/debug.c new file mode 100644 index 000000000..9fa2f3f1c --- /dev/null +++ b/private/ntos/tdi/isn/fwd/debug.c @@ -0,0 +1,7 @@ +#include "precomp.h" + +#if DBG +ULONG DbgLevel = DEF_DBG_LEVEL; +LONGLONG ActivityTreshhold = _I64_MAX; +LARGE_INTEGER CounterFrequency; +#endif diff --git a/private/ntos/tdi/isn/fwd/debug.h b/private/ntos/tdi/isn/fwd/debug.h new file mode 100644 index 000000000..251d23895 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/debug.h @@ -0,0 +1,69 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: debug.h +// +// Description: Debug macros definitions +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _IPXFWD_DEBUG_ +#define _IPXFWD_DEBUG_ + +#if DBG +#define DBG_PACKET_ALLOC ((ULONG)0x00000001) +#define DBG_INTF_TABLE ((ULONG)0x00000002) +#define DBG_ROUTE_TABLE ((ULONG)0x00000004) +#define DBG_NBROUTE_TABLE ((ULONG)0x00000008) +#define DBG_IOCTLS ((ULONG)0x00000010) +#define DBG_LINEIND ((ULONG)0x00000020) +#define DBG_IPXBIND ((ULONG)0x00000040) +#define DBG_REGISTRY ((ULONG)0x00000080) +#define DBG_INT_RECV ((ULONG)0x00000100) +#define DBG_RECV ((ULONG)0x00000200) +#define DBG_SEND ((ULONG)0x00000400) +#define DBG_INT_SEND ((ULONG)0x00000800) +#define DBG_NETBIOS ((ULONG)0x00001000) +#define DBG_IPXROUTE ((ULONG)0x00002000) +#define DBG_DIALREQS ((ULONG)0x00004000) +#define DBG_SPOOFING ((ULONG)0x00008000) + +#define DBG_INFORMATION ((ULONG)0x10000000) +#define DBG_WARNING ((ULONG)0x20000000) +#define DBG_ERROR ((ULONG)0x40000000) + +#define DEF_DBG_LEVEL ( \ + DBG_ERROR|DBG_WARNING \ + | DBG_INTF_TABLE \ + | DBG_LINEIND \ + | DBG_IPXBIND \ + | DBG_REGISTRY \ + | DBG_IPXROUTE \ + | DBG_IOCTLS \ + | DBG_DIALREQS \ + | DBG_SPOOFING \ + ) + +extern ULONG DbgLevel; +extern LONGLONG ActivityTreshhold; +extern LARGE_INTEGER CounterFrequency; + +#define IpxFwdDbgPrint(COMPONENT,LEVEL,ARGS) \ + do { \ + if ((DbgLevel & ((COMPONENT)|(LEVEL)))==((COMPONENT)|(LEVEL))){ \ + DbgPrint ARGS; \ + } \ + } while (0) + +#else +#define IpxFwdDbgPrint(COMPONENT,LEVEL,ARGS) do {NOTHING;} while (0) +#endif + +#endif diff --git a/private/ntos/tdi/isn/fwd/dirs b/private/ntos/tdi/isn/fwd/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/fwd/driver.c b/private/ntos/tdi/isn/fwd/driver.c new file mode 100644 index 000000000..ac03cb31e --- /dev/null +++ b/private/ntos/tdi/isn/fwd/driver.c @@ -0,0 +1,1157 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\driver.c + +Abstract: + IPX Forwarder driver dispatch routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + +const UCHAR BROADCAST_NODE[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +const LONGLONG WaitTimeout = -50000000i64; +volatile BOOLEAN IpxFwdInitialized = FALSE; + +BOOLEAN MeasuringPerformance = FALSE; +KSPIN_LOCK PerfCounterLock; +FWD_PERFORMANCE PerfBlock; + +LONG ClientCount = 0; +KEVENT ClientsGoneEvent; + +PFILE_OBJECT RouterFile, FilterFile; + +NTSTATUS +IpxFwdDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +IpxFwdUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +DoStart ( + IN ULONG RouteHashTableSize, + IN BOOLEAN thisMachineOnly + ); + +NTSTATUS +DoStop ( + void + ); + +NTSTATUS +DoSetInterface ( + IN ULONG InterfaceIndex, + IN BOOLEAN NetbiosAccept, + IN UCHAR NetbiosDeliver + ); + +NTSTATUS +DoGetInterface ( + IN ULONG InterfaceIndex, + OUT PFWD_IF_STATS stats, + OUT BOOLEAN *NetbiosAccept, + OUT UCHAR *NetbiosDeliver + ); + +NTSTATUS +DoSetNbNames ( + IN ULONG InterfaceIndex, + IN ULONG Count, + IN PFWD_NB_NAME Names + ); + +NTSTATUS +DoGetNbNames ( + IN ULONG InterfaceIndex, + IN OUT ULONG *BufferSize, + OUT ULONG *Count, + OUT PFWD_NB_NAME Names + ); + +NTSTATUS +DoBindInterface ( + IN ULONG InterfaceIndex, + IN PFWD_ADAPTER_BINDING_INFO info + ); + +NTSTATUS +DoUnbindInterface ( + IN ULONG InterfaceIndex + ); + +NTSTATUS +DoDisableInterface ( + IN ULONG InterfaceIndex + ); + +NTSTATUS +DoEnableInterface ( + IN ULONG InterfaceIndex + ); + +NTSTATUS +DoSetRoutes ( + IN PFWD_ROUTE_SET_PARAMS routeArray, + IN ULONG nRoutes, + OUT PULONG nProcessed + ); + +VOID +IpxFwdCancel ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP irp + ); + +NTSTATUS +DoGetPerfCounters ( + PFWD_PERFORMANCE_PARAMS perfParams + ); + +/*++ + D r i v e r E n t r y + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path + to driver-specific key in the registry + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) { + + PDEVICE_OBJECT deviceObject = NULL; + NTSTATUS status; + WCHAR deviceNameBuffer[] = IPXFWD_NAME; + UNICODE_STRING deviceNameUnicodeString; + + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, + ("IpxFwd: Entering DriverEntry\n")); + + // + // Create an non-EXCLUSIVE device object + // + + RtlInitUnicodeString (&deviceNameUnicodeString, + deviceNameBuffer); + + status = IoCreateDevice (DriverObject, + 0, + &deviceNameUnicodeString, + FILE_DEVICE_IPXFWD, + 0, + FALSE, // Non-Exclusive + &deviceObject + ); + + if (NT_SUCCESS(status)) { + // + // Create dispatch points for device control, create, close. + // + GetForwarderParameters (RegistryPath); + DriverObject->MajorFunction[IRP_MJ_CREATE] + = DriverObject->MajorFunction[IRP_MJ_CLEANUP] + = DriverObject->MajorFunction[IRP_MJ_CLOSE] + = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] + = IpxFwdDispatch; + DriverObject->DriverUnload = IpxFwdUnload; + status = BindToIpxDriver (KernelMode); + if (NT_SUCCESS (status)) { + +#if DBG + KeQueryPerformanceCounter (&CounterFrequency); +#endif + FilterFile = RouterFile = NULL; + ClientCount = 0; + return STATUS_SUCCESS; + } + IoDeleteDevice (DriverObject->DeviceObject); + } + else + IpxFwdDbgPrint (DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: IoCreateDevice failed\n")); + + return status; +} + + + +/*++ + +Routine Description: + + Process the IRPs sent to this device. + +Arguments: + + DeviceObject - pointer to a device object + + Irp - pointer to an I/O Request Packet + +Return Value: + + +--*/ +NTSTATUS +IpxFwdDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) { + PIO_STACK_LOCATION IrpStack; + PVOID inBuffer, outBuffer; + ULONG inpBufLength; + ULONG outBufLength; + NTSTATUS status; + KIRQL cancelIRQL; + + + Irp->IoStatus.Information = 0; + status = STATUS_SUCCESS; + + // + // Get a pointer to the current location in the Irp. This is where + // the function codes and parameters are located. + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + + switch (IrpStack->MajorFunction) { + case IRP_MJ_CREATE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, ("IpxFwd: IRP_MJ_CREATE\n")); + break; + + case IRP_MJ_CLOSE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, ("IpxFwd: IRP_MJ_CLOSE\n")); + if (EnterForwarder ()) { + if (IrpStack->FileObject==RouterFile) { + LeaveForwarder (); + IpxFwdInitialized = FALSE; + while (InterlockedDecrement (&ClientCount)>=0) { + KeWaitForSingleObject (&ClientsGoneEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)&WaitTimeout); + InterlockedIncrement (&ClientCount); + IpxFwdDbgPrint(DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: Waiting for all clients (%ld) to exit.\n", + ClientCount)); + } + status = DoStop (); + ClientCount = 0; + RouterFile = NULL; + } + else if (IrpStack->FileObject==FilterFile) { + UnbindFilterDriver (); + FilterFile = NULL; + LeaveForwarder (); + } + else + LeaveForwarder (); + } + break; + + case IRP_MJ_CLEANUP: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, ("IpxFwd: IRP_MJ_CLEANUP\n")); + if (EnterForwarder ()) { + if (IrpStack->FileObject==RouterFile) { + IoAcquireCancelSpinLock (&cancelIRQL); + while (!IsListEmpty (&ConnectionIrpQueue)) { + PIRP irp = CONTAINING_RECORD (ConnectionIrpQueue.Blink, + IRP, Tail.Overlay.ListEntry); + irp->Cancel = TRUE; + irp->CancelIrql = cancelIRQL; + irp->CancelRoutine = NULL; + IpxFwdCancel(DeviceObject, irp); + IoAcquireCancelSpinLock (&cancelIRQL); + } + IoReleaseCancelSpinLock(cancelIRQL); + } + LeaveForwarder (); + } + break; + + case IRP_MJ_DEVICE_CONTROL: + // + // Get the pointer to the input/output buffer and it's length + // + + status = STATUS_INVALID_PARAMETER; + inpBufLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + outBufLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + switch (IrpStack->Parameters.DeviceIoControl.IoControlCode&3) { + case METHOD_BUFFERED: + inBuffer = outBuffer = Irp->AssociatedIrp.SystemBuffer; + break; + + case METHOD_IN_DIRECT: + case METHOD_OUT_DIRECT: + inBuffer = Irp->AssociatedIrp.SystemBuffer; + if (outBufLength>0) { + outBuffer = MmGetSystemAddressForMdl (Irp->MdlAddress); + } + else { + outBuffer = NULL; + IpxFwdDbgPrint(DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: IOCTL...METHOD_DIRECT with 0 output buffer ???\n")); + } + break; + default: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: IOCTL...METHOD_NEITHER ???\n")); + goto DispatchExit; + } + + + if (EnterForwarder ()) { + if (IrpStack->FileObject==RouterFile) { + switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_FWD_SET_ROUTES: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_SET_ROUTES\n")); + if (inpBufLength>=sizeof (FWD_ROUTE_SET_PARAMS)) + status = DoSetRoutes ( + (PFWD_ROUTE_SET_PARAMS)inBuffer, + inpBufLength/sizeof(FWD_ROUTE_SET_PARAMS), + &Irp->IoStatus.Information); + + break; + + case IOCTL_FWD_SET_NB_NAMES: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_SET_NB_NAMES\n")); + if (inpBufLength==sizeof (ULONG)) + status = DoSetNbNames ( + *((PULONG)inBuffer), + outBufLength/sizeof (FWD_NB_NAME), + (PFWD_NB_NAME)outBuffer); + break; + + case IOCTL_FWD_RESET_NB_NAMES: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_RESET_NB_NAMES\n")); + if (inpBufLength==sizeof (ULONG)) + status = DoSetNbNames (*((PULONG)inBuffer), 0, NULL); + + break; + case IOCTL_FWD_GET_NB_NAMES: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_GET_NB_NAMES\n")); + if ((inpBufLength==sizeof (ULONG)) + && (outBufLength>=sizeof(ULONG))) { + Irp->IoStatus.Information = outBufLength + -FIELD_OFFSET (FWD_NB_NAMES_PARAMS, Names); + status = DoGetNbNames ( + *((PULONG)inBuffer), + &Irp->IoStatus.Information, + &((PFWD_NB_NAMES_PARAMS)outBuffer)->TotalCount, + ((PFWD_NB_NAMES_PARAMS)outBuffer)->Names); + if (NT_SUCCESS (status)) { + Irp->IoStatus.Information += FIELD_OFFSET ( + FWD_NB_NAMES_PARAMS, Names); + } + } + break; + + case IOCTL_FWD_CREATE_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_CREATE_INTERFACE\n")); + if (inpBufLength==sizeof(FWD_IF_CREATE_PARAMS)) + status = AddInterface ( + ((PFWD_IF_CREATE_PARAMS)inBuffer)->Index, + ((PFWD_IF_CREATE_PARAMS)inBuffer)->InterfaceType, + ((PFWD_IF_CREATE_PARAMS)inBuffer)->NetbiosAccept, + ((PFWD_IF_CREATE_PARAMS)inBuffer)->NetbiosDeliver); + break; + + case IOCTL_FWD_DELETE_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_DELETE_INTERFACE\n")); + if (inpBufLength==sizeof(ULONG)) + status = DeleteInterface ( + *((PULONG)inBuffer)); + + break; + + case IOCTL_FWD_SET_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_SET_INTERFACE\n")); + if (inpBufLength==sizeof(FWD_IF_SET_PARAMS)) + status = DoSetInterface ( + ((PFWD_IF_SET_PARAMS)inBuffer)->Index, + ((PFWD_IF_SET_PARAMS)inBuffer)->NetbiosAccept, + ((PFWD_IF_SET_PARAMS)inBuffer)->NetbiosDeliver); + break; + + case IOCTL_FWD_GET_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_GET_INTERFACE\n")); + if ((inpBufLength==sizeof(ULONG)) + && (outBufLength==sizeof(FWD_IF_GET_PARAMS))) { + status = DoGetInterface ( + *((PULONG)inBuffer), + &((PFWD_IF_GET_PARAMS)outBuffer)->Stats, + &((PFWD_IF_GET_PARAMS)outBuffer)->NetbiosAccept, + &((PFWD_IF_GET_PARAMS)outBuffer)->NetbiosDeliver); + if (NT_SUCCESS (status)) + Irp->IoStatus.Information = sizeof(FWD_IF_GET_PARAMS); + } + break; + + case IOCTL_FWD_BIND_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_BIND_INTERFACE\n")); + if (inpBufLength==sizeof(FWD_IF_BIND_PARAMS)) + status = DoBindInterface ( + ((PFWD_IF_BIND_PARAMS)inBuffer)->Index, + &((PFWD_IF_BIND_PARAMS)inBuffer)->Info); + break; + + case IOCTL_FWD_UNBIND_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_UNBIND_INTERFACE\n")); + if (inpBufLength==sizeof(ULONG)) + status = DoUnbindInterface (*((PULONG)inBuffer)); + break; + + case IOCTL_FWD_GET_DIAL_REQUEST: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_GET_DIAL_REQUEST\n")); + if (outBufLength>=sizeof (ULONG)) { + IoAcquireCancelSpinLock (&cancelIRQL); + if (!IsListEmpty (&ConnectionRequestQueue)) { + PINTERFACE_CB ifCB = CONTAINING_RECORD ( + ConnectionRequestQueue.Flink, + INTERFACE_CB, + ICB_ConnectionLink); + RemoveEntryList (&ifCB->ICB_ConnectionLink); + InitializeListEntry (&ifCB->ICB_ConnectionLink); + IoReleaseCancelSpinLock (cancelIRQL); + KeAcquireSpinLock (&ifCB->ICB_Lock, &cancelIRQL); + FillConnectionRequest ( + ifCB->ICB_Index, + ifCB->ICB_ConnectionPacket, + ifCB->ICB_ConnectionData, + (PFWD_DIAL_REQUEST)outBuffer, + outBufLength, + &Irp->IoStatus.Information); + status = STATUS_SUCCESS; + KeReleaseSpinLock (&ifCB->ICB_Lock, cancelIRQL); + } + else { + InsertTailList (&ConnectionIrpQueue, + &Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine (Irp, IpxFwdCancel); + IoReleaseCancelSpinLock (cancelIRQL); + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = status = STATUS_PENDING; + } + } + break; + case IOCTL_FWD_DIAL_REQUEST_FAILED: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_DIAL_REQUEST_FAILED\n")); + if (inpBufLength==sizeof (ULONG)) + status = FailConnectionRequest ( + *((PULONG)inBuffer)); + break; + case IOCTL_FWD_DISABLE_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_DISABLE_INTERFACE\n")); + if (inpBufLength==sizeof (ULONG)) + status = DoDisableInterface ( + *((PULONG)inBuffer)); + break; + case IOCTL_FWD_ENABLE_INTERFACE: + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_ENABLE_INTERFACE\n")); + if (inpBufLength==sizeof (ULONG)) + status = DoEnableInterface ( + *((PULONG)inBuffer)); + break; + default: + IpxFwdDbgPrint (DBG_IOCTLS, DBG_WARNING, ("IpxFwd: unknown IRP_MJ_DEVICE_CONTROL\n")); + break; + + } + } + else if (IrpStack->Parameters.DeviceIoControl.IoControlCode + ==IOCTL_FWD_INTERNAL_BIND_FILTER) { + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_INTERNAL_BIND_FILTER\n")); + if ((inpBufLength==sizeof (IPX_FLT_BIND_INPUT)) + && (outBufLength>=sizeof (ULONG))) { + if (outBufLength>=sizeof (IPX_FLT_BIND_OUTPUT)) { + BindFilterDriver ( + (PIPX_FLT_BIND_INPUT)inBuffer, + (PIPX_FLT_BIND_OUTPUT)outBuffer); + Irp->IoStatus.Information = sizeof (IPX_FLT_BIND_OUTPUT); + FilterFile = IrpStack->FileObject; + status = STATUS_SUCCESS; + } + else { + IPX_FLT_BIND_OUTPUT bindOutput; + BindFilterDriver ( + (PIPX_FLT_BIND_INPUT)inBuffer, + &bindOutput); + memcpy (outBuffer, &bindOutput, outBufLength); + Irp->IoStatus.Information = outBufLength; + status = STATUS_BUFFER_OVERFLOW; + } + } + } + else if (IrpStack->Parameters.DeviceIoControl.IoControlCode + ==IOCTL_FWD_GET_PERF_COUNTERS) { + IpxFwdDbgPrint(DBG_IOCTLS, DBG_INFORMATION, ("IpxFwd: IOCTL_FWD_GET_PERF_COUNTERS\n")); + if (outBufLength==sizeof (FWD_PERFORMANCE_PARAMS)) + status = DoGetPerfCounters ( + ((PFWD_PERFORMANCE_PARAMS)outBuffer)); + } + else { + status = STATUS_ACCESS_DENIED; + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, + ("IpxFwd: IOCTL: %08lx on non-router file object!\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode)); + } + LeaveForwarder (); + } else { + if (IrpStack->Parameters.DeviceIoControl.IoControlCode==IOCTL_FWD_START) { + IpxFwdDbgPrint (DBG_IOCTLS, DBG_WARNING, + ("IpxFwd: IOCTL_FWD_START\n")); + if (inpBufLength==sizeof (FWD_START_PARAMS)) { + KeInitializeEvent (&ClientsGoneEvent, + SynchronizationEvent, + FALSE); + status = DoStart ( + ((PFWD_START_PARAMS)inBuffer)->RouteHashTableSize, + ((PFWD_START_PARAMS)inBuffer)->ThisMachineOnly); + if (NT_SUCCESS (status)) { + RouterFile = IrpStack->FileObject; + IpxFwdInitialized = TRUE; + } + } + } + else { + IpxFwdDbgPrint (DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: IOCTL: %08lx but fwd not started.\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode)); + } + } + break; + default: + IpxFwdDbgPrint (DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: unknown MajorFunction.\n")); + break; + } + +DispatchExit: + if (status!=STATUS_PENDING) { + IpxFwdDbgPrint(DBG_IOCTLS, + NT_ERROR(status) ? DBG_WARNING : DBG_INFORMATION, + ("IpxFwd: completing IOCTL %08lx with status %08lx.\n", + IrpStack->Parameters.DeviceIoControl.IoControlCode, + status)); + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return status; +} + + + +/*++ + +Routine Description: + Cleans up on driver unload + +Arguments: + + DriverObject - pointer to a driver object + +Return Value: + + +--*/ +VOID +IpxFwdUnload( + IN PDRIVER_OBJECT DriverObject + ) { + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, ("IpxFwd: unloading\n")); + if (EnterForwarder ()) { + LeaveForwarder (); + IpxFwdInitialized = FALSE; + while (InterlockedDecrement (&ClientCount)>=0) { + KeWaitForSingleObject (&ClientsGoneEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)&WaitTimeout); + InterlockedIncrement (&ClientCount); + IpxFwdDbgPrint(DBG_IOCTLS, DBG_ERROR, + ("IpxFwd: Waiting for all clients (%ld) to exit.\n", + ClientCount)); + } + DoStop (); + } + + UnbindFromIpxDriver (KernelMode); + IoDeleteDevice (DriverObject->DeviceObject); +} + + + +/*++ + D o S t a r t + +Routine Description: + Initializes all driver components and binds to IPX + stack driver at strat up + +Arguments: + RouteHashTableSize - size of route hash table + thisMachineOnly - whether to forward dialin client packets + to other dests on the net + +Return Value: + STATUS_SUCCESS - initialization succeded + STATUS_UNSUCCESSFULL - failure + +--*/ +NTSTATUS +DoStart ( + IN ULONG RouteHashTableSize, + IN BOOLEAN thisMachineOnly + ) { + NTSTATUS status; + + InitializeConnectionQueues (); + RouteHashSize = RouteHashTableSize; + status = CreateTables (); + if (NT_SUCCESS (status)) { + InitializePacketAllocator (); + InitializeNetbiosQueue (); + InitializeRecvQueue (); + InitializeSendQueue (); + MeasuringPerformance = FALSE; + KeInitializeSpinLock (&PerfCounterLock); + ThisMachineOnly = thisMachineOnly; + + return STATUS_SUCCESS; + } + return status; +} + +/*++ + D o S t o p + +Routine Description: + Cleans up allocated resources and unbinds from IPX stack + driver when forwarder is stopped + +Arguments: + None + +Return Value: + STATUS_SUCCESS - cleanup succeded + +--*/ +NTSTATUS +DoStop ( + void + ) { + if (FilterFile!=NULL) { + UnbindFilterDriver (); + FilterFile = NULL; + } + DeleteSendQueue (); + DeleteRecvQueue (); + DeleteNetbiosQueue (); + DeleteTables (); // Unbinds all bound interfaces + if (WanPacketListId!=-1) { + DeregisterPacketConsumer (WanPacketListId); + WanPacketListId = -1; + } + DeletePacketAllocator (); + return STATUS_SUCCESS; +} + +/*++ + D o S e t R o u t e s + +Routine Description: + Updates route table with supplied routes + +Arguments: + routeArray - array of routes to add/de;ete/update + nRoutes - number of routes in the array + nProcessed - number of routes that were processed successfully + +Return Value: + STATUS_SUCCESS - all routes were processed ok + error status - reason of failure for the first unprocessed route + +--*/ +NTSTATUS +DoSetRoutes ( + IN PFWD_ROUTE_SET_PARAMS routeArray, + IN ULONG nRoutes, + OUT PULONG nProcessed + ) { + NTSTATUS status=STATUS_SUCCESS; + UINT i; + + for (i=0; iAction) { + case FWD_ADD_ROUTE: + status = AddRoute (routeArray->Network, + routeArray->NextHopAddress, + routeArray->TickCount, + routeArray->HopCount, + routeArray->InterfaceIndex); + break; + case FWD_DELETE_ROUTE: + status = DeleteRoute (routeArray->Network); + break; + case FWD_UPDATE_ROUTE: + status = UpdateRoute (routeArray->Network, + routeArray->NextHopAddress, + routeArray->TickCount, + routeArray->HopCount, + routeArray->InterfaceIndex); + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + if (!NT_SUCCESS (status)) + break; + } + *nProcessed = i; + return status; +} + + +/*++ + D o S e t N b N a m e s + +Routine Description: + Sets static Netbios Names on the interface + +Arguments: + InterfaceIndex - index oc interface on which to set names + Count - number of names to set + Names - array of netbios names + +Return Value: + STATUS_SUCCESS - names were set OK + STATUS_UNSUCCESSFULL - interface does not exist + STATUS_INSUFFICIENT_RESOURCES - not enough resources to complete + the operation + +--*/ +NTSTATUS +DoSetNbNames ( + IN ULONG InterfaceIndex, + IN ULONG Count, + IN PFWD_NB_NAME Names + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + PNB_ROUTE nbRoutes; + NTSTATUS status=STATUS_SUCCESS; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + if (ifCB->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (ifCB->ICB_NBRoutes, ifCB->ICB_NBRouteCount); + ifCB->ICB_NBRoutes = NULL; + ifCB->ICB_NBRouteCount = 0; + } + if (Count>0) { + status = AddNBRoutes (ifCB, Names, Count, &nbRoutes); + if (NT_SUCCESS (status)) { + ifCB->ICB_NBRoutes = nbRoutes; + ifCB->ICB_NBRouteCount = Count; + } + } + ReleaseInterfaceReference (ifCB); + } + else + status = STATUS_UNSUCCESSFUL; + + return status; +} + + +/*++ + D o G e t N b N a m e s + +Routine Description: + Gets all static Netbios Names on the interface + +Arguments: + InterfaceIndex - index of interface from which to get names + ArraySize - on input: size of the buffer to put names into + on output: size of data put into the array + Names - buffer to put names into names, if buffer + is to small to hold all names, this orutine stuffs + total number of names into the first ULONG in the + array (this is the only way to return in to the + caller through the IOCTL interface) + +Return Value: + STATUS_SUCCESS - names were copied into the array + STATUS_UNSUCCESSFULL - interface does not exist + STATUS_BUFFER_OVERFLOW - buffer is too small to copy all the + names, number of names are in the first ULONG of + the buffer + +--*/ +NTSTATUS +DoGetNbNames ( + IN ULONG InterfaceIndex, + IN OUT ULONG *ArraySize, + OUT ULONG *TotalCount, + OUT PFWD_NB_NAME Names + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + NTSTATUS status=STATUS_SUCCESS; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + if (ifCB->ICB_NBRoutes!=NULL) { + ULONG i; + PFWD_NB_NAME nameLM = Names+(*ArraySize/sizeof(FWD_NB_NAME)); + for (i=0; (iICB_NBRouteCount)&&(NamesICB_NBRoutes[i].NBR_Name); + *ArraySize = sizeof (FWD_NB_NAME)*i; + *TotalCount = ifCB->ICB_NBRouteCount; + } + else { + *ArraySize = 0; + *TotalCount = 0; + } + ReleaseInterfaceReference (ifCB); + } + else + status = STATUS_UNSUCCESSFUL; + + return status; +} + + +/*++ + D o S e t I n t e r f a c e + +Routine Description: + Sets interface configurable parameters + +Arguments: + InterfaceIndex - index of interface to set + NetbiosAccept - whether to accept nb packets on the interface + NetbiosDeliver - whether to deliver nb packets on the interface + +Return Value: + STATUS_SUCCESS - interface was set OK + STATUS_UNSUCCESSFULL - interface does not exist + +--*/ +NTSTATUS +DoSetInterface ( + IN ULONG InterfaceIndex, + IN BOOLEAN NetbiosAccept, + IN UCHAR NetbiosDeliver + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + ifCB->ICB_NetbiosAccept = NetbiosAccept; + ifCB->ICB_NetbiosDeliver = NetbiosDeliver; + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + + +/*++ + D o G e t I n t e r f a c e + +Routine Description: + Gets interface configurable parameters and statistics + +Arguments: + InterfaceIndex - index of interface to query + stats - interface statistics + NetbiosAccept - whether nb packets accepter on the interface + NetbiosDeliver - whether nb packets delivered on the interface + +Return Value: + STATUS_SUCCESS - interface data was queried OK + STATUS_UNSUCCESSFULL - interface does not exist + +--*/ +NTSTATUS +DoGetInterface ( + IN ULONG InterfaceIndex, + OUT PFWD_IF_STATS stats, + OUT BOOLEAN *NetbiosAccept, + OUT UCHAR *NetbiosDeliver + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + *NetbiosAccept = ifCB->ICB_NetbiosAccept; + *NetbiosDeliver = ifCB->ICB_NetbiosDeliver; + IF_STATS_CPY (stats, &ifCB->ICB_Stats); + if (!IS_IF_ENABLED(ifCB)) + stats->OperationalState = FWD_OPER_STATE_DOWN; + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + +/*++ + D o B i n d I n t e r f a c e + +Routine Description: + Binds interface to the specified adapter and sets binding + parameters + +Arguments: + InterfaceIndex - index of interface to bind + info - binding info + +Return Value: + STATUS_SUCCESS - interface was bound OK + STATUS_UNSUCCESSFULL - interface does not exist or could not be + bound +--*/ +NTSTATUS +DoBindInterface ( + IN ULONG InterfaceIndex, + IN PFWD_ADAPTER_BINDING_INFO info + ) { + PINTERFACE_CB ifCB; + NTSTATUS status; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + if (ifCB->ICB_InterfaceType==FWD_IF_PERMANENT) + status = BindInterface (ifCB, + (USHORT)info->AdapterIndex, + info->MaxPacketSize, + info->Network, + info->LocalNode, + info->RemoteNode); + else + status = STATUS_SUCCESS; + ReleaseInterfaceReference (ifCB); + return status; + } + else + return STATUS_UNSUCCESSFUL; +} + + +/*++ + D o U n b i n d I n t e r f a c e + +Routine Description: + Unbinds interface from the adapter and invalidates binding + parameters + +Arguments: + InterfaceIndex - index of interface to unbind + +Return Value: + STATUS_SUCCESS - interface was unbound OK + STATUS_UNSUCCESSFULL - interface does not exist +--*/ +NTSTATUS +DoUnbindInterface ( + IN ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + if (ifCB->ICB_InterfaceType==FWD_IF_PERMANENT) + UnbindInterface (ifCB); + + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + +/*++ + D o D i s a b l e I n t e r f a c e + +Routine Description: + Disables all packet traffic through the interface + +Arguments: + InterfaceIndex - index of interface to disable + +Return Value: + STATUS_SUCCESS - interface was disabled OK + STATUS_UNSUCCESSFULL - interface does not exist +--*/ +NTSTATUS +DoDisableInterface ( + IN ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + KIRQL oldIRQL; + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + if (IS_IF_ENABLED (ifCB)) { + SET_IF_DISABLED (ifCB); + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + } + else + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + + +/*++ + D o E n a b l e I n t e r f a c e + +Routine Description: + Enables all packet traffic through the interface + +Arguments: + InterfaceIndex - index of interface to enable + +Return Value: + STATUS_SUCCESS - interface was disabled OK + STATUS_UNSUCCESSFULL - interface does not exist +--*/ +NTSTATUS +DoEnableInterface ( + IN ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + SET_IF_ENABLED (ifCB); + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; + } + else + return STATUS_UNSUCCESSFUL; +} + + + +/*++ + I p x F w d C a n c e l + +Routine Description: + Cancels specified IRP + +Arguments: + DeviceObject - forwarder device object + irp - irp to cancel + +Return Value: + None +--*/ +VOID +IpxFwdCancel ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP irp + ) { + RemoveEntryList (&irp->Tail.Overlay.ListEntry); + IoReleaseCancelSpinLock (irp->CancelIrql); + + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_CANCELLED; + IpxFwdDbgPrint(DBG_IOCTLS, DBG_WARNING, ("IpxFwd: completing cancelled irp.\n")); + IoCompleteRequest(irp, IO_NO_INCREMENT); +} + +/*++ + D o G e t P e r f C o u n t e r s + +Routine Description: + Gets performance counters + +Arguments: + perfParams - buffer ot pu counters into + +Return Value: + STATUS_SUCCESS - counter were copied ok + STATUS_UNSUCCESSFULL - performance measurement were not enabled +--*/ +NTSTATUS +DoGetPerfCounters ( + OUT PFWD_PERFORMANCE_PARAMS perfParams + ) { + LONGLONG lTotalPacketProcessingTime; + LONGLONG lMaxPacketProcessingTime; + LONG lPacketCounter; + LONGLONG lTotalNbPacketProcessingTime; + LONGLONG lMaxNbPacketProcessingTime; + LONG lNbPacketCounter; + KIRQL oldIRQL; + + if (!MeasuringPerformance) + return STATUS_UNSUCCESSFUL; + + KeAcquireSpinLock (&PerfCounterLock, &oldIRQL); + *perfParams = PerfBlock; + memset (&PerfBlock, 0, sizeof (PerfBlock)); + KeReleaseSpinLock (&PerfCounterLock, oldIRQL); + return STATUS_SUCCESS; +} + + +BOOLEAN +DoLeaveForwarder ( + VOID + ) { + return LeaveForwarder (); +} diff --git a/private/ntos/tdi/isn/fwd/driver.h b/private/ntos/tdi/isn/fwd/driver.h new file mode 100644 index 000000000..ecc298345 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/driver.h @@ -0,0 +1,102 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\driver.h + +Abstract: + IPX Forwarder driver dispatch routines + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + + +#ifndef _IPXFWD_DRIVER_ +#define _IPXFWD_DRIVER_ + +// Pseudo constant 0xFFFFFFFFFFFFF +extern const UCHAR BROADCAST_NODE[6]; + +// Performance measurement: +// Enabling flag +extern BOOLEAN MeasuringPerformance; +// Access control +extern KSPIN_LOCK PerfCounterLock; +// Statistic accumulators (counters) +extern FWD_PERFORMANCE PerfBlock; + +// Access control for external callers (ipx stack, filter driver) +// Flag set upon completion of initialization of all components +extern volatile BOOLEAN IpxFwdInitialized; +// Number of clients executing forwarder code (if -1, the forwarder +// is being stopped) +extern LONG ClientCount; +// Event to be signalled by the last client inside forwarder +extern KEVENT ClientsGoneEvent; + + +/*++ + E n t e r F o r w a r d e r + +Routine Description: + Checks if forwarder is initialized and grants access + to it (records the entrance as well + +Arguments: + None + +Return Value: + TRUE - access granted + FALSE - forwarder is not yet initialized or is being stopped + +--*/ +//BOOLEAN +//EnterForwarder ( +// void +// ); +#define EnterForwarder() ( \ + (InterlockedIncrement(&ClientCount), IpxFwdInitialized) \ + ? TRUE \ + : (DoLeaveForwarder(), FALSE) \ + ) + +/*++ + L e a v e F o r w a r d e r + +Routine Description: + Records the fact that external client stopped using forwarder + +Arguments: + None + +Return Value: + None + +--*/ +//BOOLEAN +//EnterForwarder ( +// void +// ); +#define LeaveForwarder() \ + ((InterlockedDecrement(&ClientCount)<0) \ + ? KeSetEvent (&ClientsGoneEvent,0,FALSE) \ + : 0 \ + ) + +// Same as above but implemented as a routine to be used in +// the EnterForwarder macro above (this reduces code size +// and aids in debugging by improving readability of disassembly +BOOLEAN +DoLeaveForwarder ( + VOID + ); + +#endif diff --git a/private/ntos/tdi/isn/fwd/filterif.c b/private/ntos/tdi/isn/fwd/filterif.c new file mode 100644 index 000000000..f1fa584a3 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/filterif.c @@ -0,0 +1,211 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\filterif.c + +Abstract: + IPX Forwarder driver interface with filter driver + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + // Filter driver entry points +IPX_FLT_BIND_INPUT FltBindInput = {NULL, NULL}; + // Protects access to filter driver contexts +RW_LOCK FltLock; + + +/*++ + D o F i l t e r + +Routine Description: + + Calls filter driver entry point while holding reader access + to interface contexts + +Arguments: + ipxHdr - pointer to packet header + ipxHdrLength - size of the header buffer (must be at least 30) + ifInContext - context associated with interface on which packet + was received + ifOutContext - context associated with interface on which packet + will be sent +Return Value: + FILTER_PERMIT - packet should be passed on by the forwarder + FILTER_DENY - packet should be dropped + +--*/ +FILTER_ACTION +DoFilter ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContex + ) { + RWCOOKIE cookie; + FILTER_ACTION result; + AcquireReaderAccess (&FltLock, cookie); + result = FltBindInput.FilterHandler (ipxHdr, + ipxHdrLength, + ifInContext, + ifOutContex); + ReleaseReaderAccess (&FltLock, cookie); + return result; +} + +/*++ + D o I n t e r f a c e D e l e t e d + +Routine Description: + Resets interface contexts and calls filter dirver entry point + making sure that all no one holds reader access to filter driver + interface contexts +Arguments: + ifCB - interface to be deleted +Return Value: + None + +--*/ +VOID +DoInterfaceDeleted ( + PINTERFACE_CB ifCB + ) { + PVOID inContext = ifCB->ICB_FilterInContext, + outContext = ifCB->ICB_FilterOutContext; + ifCB->ICB_FilterInContext = NO_FILTER_CONTEXT; + ifCB->ICB_FilterOutContext = NO_FILTER_CONTEXT; + WaitForAllReaders (&FltLock); + FltBindInput.InterfaceDeletedHandler(inContext, + outContext); +} + +/*++ + S e t I f I n C o n t e x t + +Routine Description: + Associates filter driver context with + the packets received on the interface +Arguments: + InterfaceIndex - index of the interface + ifInContext - filter driver context +Return Value: + STATUS_SUCCESS - context associated ok + STATUS_UNSUCCESSFUL - interface does not exist +--*/ +NTSTATUS +SetIfInContext ( + IN ULONG InterfaceIndex, + IN PVOID ifInContext + ) { + PINTERFACE_CB ifCB; + NTSTATUS status = STATUS_SUCCESS; + + if (EnterForwarder ()) { + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + ifCB->ICB_FilterInContext = ifInContext; + WaitForAllReaders (&FltLock); + } + else + status = STATUS_UNSUCCESSFUL; + LeaveForwarder (); + } + return status; +} + +/*++ + S e t I f O u t C o n t e x t + +Routine Description: + Associates filter driver context with + the packets sent on the interface +Arguments: + InterfaceIndex - index of the interface + ifOutContext - filter driver context +Return Value: + STATUS_SUCCESS - context associated ok + STATUS_UNSUCCESSFUL - interface does not exist +--*/ +NTSTATUS +SetIfOutContext ( + IN ULONG InterfaceIndex, + IN PVOID ifOutContext + ) { + PINTERFACE_CB ifCB; + NTSTATUS status = STATUS_SUCCESS; + + if (EnterForwarder ()) { + ifCB = GetInterfaceReference (InterfaceIndex); + if (ifCB!=NULL) { + ifCB->ICB_FilterOutContext = ifOutContext; + WaitForAllReaders (&FltLock); + } + else + status = STATUS_UNSUCCESSFUL; + LeaveForwarder (); + } + return status; +} + +/*++ + B i n d F i l t e r D r i v e r + +Routine Description: + Exchanges entry points with filter driver +Arguments: + bindInput - filter driver entry points + bindOutput - forwarder driver entry points +Return Value: + None +--*/ +VOID +BindFilterDriver ( + IN PIPX_FLT_BIND_INPUT bindInput, + OUT PIPX_FLT_BIND_OUTPUT bindOutput + ) { + memcpy (&FltBindInput, bindInput, sizeof (IPX_FLT_BIND_INPUT)); + bindOutput->Size = sizeof (IPX_FLT_BIND_OUTPUT); + bindOutput->SetIfInContextHandler = SetIfInContext; + bindOutput->SetIfOutContextHandler = SetIfOutContext; + InitializeRWLock (&FltLock); +} + +/*++ + U n b i n d F i l t e r D r i v e r + +Routine Description: + Resets locally stored filter driver entry points + and resets filter driver contexts on all interfaces +Arguments: + None +Return Value: + None +--*/ +VOID +UnbindFilterDriver ( + VOID + ) { + PINTERFACE_CB ifCB = NULL; + FltBindInput.FilterHandler = NULL; + FltBindInput.InterfaceDeletedHandler = NULL; + + while ((ifCB=GetNextInterfaceReference (ifCB))!=NULL) { + ifCB->ICB_FilterInContext = NO_FILTER_CONTEXT; + ifCB->ICB_FilterOutContext = NO_FILTER_CONTEXT; + } + InternalInterface->ICB_FilterInContext = NO_FILTER_CONTEXT; + InternalInterface->ICB_FilterOutContext = NO_FILTER_CONTEXT; + WaitForAllReaders (&FltLock); +} + diff --git a/private/ntos/tdi/isn/fwd/filterif.h b/private/ntos/tdi/isn/fwd/filterif.h new file mode 100644 index 000000000..0e09df1de --- /dev/null +++ b/private/ntos/tdi/isn/fwd/filterif.h @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\filterif.h + +Abstract: + IPX Forwarder interface with filter driver + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + + +#ifndef _IPXFWD_FILTERIF_ +#define _IPXFWD_FILTERIF_ + // Filter driver entry points +extern IPX_FLT_BIND_INPUT FltBindInput; + + // Macros to improve performance when filter driver + // is not bound or has not associated its contexts + // with interfaces of interest +#define FltFilter(hdr,hdrSize,inContext,outContext) ( \ + ((FltBindInput.FilterHandler==NULL) \ + || ((inContext==NO_FILTER_CONTEXT) \ + &&(outContext==NO_FILTER_CONTEXT))) \ + ? FILTER_PERMIT \ + : DoFilter (hdr,hdrSize,inContext,outContext) \ +) + +#define FltInterfaceDeleted(ifCB) { \ + if ((FltBindInput.InterfaceDeletedHandler!=NULL) \ + && ((ifCB->ICB_FilterInContext!=NO_FILTER_CONTEXT) \ + ||(ifCB->ICB_FilterOutContext!=NO_FILTER_CONTEXT))) \ + DoInterfaceDeleted (ifCB); \ +} + +/*++ + B i n d F i l t e r D r i v e r + +Routine Description: + Exchanges entry points with filter driver +Arguments: + bindInput - filter driver entry points + bindOutput - forwarder driver entry points +Return Value: + None +--*/ +VOID +BindFilterDriver ( + IN PIPX_FLT_BIND_INPUT bindInput, + OUT PIPX_FLT_BIND_OUTPUT bindOutput + ); + +/*++ + U n b i n d F i l t e r D r i v e r + +Routine Description: + Resets locally stored filter driver entry points + and resets filter driver contexts on all interfaces +Arguments: + None +Return Value: + None +--*/ +VOID +UnbindFilterDriver ( + VOID + ); + + +/*++ + D o F i l t e r + +Routine Description: + + Calls filter driver entry point while holding reader access + to interface contexts + +Arguments: + ipxHdr - pointer to packet header + ipxHdrLength - size of the header buffer (must be at least 30) + ifInContext - context associated with interface on which packet + was received + ifOutContext - context associated with interface on which packet + will be sent +Return Value: + FILTER_PERMIT - packet should be passed on by the forwarder + FILTER_DENY - packet should be dropped + +--*/ +FILTER_ACTION +DoFilter ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContex + ); + +/*++ + D o I n t e r f a c e D e l e t e d + +Routine Description: + Resets interface contexts and calls filter dirver entry point + making sure that all no one holds reader access to filter driver + interface contexts +Arguments: + ifCB - interface to be deleted +Return Value: + None + +--*/ +VOID +DoInterfaceDeleted ( + PINTERFACE_CB ifCB + ); + +#endif + diff --git a/private/ntos/tdi/isn/fwd/fwd.mak b/private/ntos/tdi/isn/fwd/fwd.mak new file mode 100644 index 000000000..e47660250 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/fwd.mak @@ -0,0 +1,2427 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (PPC) Dynamic-Link Library" 0x0702 +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=fwd - Win32 Release +!MESSAGE No configuration specified. Defaulting to fwd - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "fwd - Win32 Release" && "$(CFG)" !=\ + "fwd - Win32 (PPC) Release" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "fwd.mak" CFG="fwd - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "fwd - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "fwd - Win32 (PPC) Release" (based on\ + "Win32 (PPC) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "fwd - Win32 Release" + +!IF "$(CFG)" == "fwd - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(OUTDIR)\fwd.dll" + +CLEAN : + -@erase "$(INTDIR)\ddreqs.obj" + -@erase "$(INTDIR)\debug.obj" + -@erase "$(INTDIR)\driver.obj" + -@erase "$(INTDIR)\filterif.obj" + -@erase "$(INTDIR)\ipxbind.obj" + -@erase "$(INTDIR)\lineind.obj" + -@erase "$(INTDIR)\netbios.obj" + -@erase "$(INTDIR)\nwlnkfwd.res" + -@erase "$(INTDIR)\packets.obj" + -@erase "$(INTDIR)\rcvind.obj" + -@erase "$(INTDIR)\registry.obj" + -@erase "$(INTDIR)\send.obj" + -@erase "$(INTDIR)\tables.obj" + -@erase "$(OUTDIR)\fwd.dll" + -@erase "$(OUTDIR)\fwd.exp" + -@erase "$(OUTDIR)\fwd.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\inc" /I "..\..\inc" /I "..\..\..\inc" /I "..\..\..\..\net\routing\inc" /I "..\..\..\..\inc" /D _X86_=1 /D i386=1 /D "STD_CALL" /D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D WIN32=100 /D _NT1X_=100 /D WINNT=1 /D WIN32_LEAN_AND_MEAN=1 /D DBG=1 /D DEVL=1 /D FPO=0 /D "_NTDRIVER_" /YX /c +CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "..\inc" /I "..\..\inc" /I "..\..\..\inc"\ + /I "..\..\..\..\net\routing\inc" /I "..\..\..\..\inc" /D _X86_=1 /D i386=1 /D\ + "STD_CALL" /D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D WIN32=100 /D\ + _NT1X_=100 /D WINNT=1 /D WIN32_LEAN_AND_MEAN=1 /D DBG=1 /D DEVL=1 /D FPO=0 /D\ + "_NTDRIVER_" /Fp"$(INTDIR)/fwd.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +RSC_PROJ=/l 0x409 /fo"$(INTDIR)/nwlnkfwd.res" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/fwd.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo\ + /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)/fwd.pdb" /machine:I386\ + /out:"$(OUTDIR)/fwd.dll" /implib:"$(OUTDIR)/fwd.lib" +LINK32_OBJS= \ + "$(INTDIR)\ddreqs.obj" \ + "$(INTDIR)\debug.obj" \ + "$(INTDIR)\driver.obj" \ + "$(INTDIR)\filterif.obj" \ + "$(INTDIR)\ipxbind.obj" \ + "$(INTDIR)\lineind.obj" \ + "$(INTDIR)\netbios.obj" \ + "$(INTDIR)\nwlnkfwd.res" \ + "$(INTDIR)\packets.obj" \ + "$(INTDIR)\rcvind.obj" \ + "$(INTDIR)\registry.obj" \ + "$(INTDIR)\send.obj" \ + "$(INTDIR)\tables.obj" + +"$(OUTDIR)\fwd.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "fwd___Wi" +# PROP BASE Intermediate_Dir "fwd___Wi" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "fwd___Wi" +# PROP Intermediate_Dir "fwd___Wi" +# PROP Target_Dir "" +OUTDIR=.\fwd___Wi +INTDIR=.\fwd___Wi + +ALL : "$(OUTDIR)\fwd.dll" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CLEAN : + -@erase ".\fwd___Wi\fwd.dll" + -@erase ".\fwd___Wi\Tables.obj" + -@erase ".\fwd___Wi\rcvind.obj" + -@erase ".\fwd___Wi\packets.obj" + -@erase ".\fwd___Wi\ipxbind.obj" + -@erase ".\fwd___Wi\driver.obj" + -@erase ".\fwd___Wi\send.obj" + -@erase ".\fwd___Wi\registry.obj" + -@erase ".\fwd___Wi\netbios.obj" + -@erase ".\fwd___Wi\lineind.obj" + -@erase ".\fwd___Wi\nwlnkfwd.res" + -@erase ".\fwd___Wi\fwd.lib" + -@erase ".\fwd___Wi\fwd.exp" + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /PPC32 +# ADD MTL /nologo /D "NDEBUG" /PPC32 +MTL_PROJ=/nologo /D "NDEBUG" /PPC32 +CPP=cl.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /Z7 /Oi /Gy /I "..\inc" /I "..\..\..\inc" /I "..\..\..\..\inc" /I "e:\NT\public\oak\inc" /I "e:\NT\public\sdk\inc" /I "e:\NT\public\sdk\inc\crt" /FI"e:\NT\public\sdk\inc\warning.h" /D PPC=1 /D _PPC_=1 /D "NO_EXT_KEYS" /D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D WIN32=100 /D _NT1X_=100 /D WINNT=1 /D WIN32_LEAN_AND_MEAN=1 /D _M_PPC=1 /D DBG=1 /D DEVL=1 /D "_NTDRIVER_" /D __stdcall= /D __cdecl= /D _cdecl= /D cdecl= /D FPO=1 /D "LANGUAGE_C" -Zel -ZB64 /c +CPP_PROJ=/nologo /ML /W3 /Z7 /Oi /Gy /I "..\inc" /I "..\..\..\inc" /I\ + "..\..\..\..\inc" /I "e:\NT\public\oak\inc" /I "e:\NT\public\sdk\inc" /I\ + "e:\NT\public\sdk\inc\crt" /FI"e:\NT\public\sdk\inc\warning.h" /D PPC=1 /D\ + _PPC_=1 /D "NO_EXT_KEYS" /D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D\ + WIN32=100 /D _NT1X_=100 /D WINNT=1 /D WIN32_LEAN_AND_MEAN=1 /D _M_PPC=1 /D\ + DBG=1 /D DEVL=1 /D "_NTDRIVER_" /D __stdcall= /D __cdecl= /D _cdecl= /D cdecl=\ + /D FPO=1 /D "LANGUAGE_C" /Fo"$(INTDIR)/" -Zel -ZB64 /c +CPP_OBJS=.\fwd___Wi/ +CPP_SBRS= + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +RSC_PROJ=/l 0x409 /fo"$(INTDIR)/nwlnkfwd.res" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/fwd.bsc" +BSC32_SBRS= +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:PPC +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:PPC +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo\ + /subsystem:windows /dll /pdb:"$(OUTDIR)/fwd.pdb" /machine:PPC\ + /out:"$(OUTDIR)/fwd.dll" /implib:"$(OUTDIR)/fwd.lib" +LINK32_OBJS= \ + "$(INTDIR)/Tables.obj" \ + "$(INTDIR)/rcvind.obj" \ + "$(INTDIR)/packets.obj" \ + "$(INTDIR)/ipxbind.obj" \ + "$(INTDIR)/driver.obj" \ + "$(INTDIR)/send.obj" \ + "$(INTDIR)/registry.obj" \ + "$(INTDIR)/netbios.obj" \ + "$(INTDIR)/lineind.obj" \ + "$(INTDIR)/nwlnkfwd.res" + +"$(OUTDIR)\fwd.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +################################################################################ +# Begin Target + +# Name "fwd - Win32 Release" +# Name "fwd - Win32 (PPC) Release" + +!IF "$(CFG)" == "fwd - Win32 Release" + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\sources.inc + +!IF "$(CFG)" == "fwd - Win32 Release" + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\send.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_SEND_=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\send.obj" : $(SOURCE) $(DEP_CPP_SEND_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_SEND_=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\send.obj" : $(SOURCE) $(DEP_CPP_SEND_) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\rcvind.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_RCVIN=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\rcvind.obj" : $(SOURCE) $(DEP_CPP_RCVIN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_RCVIN=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\rcvind.obj" : $(SOURCE) $(DEP_CPP_RCVIN) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\netbios.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_NETBI=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\netbios.obj" : $(SOURCE) $(DEP_CPP_NETBI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_NETBI=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\netbios.obj" : $(SOURCE) $(DEP_CPP_NETBI) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\lineind.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_LINEI=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\lineind.obj" : $(SOURCE) $(DEP_CPP_LINEI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_LINEI=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\lineind.obj" : $(SOURCE) $(DEP_CPP_LINEI) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\ipxbind.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_IPXBI=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\ipxbind.obj" : $(SOURCE) $(DEP_CPP_IPXBI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_IPXBI=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\ipxbind.obj" : $(SOURCE) $(DEP_CPP_IPXBI) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\driver.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_DRIVE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\driver.obj" : $(SOURCE) $(DEP_CPP_DRIVE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_DRIVE=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\driver.obj" : $(SOURCE) $(DEP_CPP_DRIVE) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mp\sources + +!IF "$(CFG)" == "fwd - Win32 Release" + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\up\sources + +!IF "$(CFG)" == "fwd - Win32 Release" + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\registry.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_REGIS=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\registry.obj" : $(SOURCE) $(DEP_CPP_REGIS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_REGIS=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\registry.obj" : $(SOURCE) $(DEP_CPP_REGIS) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\packets.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_PACKE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\packets.obj" : $(SOURCE) $(DEP_CPP_PACKE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_CPP_PACKE=\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntmp.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\..\inc\ipxfwd.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\send.h"\ + ".\tables.h"\ + "e:\NT\public\oak\inc\zwapi.h"\ + "e:\NT\public\sdk\inc\cfg.h"\ + "e:\NT\public\sdk\inc\devioctl.h"\ + "E:\NT\public\sdk\inc\mipsinst.h"\ + "e:\NT\public\sdk\inc\netevent.h"\ + "e:\NT\public\sdk\inc\nt.h"\ + "E:\nt\public\sdk\inc\ntalpha.h"\ + "e:\NT\public\sdk\inc\ntconfig.h"\ + "e:\NT\public\sdk\inc\ntddndis.h"\ + "e:\NT\public\sdk\inc\ntddtdi.h"\ + "e:\NT\public\sdk\inc\ntdef.h"\ + "e:\NT\public\sdk\inc\ntelfapi.h"\ + "e:\NT\public\sdk\inc\ntexapi.h"\ + "E:\nt\public\sdk\inc\nti386.h"\ + "e:\NT\public\sdk\inc\ntimage.h"\ + "e:\NT\public\sdk\inc\ntioapi.h"\ + "e:\NT\public\sdk\inc\ntiolog.h"\ + "e:\NT\public\sdk\inc\ntkeapi.h"\ + "e:\NT\public\sdk\inc\ntkxapi.h"\ + "e:\NT\public\sdk\inc\ntldr.h"\ + "e:\NT\public\sdk\inc\ntlpcapi.h"\ + "E:\nt\public\sdk\inc\ntmips.h"\ + "e:\NT\public\sdk\inc\ntmmapi.h"\ + "e:\NT\public\sdk\inc\ntnls.h"\ + "e:\NT\public\sdk\inc\ntobapi.h"\ + "e:\NT\public\sdk\inc\ntpnpapi.h"\ + "e:\NT\public\sdk\inc\ntpoapi.h"\ + "E:\nt\public\sdk\inc\ntppc.h"\ + "e:\NT\public\sdk\inc\ntpsapi.h"\ + "e:\NT\public\sdk\inc\ntregapi.h"\ + "e:\NT\public\sdk\inc\ntrtl.h"\ + "e:\NT\public\sdk\inc\ntseapi.h"\ + "e:\NT\public\sdk\inc\ntstatus.h"\ + "e:\NT\public\sdk\inc\ntxcapi.h"\ + "E:\NT\public\sdk\inc\ppcinst.h"\ + + +"$(INTDIR)\packets.obj" : $(SOURCE) $(DEP_CPP_PACKE) "$(INTDIR)" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\nwlnkfwd.rc + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_RSC_NWLNK=\ + {$(INCLUDE)}"\common.ver"\ + {$(INCLUDE)}"\ntverp.h"\ + + +"$(INTDIR)\nwlnkfwd.res" : $(SOURCE) $(DEP_RSC_NWLNK) "$(INTDIR)" + $(RSC) $(RSC_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +DEP_RSC_NWLNK=\ + ".\common.ver"\ + ".\ntverp.h"\ + + +"$(INTDIR)\nwlnkfwd.res" : $(SOURCE) $(DEP_RSC_NWLNK) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)/nwlnkfwd.res" /d "NDEBUG" $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\tables.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_TABLE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\tables.obj" : $(SOURCE) $(DEP_CPP_TABLE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\ddreqs.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_DDREQ=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\ddreqs.obj" : $(SOURCE) $(DEP_CPP_DDREQ) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\debug.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_DEBUG=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\debug.obj" : $(SOURCE) $(DEP_CPP_DEBUG) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\filterif.c + +!IF "$(CFG)" == "fwd - Win32 Release" + +DEP_CPP_FILTE=\ + "..\..\..\..\inc\ipxfwd.h"\ + "..\..\..\..\inc\nettypes.h"\ + "..\..\..\..\inc\packoff.h"\ + "..\..\..\..\inc\packon.h"\ + "..\..\..\..\inc\tdi.h"\ + "..\..\..\..\inc\tdikrnl.h"\ + "..\..\..\inc\afilter.h"\ + "..\..\..\inc\bugcodes.h"\ + "..\..\..\inc\efilter.h"\ + "..\..\..\inc\exlevels.h"\ + "..\..\..\inc\ffilter.h"\ + "..\..\..\inc\ndis.h"\ + "..\..\..\inc\ntddk.h"\ + "..\..\..\inc\ntiologc.h"\ + "..\..\..\inc\ntos.h"\ + "..\..\..\inc\tfilter.h"\ + "..\inc\bind.h"\ + "..\inc\ipxfltif.h"\ + "..\inc\isnkrnl.h"\ + ".\..\..\..\inc\alpha.h"\ + ".\..\..\..\inc\alpharef.h"\ + ".\..\..\..\inc\arc.h"\ + ".\..\..\..\inc\cache.h"\ + ".\..\..\..\inc\cm.h"\ + ".\..\..\..\inc\dbgk.h"\ + ".\..\..\..\inc\ex.h"\ + ".\..\..\..\inc\exboosts.h"\ + ".\..\..\..\inc\hal.h"\ + ".\..\..\..\inc\i386.h"\ + ".\..\..\..\inc\init.h"\ + ".\..\..\..\inc\kd.h"\ + ".\..\..\..\inc\ke.h"\ + ".\..\..\..\inc\lfs.h"\ + ".\..\..\..\inc\lpc.h"\ + ".\..\..\..\inc\mips.h"\ + ".\..\..\..\inc\mm.h"\ + ".\..\..\..\inc\ntosdef.h"\ + ".\..\..\..\inc\ob.h"\ + ".\..\..\..\inc\pnp.h"\ + ".\..\..\..\inc\po.h"\ + ".\..\..\..\inc\ppc.h"\ + ".\..\..\..\inc\ps.h"\ + ".\..\..\..\inc\se.h"\ + ".\..\..\..\inc\v86emul.h"\ + ".\ddreqs.h"\ + ".\debug.h"\ + ".\driver.h"\ + ".\filterif.h"\ + ".\fwddefs.h"\ + ".\ipxbind.h"\ + ".\lineind.h"\ + ".\netbios.h"\ + ".\packets.h"\ + ".\precomp.h"\ + ".\rcvind.h"\ + ".\registry.h"\ + ".\rwlock.h"\ + ".\send.h"\ + ".\tables.h"\ + "c:\nt\public\sdk\inc\mipsinst.h"\ + "c:\nt\public\sdk\inc\ntalpha.h"\ + "c:\nt\public\sdk\inc\nti386.h"\ + "c:\nt\public\sdk\inc\ntmips.h"\ + "c:\nt\public\sdk\inc\ntppc.h"\ + "c:\nt\public\sdk\inc\ppcinst.h"\ + {$(INCLUDE)}"\cfg.h"\ + {$(INCLUDE)}"\devioctl.h"\ + {$(INCLUDE)}"\netevent.h"\ + {$(INCLUDE)}"\nt.h"\ + {$(INCLUDE)}"\ntconfig.h"\ + {$(INCLUDE)}"\ntddndis.h"\ + {$(INCLUDE)}"\ntddtdi.h"\ + {$(INCLUDE)}"\ntdef.h"\ + {$(INCLUDE)}"\ntelfapi.h"\ + {$(INCLUDE)}"\ntexapi.h"\ + {$(INCLUDE)}"\ntimage.h"\ + {$(INCLUDE)}"\ntioapi.h"\ + {$(INCLUDE)}"\ntiolog.h"\ + {$(INCLUDE)}"\ntkeapi.h"\ + {$(INCLUDE)}"\ntkxapi.h"\ + {$(INCLUDE)}"\ntldr.h"\ + {$(INCLUDE)}"\ntlpcapi.h"\ + {$(INCLUDE)}"\ntmmapi.h"\ + {$(INCLUDE)}"\ntnls.h"\ + {$(INCLUDE)}"\ntobapi.h"\ + {$(INCLUDE)}"\ntpnpapi.h"\ + {$(INCLUDE)}"\ntpoapi.h"\ + {$(INCLUDE)}"\ntpsapi.h"\ + {$(INCLUDE)}"\ntregapi.h"\ + {$(INCLUDE)}"\ntrtl.h"\ + {$(INCLUDE)}"\ntseapi.h"\ + {$(INCLUDE)}"\ntstatus.h"\ + {$(INCLUDE)}"\ntxcapi.h"\ + {$(INCLUDE)}"\zwapi.h"\ + + +"$(INTDIR)\filterif.obj" : $(SOURCE) $(DEP_CPP_FILTE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "fwd - Win32 (PPC) Release" + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/private/ntos/tdi/isn/fwd/fwddefs.h b/private/ntos/tdi/isn/fwd/fwddefs.h new file mode 100644 index 000000000..af862b3b5 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/fwddefs.h @@ -0,0 +1,125 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\fwddefs.h + +Abstract: + IPX Forwarder driver constants and general macro definitions + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_FWDDEFS_ +#define _IPXFWD_FWDDEFS_ + + +// Forwarder tag used in memory allocations +#define FWD_POOL_TAG 'wFwN' + +//*** Offsets into the IPX header +#define IPXH_HDRSIZE 30 // Size of the IPX header +#define IPXH_CHECKSUM 0 // Checksum +#define IPXH_LENGTH 2 // Length +#define IPXH_XPORTCTL 4 // Transport Control +#define IPXH_PKTTYPE 5 // Packet Type +#define IPXH_DESTADDR 6 // Dest. Address (Total) +#define IPXH_DESTNET 6 // Dest. Network Address +#define IPXH_DESTNODE 10 // Dest. Node Address +#define IPXH_DESTSOCK 16 // Dest. Socket Number +#define IPXH_SRCADDR 18 // Source Address (Total) +#define IPXH_SRCNET 18 // Source Network Address +#define IPXH_SRCNODE 22 // Source Node Address +#define IPXH_SRCSOCK 28 // Source Socket Number + +//*** Packet Types we care about +#define IPX_NETBIOS_TYPE 20 // Netbios propagated packet + +//*** Socket Numbers we care about +#define IPX_NETBIOS_SOCKET ((USHORT)0x0455) +#define IPX_SAP_SOCKET ((USHORT)0x0452) +#define IPX_SMB_NAME_SOCKET ((USHORT)0x0551) + +//*** maximum nr of hops for a normal packet *** +#define IPX_MAX_HOPS 16 + +//*** offsets into the netbios name frames *** +#define NB_NAME_TYPE_FLAG 62 +#define NB_DATA_STREAM_TYPE2 63 +#define NB_NAME 64 +#define NB_TOTAL_DATA_LENGTH 80 +// *** offsets into smb name claim/query frames +#define SMB_OPERATION 62 +#define SMB_NAME_TYPE 63 +#define SMB_MESSAGE_IF 64 +#define SMB_NAME 66 + + +// Some commonly used macros +#define IPX_NODE_CPY(dst,src) memcpy(dst,src,6) +#define IPX_NODE_CMP(node1,node2) memcmp(node1,node2,6) + +#define IPX_NET_CPY(dst,src) memcpy(dst,src,4) +#define IPX_NET_CMP(net1,net2) memcmp(net1,net2,4) + +#define NB_NAME_CPY(dst,src) strncpy((char *)dst,(char *)src,16) +#define NB_NAME_CMP(name1,name2) strncmp((char *)name1,(char *)name2,16) + +// Make sure the structure is copied with DWORD granularity +#define IF_STATS_CPY(dst,src) \ + (dst)->OperationalState = (src)->OperationalState; \ + (dst)->MaxPacketSize = (src)->MaxPacketSize; \ + (dst)->InHdrErrors = (src)->InHdrErrors; \ + (dst)->InFiltered = (src)->InFiltered; \ + (dst)->InNoRoutes = (src)->InNoRoutes; \ + (dst)->InDiscards = (src)->InDiscards; \ + (dst)->InDelivers = (src)->InDelivers; \ + (dst)->OutFiltered = (src)->OutFiltered; \ + (dst)->OutDiscards = (src)->OutDiscards; \ + (dst)->OutDelivers = (src)->OutDelivers; \ + (dst)->NetbiosReceived = (src)->NetbiosReceived; \ + (dst)->NetbiosSent = (src)->NetbiosSent; + +// Extensions to list macros +#define InitializeListEntry(entry) InitializeListHead(entry) +#define IsListEntry(entry) IsListEmpty(entry) +#define IsSingleEntry(head) ((head)->Flink==(head)->Blink) + +// Conversions from/to on-the-wire format +#define GETUSHORT(src) ( \ + (USHORT)( \ + (((UCHAR *)src)[0]<<8) \ + + (((UCHAR *)src)[1]) \ + ) \ +) + +#define GETULONG(src) ( \ + (ULONG)( \ + (((UCHAR *)src)[0]<<24) \ + + (((UCHAR *)src)[1]<<16) \ + + (((UCHAR *)src)[2]<<8) \ + + (((UCHAR *)src)[3]) \ + ) \ +) + +#define PUTUSHORT(src,dst) { \ + ((UCHAR *)dst)[0] = ((UCHAR)(src>>8)); \ + ((UCHAR *)dst)[1] = ((UCHAR)src); \ +} + +#define PUTULONG(src,dst) { \ + ((UCHAR *)dst)[0] = ((UCHAR)(src>>24)); \ + ((UCHAR *)dst)[1] = ((UCHAR)(src>>16)); \ + ((UCHAR *)dst)[2] = ((UCHAR)(src>>8)); \ + ((UCHAR *)dst)[3] = ((UCHAR)src); \ +} + + +#endif diff --git a/private/ntos/tdi/isn/fwd/ipxbind.c b/private/ntos/tdi/isn/fwd/ipxbind.c new file mode 100644 index 000000000..71c7a37b3 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/ipxbind.c @@ -0,0 +1,462 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\ipxbind.c + +Abstract: + IPX Forwarder Driver interface with IPX stack driver + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + +// global handle of the IPX driver +HANDLE HdlIpxFile; + + +// Buffer for IPX binding output structure +PIPX_INTERNAL_BIND_RIP_OUTPUT IPXBindOutput=NULL; + +NTSTATUS +IpxFwdFindRoute ( + IN PUCHAR Network, + IN PUCHAR Node, + OUT PIPX_FIND_ROUTE_REQUEST RouteEntry + ); + +/*++ +******************************************************************* + B i n d T o I p x D r i v e r + +Routine Description: + Exchanges binding information with IPX stack driver +Arguments: + None +Return Value: + STATUS_SUCCESS - exchange was done OK + STATUS_INSUFFICIENT_RESOURCES - could not allocate buffers for + info exchange + error status returned by IPX stack driver + +******************************************************************* +--*/ +NTSTATUS +BindToIpxDriver ( + KPROCESSOR_MODE requestorMode + ) { + NTSTATUS status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PIPX_INTERNAL_BIND_INPUT bip; + UNICODE_STRING UstrIpxFileName; + PWSTR WstrIpxFileName; + + ASSERT (IPXBindOutput==NULL); + + // Read Ipx exported device name from the registry + status = ReadIpxDeviceName (&WstrIpxFileName); + if (!NT_SUCCESS (status)) + return status; + + RtlInitUnicodeString (&UstrIpxFileName, WstrIpxFileName); + InitializeObjectAttributes( + &ObjectAttributes, + &UstrIpxFileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (requestorMode==UserMode) + status = ZwCreateFile(&HdlIpxFile, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + else + status = NtCreateFile(&HdlIpxFile, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(status)) { + IpxFwdDbgPrint (DBG_IPXBIND, DBG_ERROR, + ("IpxFwd: Open of the IPX driver failed with %lx\n", status)); + return status; + } + + IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, + ("IpxFwd: Open of the IPX driver was successful.\n")); + + // First, send a IOCTL to find out how much data we need to allocate + if ((bip = ExAllocatePoolWithTag ( + PagedPool, + sizeof(IPX_INTERNAL_BIND_INPUT), + FWD_POOL_TAG)) == NULL) { + + if (KeGetPreviousMode()!=KernelMode) + ZwClose (HdlIpxFile); + else + NtClose (HdlIpxFile); + IpxFwdDbgPrint (DBG_IPXBIND, DBG_ERROR, + ("IpxFwd: Could not allocate input binding buffer!\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // fill in our bind data + // bip->Version = 1; + bip->Version = ISN_VERSION; + bip->Identifier = IDENTIFIER_RIP; + bip->BroadcastEnable = TRUE; + bip->LookaheadRequired = IPXH_HDRSIZE; + bip->ProtocolOptions = 0; + bip->ReceiveHandler = IpxFwdReceive; + bip->ReceiveCompleteHandler = IpxFwdReceiveComplete; + bip->SendCompleteHandler = IpxFwdSendComplete; + bip->TransferDataCompleteHandler = IpxFwdTransferDataComplete; + bip->FindRouteCompleteHandler = NULL; + bip->LineUpHandler = IpxFwdLineUp; + bip->LineDownHandler = IpxFwdLineDown; + bip->InternalSendHandler = IpxFwdInternalSend; + bip->FindRouteHandler = IpxFwdFindRoute; + bip->InternalReceiveHandler = IpxFwdInternalReceive; +// bip->RipParameters = GlobalWanNetwork ? IPX_RIP_PARAM_GLOBAL_NETWORK : 0; + + + if (requestorMode==UserMode) + status = ZwDeviceIoControlFile( + HdlIpxFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT),// Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + else + status = NtDeviceIoControlFile( + HdlIpxFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT),// Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + + if (status == STATUS_PENDING) { + if (requestorMode==UserMode) + status = ZwWaitForSingleObject( + HdlIpxFile, + FALSE, + NULL); + else + status = NtWaitForSingleObject( + HdlIpxFile, + FALSE, + NULL); + if (NT_SUCCESS(status)) + status = IoStatusBlock.Status; + } + + if (status != STATUS_BUFFER_TOO_SMALL) { + IpxFwdDbgPrint (DBG_IPXBIND, DBG_ERROR, + ("IpxFwd: Ioctl to the IPX driver failed with %lx\n", status)); + + ExFreePool(bip); + if (requestorMode==UserMode) + ZwClose (HdlIpxFile); + else + NtClose (HdlIpxFile); + return STATUS_INVALID_PARAMETER; + } + + if ((IPXBindOutput = (PIPX_INTERNAL_BIND_RIP_OUTPUT) + ExAllocatePoolWithTag(NonPagedPool, + IoStatusBlock.Information, + FWD_POOL_TAG)) == NULL) { + + ExFreePool(bip); + if (requestorMode==UserMode) + ZwClose (HdlIpxFile); + else + NtClose (HdlIpxFile); + IpxFwdDbgPrint (DBG_IPXBIND, DBG_ERROR, + ("IpxFwd: Could not allocate output binding buffer!\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + if (requestorMode==UserMode) + status = ZwDeviceIoControlFile( + HdlIpxFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT),// Input Buffer Length + IPXBindOutput, // Output Buffer + IoStatusBlock.Information); // Output Buffer Length + else + status = NtDeviceIoControlFile( + HdlIpxFile, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT),// Input Buffer Length + IPXBindOutput, // Output Buffer + IoStatusBlock.Information); // Output Buffer Length + + + if (status == STATUS_PENDING) { + if (requestorMode==UserMode) + status = ZwWaitForSingleObject( + HdlIpxFile, + (BOOLEAN)FALSE, + NULL); + else + status = NtWaitForSingleObject( + HdlIpxFile, + (BOOLEAN)FALSE, + NULL); + if (NT_SUCCESS(status)) + status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS (status)) { + IpxFwdDbgPrint (DBG_IPXBIND, DBG_ERROR, + ("IpxFwd: Ioctl to the IPX driver failed with %lx\n", IoStatusBlock.Status)); + + ExFreePool(bip); + ExFreePool(IPXBindOutput); + IPXBindOutput = NULL; + if (requestorMode==UserMode) + ZwClose (HdlIpxFile); + else + NtClose (HdlIpxFile); + return status; + } + + IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, + ("IpxFwd: Succesfuly bound to the IPX driver\n")); + + ExFreePool (bip); + ExFreePool (WstrIpxFileName); + + return status; +} + + +/*++ +******************************************************************* + U n b i n d F r o m I p x D r i v e r + +Routine Description: + Closes connection to IPX stack driver +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +UnbindFromIpxDriver ( + KPROCESSOR_MODE requestorMode + ) { + // Free binding output buffer and close driver handle + ASSERT (IPXBindOutput!=NULL); + ExFreePool (IPXBindOutput); + IPXBindOutput = NULL; + IpxFwdDbgPrint (DBG_IPXBIND, DBG_WARNING, + ("IpxFwd: Closing IPX driver handle\n")); + if (requestorMode==UserMode) + ZwClose (HdlIpxFile); + else + NtClose (HdlIpxFile); +} + + +/*++ +******************************************************************* + F w F i n d R o u t e + +Routine Description: + This routine is provided by the Kernel Forwarder to find the route + to a given node and network +Arguments: + Network - the destination network + Node - destination node + RouteEntry - filled in by the Forwarder if a route exists +Return Value: + STATUS_SUCCESS + STATUS_NETWORK_UNREACHABLE - if the findroute failed +******************************************************************* +--*/ +NTSTATUS +IpxFwdFindRoute ( + IN PUCHAR Network, + IN PUCHAR Node, + OUT PIPX_FIND_ROUTE_REQUEST RouteEntry + ) { + PINTERFACE_CB ifCB; + ULONG net; + KIRQL oldIRQL; + NTSTATUS status; + PFWD_ROUTE fwRoute; + + if (!EnterForwarder ()) + return STATUS_UNSUCCESSFUL; + + net = GETULONG (Network); + + ifCB = FindDestination (net, Node, &fwRoute); + if (ifCB!=NULL) { + if (IS_IF_ENABLED(ifCB)) { + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + switch (ifCB->ICB_Stats.OperationalState) { + case FWD_OPER_STATE_UP: + IPX_NET_CPY (&RouteEntry->Network, Network); + if (fwRoute->FR_Network==ifCB->ICB_Network) { + if (Node!=NULL) { + IPX_NODE_CPY (RouteEntry->LocalTarget.MacAddress, Node); + } + else { + IPX_NODE_CPY (RouteEntry->LocalTarget.MacAddress, + BROADCAST_NODE); + } + } + else { + IPX_NODE_CPY (RouteEntry->LocalTarget.MacAddress, + fwRoute->FR_NextHopAddress); + } + if (ifCB!=InternalInterface) { + ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + ifCB->ICB_AdapterContext, + &RouteEntry->LocalTarget); + } + else { + CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + VIRTUAL_NET_ADAPTER_CONTEXT, + &RouteEntry->LocalTarget); + } + status = STATUS_SUCCESS; + break; + case FWD_OPER_STATE_SLEEPING: + IPX_NODE_CPY (&RouteEntry->LocalTarget.MacAddress, + fwRoute->FR_NextHopAddress); + CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET (DEMAND_DIAL_ADAPTER_CONTEXT, + &RouteEntry->LocalTarget); + status = STATUS_SUCCESS; + break; + case FWD_OPER_STATE_DOWN: + status = STATUS_NETWORK_UNREACHABLE; + break; + default: + ASSERTMSG ("Inavalid operational state", FALSE); + } + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + #if DBG + if (Node!=NULL) { + if (NT_SUCCESS (status)) { + IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, + ("IpxFwd: Found route for IPX driver:" + " %08lX:%02X%02X%02X%02X%02X%02X" + " -> %ld(%ld):%02X%02X%02X%02X%02X%02X\n", + net, Node[0],Node[1],Node[2],Node[3],Node[4],Node[5], + ifCB->ICB_Index, RouteEntry->LocalTarget.NicId, + RouteEntry->LocalTarget.MacAddress[0], + RouteEntry->LocalTarget.MacAddress[1], + RouteEntry->LocalTarget.MacAddress[2], + RouteEntry->LocalTarget.MacAddress[3], + RouteEntry->LocalTarget.MacAddress[4], + RouteEntry->LocalTarget.MacAddress[5])); + } + else { + IpxFwdDbgPrint (DBG_IPXROUTE, DBG_WARNING, + ("IpxFwd: Network unreachable for:" + " %08lX:%02X%02X%02X%02X%02X%02X -> %ld.\n", + net, Node[0],Node[1],Node[2],Node[3],Node[4],Node[5], + ifCB->ICB_Index)); + } + } + else { + if (NT_SUCCESS (status)) { + IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, + ("IpxFwd: Found route for IPX driver:" + " %08lX" + " -> %ld(%ld):%02X%02X%02X%02X%02X%02X\n", + net, ifCB->ICB_Index, RouteEntry->LocalTarget.NicId, + RouteEntry->LocalTarget.MacAddress[0], + RouteEntry->LocalTarget.MacAddress[1], + RouteEntry->LocalTarget.MacAddress[2], + RouteEntry->LocalTarget.MacAddress[3], + RouteEntry->LocalTarget.MacAddress[4], + RouteEntry->LocalTarget.MacAddress[5])); + } + else { + IpxFwdDbgPrint (DBG_IPXROUTE, DBG_WARNING, + ("IpxFwd: Network unreachable for:" + " %08lX -> %ld.\n", net)); + } + } + #endif + ReleaseInterfaceReference (ifCB); + ReleaseRouteReference (fwRoute); + + } + } + else { +#if DBG + if (Node!=NULL) { + IpxFwdDbgPrint (DBG_IPXROUTE, DBG_WARNING, + ("IpxFwd: No route for:" + " %08lX:%02X%02X%02X%02X%02X%02X.\n", + net, Node[0],Node[1],Node[2],Node[3],Node[4],Node[5])); + } + else { + IpxFwdDbgPrint (DBG_IPXROUTE, DBG_WARNING, + ("IpxFwd: No route for: %08lX.\n", net)); + } +#endif + status = STATUS_NETWORK_UNREACHABLE; + } + LeaveForwarder (); + return status; +} + + diff --git a/private/ntos/tdi/isn/fwd/ipxbind.h b/private/ntos/tdi/isn/fwd/ipxbind.h new file mode 100644 index 000000000..7cf367a2b --- /dev/null +++ b/private/ntos/tdi/isn/fwd/ipxbind.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\ipxbind.h + +Abstract: + IPX Forwarder Driver interface with IPX stack driver + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + + +#ifndef _IPXFWD_IPXBIND_ +#define _IPXFWD_IPXBIND_ + +extern PIPX_INTERNAL_BIND_RIP_OUTPUT IPXBindOutput; +#define IPXMacHeaderSize (IPXBindOutput->MacHeaderNeeded) +#define IPXOpenAdapterProc (IPXBindOutput->OpenAdapterHandler) +#define IPXCloseAdapterProc (IPXBindOutput->CloseAdapterHandler) +#define IPXInternalSendCompletProc (IPXBindOutput->InternalSendCompleteHandler) +#define IPXSendProc (IPXBindOutput->SendHandler) +#define IPXTransferData (IPXBindOutput->TransferDataHandler) + + +/*++ +******************************************************************* + B i n d T o I p x D r i v e r + +Routine Description: + Exchanges binding information with IPX stack driver +Arguments: +Return Value: + STATUS_SUCCESS - exchange was done OK + STATUS_INSUFFICIENT_RESOURCES - could not allocate buffers for + info exchange + error status returned by IPX stack driver + +******************************************************************* +--*/ +NTSTATUS +BindToIpxDriver ( + KPROCESSOR_MODE requestorMode + ); + + +/*++ +******************************************************************* + U n b i n d T o I p x D r i v e r + +Routine Description: + Closes connection to IPX stack driver +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +UnbindFromIpxDriver ( + KPROCESSOR_MODE requestorMode + ); + + +#endif + diff --git a/private/ntos/tdi/isn/fwd/lineind.c b/private/ntos/tdi/isn/fwd/lineind.c new file mode 100644 index 000000000..e00518d6a --- /dev/null +++ b/private/ntos/tdi/isn/fwd/lineind.c @@ -0,0 +1,323 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\lineind.c + +Abstract: + Processing line indication (bind/unbind) + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + +/*++ +******************************************************************* + B i n d I n t e r f a c e + +Routine Description: + Binds interface to physical adapter and exchanges contexts + with IPX stack +Arguments: + ifCB - interface to bind + NicId - id of an adapter + MaxPacketSize - max size of packet allowed + Network - adapter network address + LocalNode - adapter local node address + RemoteNode - peer node address (for clients on global + net) +Return Value: + STATUS_SUCCESS - interface was bound OK + error status returned by IPX stack driver + +******************************************************************* +--*/ +NTSTATUS +BindInterface ( + IN PINTERFACE_CB ifCB, + IN USHORT NicId, + IN ULONG MaxPacketSize, + IN ULONG Network, + IN PUCHAR LocalNode, + IN PUCHAR RemoteNode + ) { + KIRQL oldIRQL; + NTSTATUS status; + NIC_HANDLE NicHandle={0}; + + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + if (ifCB->ICB_Stats.OperationalState!=FWD_OPER_STATE_UP) { + switch (ifCB->ICB_InterfaceType) { + case FWD_IF_PERMANENT: + if (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX) + status = RegisterPacketConsumer (MaxPacketSize, + &ifCB->ICB_PacketListId); + else + status = STATUS_SUCCESS; + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + if (IS_IF_CONNECTING (ifCB)) { + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + } + status = STATUS_SUCCESS; + break; + default: + ASSERTMSG ("Invalid interface type ", FALSE); + break; + } + if (NT_SUCCESS (status)) { + if (Network==GlobalNetwork) { + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + IPX_NODE_CPY (ifCB->ICB_RemoteNode, RemoteNode); + status = AddGlobalNetClient (ifCB); + ASSERT (status==STATUS_SUCCESS); + } + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + + if (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX) { + NIC_HANDLE_FROM_NIC(NicHandle, NicId); + status = IPXOpenAdapterProc (NicHandle, (ULONG)ifCB, + &ifCB->ICB_AdapterContext); + } + + if (NT_SUCCESS (status)) { + ifCB->ICB_Network = Network; + IPX_NODE_CPY (ifCB->ICB_RemoteNode, RemoteNode); + IPX_NODE_CPY (ifCB->ICB_LocalNode, LocalNode); + if (ifCB->ICB_InterfaceType==FWD_IF_PERMANENT) + ifCB->ICB_Stats.MaxPacketSize = MaxPacketSize; + else + ifCB->ICB_Stats.MaxPacketSize = WAN_PACKET_SIZE; + ifCB->ICB_NicId = NicId; + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_UP; + + AcquireInterfaceReference (ifCB); + IpxFwdDbgPrint (DBG_LINEIND, DBG_INFORMATION, + ("IpxFwd: Bound interface %ld (icb: %08lx):" + " Nic-%d, Net-%08lx," + " LocalNode-%02x%02x%02x%02x%02x%02x," + " RemoteNode-%02x%02x%02x%02x%02x%02x.\n", + ifCB->ICB_Index, ifCB, NicId, Network, + LocalNode[0], LocalNode[1], LocalNode[2], + LocalNode[3], LocalNode[4], LocalNode[5], + RemoteNode[0], RemoteNode[1], RemoteNode[2], + RemoteNode[3], RemoteNode[4], RemoteNode[5])); + + if (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX) { + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + return STATUS_SUCCESS; + } + + IpxFwdDbgPrint (DBG_LINEIND, DBG_ERROR, + ("IpxFwd: Could not open adapter %d to bind" + " interface %ld (icb: %08lx)!\n", + NicId, ifCB->ICB_Index, ifCB)); + + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + if (Network==GlobalNetwork) { + DeleteGlobalNetClient (ifCB); + } + + switch (ifCB->ICB_InterfaceType) { + case FWD_IF_PERMANENT: + DeregisterPacketConsumer (ifCB->ICB_PacketListId); + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + break; + } + } + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + else { + ASSERT (Network==ifCB->ICB_Network); + ASSERT (NicId==(USHORT)ifCB->ICB_AdapterContext.NicId); + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + status = STATUS_SUCCESS; // Report success if already + // connected + IpxFwdDbgPrint (DBG_LINEIND, DBG_WARNING, + ("IpxFwd: Interface %ld (icb: %08lx) is already bound to Nic %d.\n", + ifCB->ICB_Index, ifCB, NicId)); + } + return status; +} + + +/*++ +******************************************************************* + U n b i n d I n t e r f a c e + +Routine Description: + Unbinds interface from physical adapter and breaks connection + with IPX stack +Arguments: + ifCB - interface to unbind +Return Value: + None +******************************************************************* +--*/ +VOID +UnbindInterface ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + KeAcquireSpinLock (&ifCB->ICB_Lock, &oldIRQL); + if (ifCB->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) { + switch (ifCB->ICB_InterfaceType) { + case FWD_IF_PERMANENT: + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + if (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX) + DeregisterPacketConsumer (ifCB->ICB_PacketListId); + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_SLEEPING; + KeQuerySystemTime ((PLARGE_INTEGER)&ifCB->ICB_DisconnectTime); + break; + default: + ASSERTMSG ("Invalid interface type ", FALSE); + break; + } + if (ifCB->ICB_CashedInterface!=NULL) + ReleaseInterfaceReference (ifCB->ICB_CashedInterface); + ifCB->ICB_CashedInterface = NULL; + if (ifCB->ICB_CashedRoute!=NULL) + ReleaseRouteReference (ifCB->ICB_CashedRoute); + ifCB->ICB_CashedRoute = NULL; + if (ifCB->ICB_Network==GlobalNetwork) + DeleteGlobalNetClient (ifCB); + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + + IpxFwdDbgPrint (DBG_LINEIND, DBG_INFORMATION, + ("IpxFwd: Unbinding interface %ld (icb: %08lx) from Nic %ld.\n", + ifCB->ICB_Index, ifCB, ifCB->ICB_AdapterContext)); + if (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX) { + IPXCloseAdapterProc (ifCB->ICB_AdapterContext); + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + ReleaseInterfaceReference (ifCB); + } + else { + KeReleaseSpinLock (&ifCB->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_LINEIND, DBG_WARNING, + ("IpxFwd: Interface %ld (icb: %08lx) is already unbound.\n", + ifCB->ICB_Index, ifCB)); + } +} + + + +/*++ +******************************************************************* + F w L i n e U p + +Routine Description: + Process line up indication delivered by IPX stack +Arguments: + NicId - adapter ID on which connection was established + LineInfo - NDIS/IPX line information + DeviceType - medium specs + ConfigurationData - IPX CP configuration data +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) { + PINTERFACE_CB ifCB; + if (ConfigurationData==NULL) // This is just an update for multilink + // connections + return; + + if (!EnterForwarder()) { + return; + } + IpxFwdDbgPrint (DBG_LINEIND, DBG_INFORMATION, ("IpxFwd: FwdLineUp.\n")); + + ifCB = GetInterfaceReference ( + ((PIPXCP_CONFIGURATION)ConfigurationData)->InterfaceIndex); + if (ifCB!=NULL) { + LONG Net = GETULONG (((PIPXCP_CONFIGURATION)ConfigurationData)->Network); + + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + ASSERT (ifCB->ICB_InterfaceType!=FWD_IF_PERMANENT); + + BindInterface (ifCB, + NicId, + LineInfo->MaximumPacketSize, + Net, + ((PIPXCP_CONFIGURATION)ConfigurationData)->LocalNode, + ((PIPXCP_CONFIGURATION)ConfigurationData)->RemoteNode + ); + ReleaseInterfaceReference (ifCB); + } + LeaveForwarder (); +} + + + + +/*++ +******************************************************************* + F w L i n e D o w n + +Routine Description: + Process line down indication delivered by IPX stack +Arguments: + NicId - disconnected adapter ID +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdLineDown ( + IN USHORT NicId, + IN ULONG Context + ) { + PINTERFACE_CB ifCB; + + if (!EnterForwarder()) { + return; + } + IpxFwdDbgPrint (DBG_LINEIND, DBG_INFORMATION, ("IpxFwd: FwdLineDown.\n")); + + + ifCB = InterfaceContextToReference ((PVOID)Context, NicId); + if (ifCB!=NULL) { + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + ASSERT (ifCB->ICB_InterfaceType!=FWD_IF_PERMANENT); + UnbindInterface (ifCB); + ReleaseInterfaceReference (ifCB); + } + LeaveForwarder (); +} + diff --git a/private/ntos/tdi/isn/fwd/lineind.h b/private/ntos/tdi/isn/fwd/lineind.h new file mode 100644 index 000000000..bbc8df7ac --- /dev/null +++ b/private/ntos/tdi/isn/fwd/lineind.h @@ -0,0 +1,116 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\lineind.h + +Abstract: + Processing line indication (bind/unbind) + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_LINEIND_ +#define _IPXFWD_LINEIND_ + +/*++ +******************************************************************* + B i n d I n t e r f a c e + +Routine Description: + Binds interface to physical adapter and exchanges contexts + with IPX stack +Arguments: + ifCB - interface to bind + NicId - id of an adapter + MaxPacketSize - max size of packet allowed + Network - adapter network address + LocalNode - adapter local node address + RemoteNode - peer node address (for clients on global + net) +Return Value: + STATUS_SUCCESS - interface was bound OK + error status returned by IPX stack driver + +******************************************************************* +--*/ +NTSTATUS +BindInterface ( + IN PINTERFACE_CB ifCB, + IN USHORT NicId, + IN ULONG MaxPacketSize, + IN ULONG Network, + IN PUCHAR LocalNode, + IN PUCHAR RemoteNode + ); + +/*++ +******************************************************************* + U n b i n d I n t e r f a c e + +Routine Description: + Unbinds interface from physical adapter and breaks connection + with IPX stack +Arguments: + ifCB - interface to unbind +Return Value: + None +******************************************************************* +--*/ +VOID +UnbindInterface ( + PINTERFACE_CB ifCB + ); + +/*++ +******************************************************************* + F w L i n e U p + +Routine Description: + Process line up indication delivered by IPX stack +Arguments: + NicId - adapter ID on which connection was established + LineInfo - NDIS/IPX line information + DeviceType - medium specs + ConfigurationData - IPX CP configuration data +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ); + +/*++ +******************************************************************* + F w L i n e D o w n + +Routine Description: + Process line down indication delivered by IPX stack +Arguments: + NicId - disconnected adapter ID +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdLineDown ( + IN USHORT NicId, + IN ULONG Context + ); + +#endif diff --git a/private/ntos/tdi/isn/fwd/mp/makefile b/private/ntos/tdi/isn/fwd/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/fwd/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/fwd/mp/sources b/private/ntos/tdi/isn/fwd/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/fwd/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/fwd/netbios.c b/private/ntos/tdi/isn/fwd/netbios.c new file mode 100644 index 000000000..4a1f9b84b --- /dev/null +++ b/private/ntos/tdi/isn/fwd/netbios.c @@ -0,0 +1,311 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\netbios.c + +Abstract: + Netbios packet processing + +Author: + + Vadim Eydelman + +Revision History: + +--*/ +#include "precomp.h" + +LIST_ENTRY NetbiosQueue; +KSPIN_LOCK NetbiosQueueLock; +WORK_QUEUE_ITEM NetbiosWorker; +BOOLEAN NetbiosWorkerScheduled=FALSE; +ULONG NetbiosPacketsQuota; +ULONG MaxNetbiosPacketsQueued = DEF_MAX_NETBIOS_PACKETS_QUEUED; + + +/*++ +******************************************************************* + P r o c e s s N e t b i o s Q u e u e + +Routine Description: + Process packets in the netbios broadcast queue (sends them on + all interfaces in sequence) +Arguments: + Context - unused +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessNetbiosQueue ( + PVOID Context + ) { + KIRQL oldIRQL; + LIST_ENTRY tempQueue; + + KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL); + // Check if there is something in the queue + if (!IsListEmpty (&NetbiosQueue)) { + // Move the queue to local variable + InsertHeadList (&NetbiosQueue, &tempQueue); + RemoveEntryList (&NetbiosQueue); + InitializeListHead (&NetbiosQueue); + + KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL); + do { + PLIST_ENTRY cur; + PPACKET_TAG pktTag; + + cur = RemoveHeadList (&tempQueue); + pktTag = CONTAINING_RECORD (cur, PACKET_TAG, PT_QueueLink); + // Check if this packet has to be sent on other + // interfaces + if (!(pktTag->PT_Flags&PT_NB_DESTROY)) { + PINTERFACE_CB dstIf = pktTag->PT_InterfaceReference; + PUCHAR dataPtr = pktTag->PT_Data; + UINT rtCount = *(dataPtr+IPXH_XPORTCTL); + PUCHAR netListPtr; + UINT i; + + if (dstIf==NULL) { + // This is a brand new packet: not sent on any + // interface yet + USHORT dstSock = GETUSHORT (dataPtr+IPXH_DESTSOCK); + // Check if we have a static route for this name + // (offset to name depends on packet dest socket) + if (dstSock==IPX_NETBIOS_SOCKET) + dstIf = FindNBDestination (dataPtr+NB_NAME); + else if (dstSock==IPX_SMB_NAME_SOCKET) + dstIf = FindNBDestination (dataPtr+SMB_NAME); + else + dstIf = NULL; + + if (dstIf!=NULL) { + // Static route found, make sure this packet + // won't be sent on any other interface + pktTag->PT_Flags |= PT_NB_DESTROY; + InterlockedIncrement (&NetbiosPacketsQuota); + // Make sure the packet has not traversed + // this network already + for (i=0, netListPtr=dataPtr+IPXH_HDRSIZE; iICB_Network) + break; + } + // Make sure we are allowed to send on this + // interface + if ((dstIf!=InternalInterface) + && (i==rtCount) // Has not already traversed + // this network + && IS_IF_ENABLED (dstIf) + && ((dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL) + || (dstIf->ICB_NetbiosDeliver + ==FWD_NB_DELIVER_STATIC) + || ((dstIf->ICB_NetbiosDeliver + ==FWD_NB_DELIVER_IF_UP) + && (dstIf->ICB_Stats.OperationalState + ==FWD_OPER_STATE_UP)))) { + NOTHING; + } + else { + // We have static route, but can't send it, + // no point to propagate in on other interfaces + // as well + ReleaseInterfaceReference (dstIf); + dstIf = NULL; + goto FreePacket; + } + } + else { // no static route + goto FindNextInterface; + } + } + else { // not a brand new packet (already sent on some + // interfaces) + + FindNextInterface: + + // Loop through the interface list till we find + // the one on which we can send + while ((dstIf=GetNextInterfaceReference (dstIf))!=NULL) { + // Check if we allowed to send on this interface + if (IS_IF_ENABLED (dstIf) + && ((dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL) + || ((dstIf->ICB_NetbiosDeliver + ==FWD_NB_DELIVER_IF_UP) + && (dstIf->ICB_Stats.OperationalState + ==FWD_OPER_STATE_UP)))) { + // Make sure the packet has not traversed + // this network already + for (i=0, netListPtr=dataPtr+IPXH_HDRSIZE; iICB_Network) + break; + } + // Network was not in the list + if (i==rtCount) + break; + } + } + } + // Save the destination interface in the packet + pktTag->PT_InterfaceReference = dstIf; + // Go ahead and send if we have a valid destination + if (dstIf!=NULL) { + SendPacket (dstIf, pktTag); + // The rest does not apply: if the packet was sent or + // failed, it will be queued back to NetbiosQueue; + // if it was queued to interface to be connected, + // a copy of it will be queued to NetbiosQueue + continue; + } + // else no more destinations to send this packet on + } + else { // Packet has to be destroyed + if (pktTag->PT_InterfaceReference!=NULL) + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); + } + + FreePacket: + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: No more interfaces for nb packet %08lx.\n", + pktTag)); + if (MeasuringPerformance + && (pktTag->PT_PerfCounter!=0)) { + LARGE_INTEGER PerfCounter = KeQueryPerformanceCounter (NULL); + PerfCounter.QuadPart -= pktTag->PT_PerfCounter; + KeAcquireSpinLock (&PerfCounterLock, &oldIRQL); + PerfBlock.TotalNbPacketProcessingTime += PerfCounter.QuadPart; + PerfBlock.NbPacketCounter += 1; + if (PerfBlock.MaxNbPacketProcessingTime < PerfCounter.QuadPart) + PerfBlock.MaxNbPacketProcessingTime = PerfCounter.QuadPart; + KeReleaseSpinLock (&PerfCounterLock, oldIRQL); + } + if (!(pktTag->PT_Flags&PT_NB_DESTROY)) + InterlockedIncrement (&NetbiosPacketsQuota); + FreePacket (pktTag); + } while (!IsListEmpty (&tempQueue)); + + KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL); + if (IsListEmpty (&NetbiosQueue) + || !EnterForwarder ()) { + NetbiosWorkerScheduled = FALSE; + } + else { + ExQueueWorkItem (&NetbiosWorker, DelayedWorkQueue); + } + } + KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL); + LeaveForwarder (); +} + +/*++ +******************************************************************* + P r o c e s s N e t b i o s P a c k e t + +Routine Description: + Processes received netbios broadcast packet (checks network list + and source filter, updates input statistics) +Arguments: + srcIf - interfae on which packet was received + pktTag - netbios packet +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessNetbiosPacket ( + PINTERFACE_CB srcIf, + PPACKET_TAG pktTag + ) { + PUCHAR dataPtr; + UINT rtCount; + UINT i; + KIRQL oldIRQL; + + + dataPtr = pktTag->PT_Data; + rtCount = *(dataPtr+IPXH_XPORTCTL); + + // Check if source network is already in the packet network list + for (i=0, dataPtr+=IPXH_HDRSIZE; iICB_Network==GETULONG (dataPtr)) + break; + } + // We scaned the whole list and we haven't found it + if (i==rtCount) { + FILTER_ACTION action; + action = FltFilter (pktTag->PT_Data, + GETUSHORT (pktTag->PT_Data+IPXH_LENGTH), + srcIf->ICB_FilterInContext, NULL); + // Apply the input filter + if (action==FILTER_PERMIT) { + InterlockedIncrement (&srcIf->ICB_Stats.NetbiosReceived); + InterlockedIncrement (&srcIf->ICB_Stats.InDelivers); + PUTULONG (srcIf->ICB_Network, dataPtr); + *(pktTag->PT_Data+IPXH_XPORTCTL) += 1; + IPX_NODE_CPY (pktTag->PT_Target.MacAddress, BROADCAST_NODE); + // Initialize the packet + pktTag->PT_InterfaceReference = NULL; // not yet sent on any + // interfaces + pktTag->PT_Flags = 0; // No flags + QueueNetbiosPacket (pktTag); + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Queued nb packet %08lx from if %ld.\n", + pktTag, srcIf->ICB_Index)); + } + else { + ASSERT (action==FILTER_DENY_IN); + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: Filtered out nb packet %08lx" + " from if %ld.\n", pktTag, srcIf->ICB_Index)); + InterlockedIncrement (&NetbiosPacketsQuota); + InterlockedIncrement (&srcIf->ICB_Stats.InFiltered); + FreePacket (pktTag); + } + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: Source net is already in nb packet %08lx" + " from if %ld.\n", pktTag, srcIf->ICB_Index)); + InterlockedIncrement (&NetbiosPacketsQuota); + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + FreePacket (pktTag); + } + ReleaseInterfaceReference (srcIf); + +} + + +/*++ +******************************************************************* + D e l e t e N e t b i o s Q u e u e + +Routine Description: + Deletes the netbios bradcast queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteNetbiosQueue ( + void + ) { + while (!IsListEmpty (&NetbiosQueue)) { + PPACKET_TAG pktTag = CONTAINING_RECORD (NetbiosQueue.Flink, + PACKET_TAG, + PT_QueueLink); + RemoveEntryList (&pktTag->PT_QueueLink); + if (pktTag->PT_InterfaceReference!=NULL) { + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); + } + FreePacket (pktTag); + } +} + diff --git a/private/ntos/tdi/isn/fwd/netbios.h b/private/ntos/tdi/isn/fwd/netbios.h new file mode 100644 index 000000000..ffa6da1c4 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/netbios.h @@ -0,0 +1,134 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\netbios.h + +Abstract: + Netbios packet processing + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef IPXFWD_NETBIOS +#define IPXFWD_NETBIOS + +extern LIST_ENTRY NetbiosQueue; +extern KSPIN_LOCK NetbiosQueueLock; +extern WORK_QUEUE_ITEM NetbiosWorker; +extern BOOLEAN NetbiosWorkerScheduled; +extern ULONG NetbiosPacketsQuota; +extern ULONG MaxNetbiosPacketsQueued; +#define DEF_MAX_NETBIOS_PACKETS_QUEUED 256 + + +/*++ +******************************************************************* + I n i t i a l i z e N e t b i o s Q u e u e + +Routine Description: + Initializes the netbios bradcast queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +//VOID +//InitializeNetbiosQueue ( +// void +// ) +#define InitializeNetbiosQueue() { \ + InitializeListHead (&NetbiosQueue); \ + KeInitializeSpinLock (&NetbiosQueueLock); \ + ExInitializeWorkItem (&NetbiosWorker, &ProcessNetbiosQueue, NULL);\ + NetbiosWorkerScheduled = FALSE; \ + NetbiosPacketsQuota = MaxNetbiosPacketsQueued; \ +} + +/*++ +******************************************************************* + D e l e t e N e t b i o s Q u e u e + +Routine Description: + Deletes the netbios bradcast queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteNetbiosQueue ( + void + ); + + +/*++ +******************************************************************* + P r o c e s s N e t b i o s Q u e u e + +Routine Description: + Process packets in the netbios bradcast queue +Arguments: + Context - unused +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessNetbiosQueue ( + PVOID Context + ); + +/*++ +******************************************************************* + P r o c e s s N e t b i o s P a c k e t + +Routine Description: + Processes received netbios broadcast packet +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessNetbiosPacket ( + PINTERFACE_CB srcIf, + PPACKET_TAG pktTag + ); + + +#define QueueNetbiosPacket(pktTag) { \ + KIRQL oldIRQL; \ + KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL); \ + InsertTailList (&NetbiosQueue, &pktTag->PT_QueueLink); \ + KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL); \ +} + +#define ScheduleNetbiosWorker() { \ + KIRQL oldIRQL; \ + KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL); \ + if (!NetbiosWorkerScheduled \ + && !IsListEmpty (&NetbiosQueue) \ + && EnterForwarder ()) { \ + NetbiosWorkerScheduled = TRUE; \ + ExQueueWorkItem (&NetbiosWorker, DelayedWorkQueue); \ + } \ + KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL); \ +} + +#endif + diff --git a/private/ntos/tdi/isn/fwd/nwlnkfwd.rc b/private/ntos/tdi/isn/fwd/nwlnkfwd.rc new file mode 100644 index 000000000..477fd6e01 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/nwlnkfwd.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 Forwarder Driver" +#define VER_INTERNALNAME_STR "nwlnkfwd.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkfwd.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/fwd/packets.c b/private/ntos/tdi/isn/fwd/packets.c new file mode 100644 index 000000000..9577a6d2c --- /dev/null +++ b/private/ntos/tdi/isn/fwd/packets.c @@ -0,0 +1,528 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\packets.c + +Abstract: + IPX Forwarder Driver packet allocator + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + +ULONG RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT; +ULONG MaxRcvPktsPoolSize =0; +ULONG RcvPktsPoolSize = 0; +KSPIN_LOCK AllocatorLock; +const LONGLONG SegmentTimeout = -10i64*10000000i64; + +SEGMENT_LIST ListEther={1500}; +SEGMENT_LIST ListTR4={4500}; +SEGMENT_LIST ListTR16={17986}; + +PSEGMENT_LIST SegmentMap[FRAME_SIZE_VARIATIONS][FRAME_SIZE_VARIATIONS] = { + {&ListEther, &ListEther, &ListEther}, + {&ListEther, &ListTR4, &ListTR4}, + {&ListEther, &ListTR4, &ListTR16} +}; + +VOID +AllocationWorker ( + PVOID Context + ); + +VOID +SegmentTimeoutDpc ( + PKDPC dpc, + PVOID Context, + PVOID SystemArgument1, + PVOID SystemArgument2 + ); + +/*++ +******************************************************************* + C r e a t e S e g m e n t + +Routine Description: + Allocates and initializes packet segment +Arguments: + list - segment list to which new segment will be added +Return Value: + Pointer to allocated segment, NULL if fails + +******************************************************************* +--*/ +PPACKET_SEGMENT +CreateSegment ( + PSEGMENT_LIST list + ) { + KIRQL oldIRQL; + NDIS_STATUS status; + PPACKET_SEGMENT segment; + ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize + +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers); + if (MaxRcvPktsPoolSize!=0) { + // Check if this allocation would exceed the limit + KeAcquireSpinLock (&AllocatorLock, &oldIRQL); + if (RcvPktsPoolSize+segmentsizePS_SegmentList = list; + segment->PS_FreeHead = NULL; + segment->PS_BusyCount = 0; + KeQuerySystemTime ((PLARGE_INTEGER)&segment->PS_FreeStamp); + NdisAllocatePacketPool ( + &status, + &segment->PS_PacketPool, + list->SL_BlockCount, + IPXMacHeaderSize + +FIELD_OFFSET (PACKET_TAG, PT_MacHeader)); + if (status==NDIS_STATUS_SUCCESS) { + NdisAllocateBufferPool ( + &status, + &segment->PS_BufferPool, + list->SL_BlockCount*2); + if (status==NDIS_STATUS_SUCCESS) { + PUCHAR bufferptr = segment->PS_Buffers; + PNDIS_PACKET packetDscr; + PNDIS_BUFFER bufferDscr; + PPACKET_TAG packetTag; + ULONG i; + + for (i=0; iSL_BlockCount; i++, + bufferptr+=list->SL_BlockSize) { + NdisAllocatePacket ( + &status, + &packetDscr, + segment->PS_PacketPool); + ASSERT (status==NDIS_STATUS_SUCCESS); + + packetTag = (PPACKET_TAG)packetDscr->ProtocolReserved; + packetTag->PT_Segment = segment; + packetTag->PT_Data = bufferptr; + packetTag->PT_InterfaceReference = NULL; + + NdisAllocateBuffer ( + &status, + &packetTag->PT_MacHdrBufDscr, + segment->PS_BufferPool, + packetTag->PT_MacHeader, + IPXMacHeaderSize); + ASSERT (status==NDIS_STATUS_SUCCESS); + + NdisAllocateBuffer ( + &status, + &bufferDscr, + segment->PS_BufferPool, + bufferptr, + list->SL_BlockSize); + ASSERT (status==NDIS_STATUS_SUCCESS); + NdisChainBufferAtFront (packetDscr, bufferDscr); + + packetTag->PT_Next = segment->PS_FreeHead; + segment->PS_FreeHead = packetTag; + } + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING, + ("IpxFwd: Allocated packet segment %08lx for list %ld.\n", + segment, list->SL_BlockSize)); + return segment; + } + else { + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR, + ("IpxFwd: Failed to allocate buffer pool" + " for new segment in list %ld.\n", + list->SL_BlockSize)); + } + NdisFreePacketPool (segment->PS_PacketPool); + } + else { + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR, + ("IpxFwd: Failed to allocate packet pool" + " for new segment in list %ld.\n", + list->SL_BlockSize)); + } + ExFreePool (segment); + } + else { + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR, + ("IpxFwd: Failed to allocate new segment for list %ld.\n", + list->SL_BlockSize)); + } + + return NULL; +} + + +/*++ +******************************************************************* + D e l e t e S e g m e n t + +Routine Description: + Frees packet segment +Arguments: + segment - segment to free +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteSegment ( + PPACKET_SEGMENT segment + ) { + PSEGMENT_LIST list = segment->PS_SegmentList; + + ASSERT (segment->PS_BusyCount == 0); + // Free all NDIS packet and buffer descriptors first + while (segment->PS_FreeHead!=NULL) { + PNDIS_BUFFER bufferDscr; + PPACKET_TAG packetTag = segment->PS_FreeHead; + PNDIS_PACKET packetDscr = CONTAINING_RECORD (packetTag, + NDIS_PACKET, ProtocolReserved); + + segment->PS_FreeHead = packetTag->PT_Next; + + ASSERT (packetTag->PT_MacHdrBufDscr!=NULL); + NdisFreeBuffer (packetTag->PT_MacHdrBufDscr); + + NdisUnchainBufferAtFront (packetDscr, &bufferDscr); + ASSERT (bufferDscr!=NULL); + NdisFreeBuffer (bufferDscr); + + NdisFreePacket (packetDscr); + } + NdisFreeBufferPool (segment->PS_BufferPool); + NdisFreePacketPool (segment->PS_PacketPool); + + // Decrement memory used if we have a quota + if (MaxRcvPktsPoolSize!=0) { + KIRQL oldIRQL; + ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize + +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers); + KeAcquireSpinLock (&AllocatorLock, &oldIRQL); + RcvPktsPoolSize -= segmentsize; + KeReleaseSpinLock (&AllocatorLock, oldIRQL); + } + ExFreePool (segment); + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING, + ("IpxFwd: Deleting segment %08lx in list %ld.\n", + segment, list->SL_BlockSize)); +} + + +/*++ +******************************************************************* + R e g i s t e r P a c k e t C o n s u m e r + +Routine Description: + Registers a consumer (bound interface) of packets of the + given size +Arguments: + pktsize - maximum size of packets needed + listId - buffer to return packet list id where packets + of required size are located +Return Value: + STATUS_SUCCESS - registration succeded + STATUS_INSUFFICIENT_RESOURCES - not enogh resources to register + +******************************************************************* +--*/ +NTSTATUS +RegisterPacketConsumer ( + IN ULONG pktsize, + OUT INT *listID + ) { + NTSTATUS status=STATUS_SUCCESS; + KIRQL oldIRQL; + PSEGMENT_LIST list; + INT i; + LONG addRefCount = 1; + + KeAcquireSpinLock (&AllocatorLock, &oldIRQL); + ASSERT (pktsize<=SegmentMap[FRAME_SIZE_VARIATIONS-1] + [FRAME_SIZE_VARIATIONS-1]->SL_BlockSize); + + for (i=0; iSL_BlockSize) { + list->SL_RefCount += 1; + *listID = i; + break; + } + } + KeReleaseSpinLock (&AllocatorLock, oldIRQL); + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING, + ("IpxFwd: Registered packet consumer, pktsz: %ld, list: %ld.\n", + pktsize, list->SL_BlockSize)); + return status; +} + +/*++ +******************************************************************* + D e r e g i s t e r P a c k e t C o n s u m e r + +Routine Description: + Deregisters a consumer (bound interface) of packets of the + given size +Arguments: + listId - packet list id used by the consumer +Return Value: + None + +******************************************************************* +--*/ +VOID +DeregisterPacketConsumer ( + IN INT listID + ) { + KIRQL oldIRQL; + PSEGMENT_LIST list; + + ASSERT ((listID>=0) && (listIDSL_RefCount>0); + + list->SL_RefCount -= 1; + + KeReleaseSpinLock (&AllocatorLock, oldIRQL); + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING, + ("IpxFwd: Deregistered packet consumer, list: %ld.\n", + list->SL_BlockSize)); + + } + +/*++ +******************************************************************* + I n i t i a l i z e S e g m e n t L i s t + +Routine Description: + Initializes list of packet segments +Arguments: + list - list to initalize +Return Value: + None + +******************************************************************* +--*/ +VOID +InitializeSegmentList( + PSEGMENT_LIST list + ) { + InitializeListHead (&list->SL_Head); + list->SL_FreeCount = 0; + // Make sure we don't have any leftover larger than + // the buffer size (kernel memory allocator + // allocates full pages) + list->SL_BlockCount = + (ROUND_TO_PAGES ( + list->SL_BlockSize*RcvPktsPerSegment + +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers)) + -FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers)) + /list->SL_BlockSize; + list->SL_LowCount = list->SL_BlockCount/2; + list->SL_RefCount = 0; + list->SL_AllocatorPending = FALSE; + list->SL_TimerDpcPending = FALSE; + KeInitializeSpinLock (&list->SL_Lock); + KeInitializeTimer (&list->SL_Timer); + KeInitializeDpc (&list->SL_TimerDpc, SegmentTimeoutDpc, list); + ExInitializeWorkItem (&list->SL_Allocator, AllocationWorker, list); +} + +/*++ +******************************************************************* + D e l e t e S e g m e n t L i s t + +Routine Description: + Deletes list of packet segments +Arguments: + list - list to delete +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteSegmentList ( + PSEGMENT_LIST list + ) { + KeCancelTimer (&list->SL_Timer); + while (!IsListEmpty (&list->SL_Head)) { + PPACKET_SEGMENT segment; + segment = CONTAINING_RECORD (list->SL_Head.Blink, + PACKET_SEGMENT, PS_Link); + + RemoveEntryList (&segment->PS_Link); + DeleteSegment (segment); + } +} + + +/*++ +******************************************************************* + S e g m e n t T i m e o u t D p c + +Routine Description: + Timer DPC that launches allocator worker to get rid of unused + segments +Arguments: + Context - segment list to check for unused segments +Return Value: + None + +******************************************************************* +--*/ +VOID +SegmentTimeoutDpc ( + PKDPC dpc, + PVOID Context, + PVOID SystemArgument1, + PVOID SystemArgument2 + ) { +#define list ((PSEGMENT_LIST)Context) + KIRQL oldIRQL; + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION, + ("IpxFwd: Segment timed out in list: %ld.\n", + list->SL_BlockSize)); + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); + list->SL_TimerDpcPending = FALSE; + if (!list->SL_AllocatorPending + && (list->SL_FreeCount>=list->SL_BlockCount) + && EnterForwarder ()) { + list->SL_AllocatorPending = TRUE; + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + ExQueueWorkItem (&list->SL_Allocator, DelayedWorkQueue); + } + else { + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + } + LeaveForwarder (); +#undef list +} + + +/*++ +******************************************************************* + A l l o c a t i o n W o r k e r + +Routine Description: + Adds new segment or releases unused segments from the list + depending on the free packet count and time that segments + are not used +Arguments: + Context - packet list to process +Return Value: + None + +******************************************************************* +--*/ +VOID +AllocationWorker ( + PVOID Context + ) { +#define list ((PSEGMENT_LIST)Context) + KIRQL oldIRQL; + PPACKET_SEGMENT segment = NULL; + LONGLONG curTime; + + IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION, + ("IpxFwd: Allocating/scavenging segment(s) in list: %ld.\n", + list->SL_BlockSize)); + KeQuerySystemTime ((PLARGE_INTEGER)&curTime); + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); + list->SL_AllocatorPending = FALSE; + if (list->SL_FreeCountSL_BlockCount) { + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + // First allocate a segment + segment = CreateSegment (list); + if (segment!=NULL) { + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); + InsertTailList (&list->SL_Head, &segment->PS_Link); + list->SL_FreeCount += list->SL_BlockCount; + if (!list->SL_TimerDpcPending + && EnterForwarder ()) { + list->SL_TimerDpcPending = TRUE; + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + KeSetTimer (&list->SL_Timer, *((PLARGE_INTEGER)&SegmentTimeout), &list->SL_TimerDpc); + } + else { + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + } + } + } + else { + // Make sure that there is either more than segment in the list + // or there is one and no registered users + if (!IsListEmpty (&list->SL_Head)) { + segment = CONTAINING_RECORD (list->SL_Head.Blink, + PACKET_SEGMENT, PS_Link); + // Check for all segments with no used blocks + // except for the last one (delete event the last + // one if there are no clients) + while ((segment->PS_BusyCount==0) + && ((list->SL_Head.Flink!=&segment->PS_Link) + || (list->SL_RefCount<=0))) { + LONGLONG timeDiff; + // Check if it has not been used for long enough + timeDiff = SegmentTimeout - (segment->PS_FreeStamp-curTime); + if (timeDiff>=0) { + // Delete the segment + RemoveEntryList (&segment->PS_Link); + list->SL_FreeCount -= list->SL_BlockCount; + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + DeleteSegment (segment); + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); + if (!IsListEmpty (&list->SL_Head)) { + segment = CONTAINING_RECORD (list->SL_Head.Blink, + PACKET_SEGMENT, PS_Link); + continue; + } + } + else { // Reschedule the timer otherwise + if (!list->SL_TimerDpcPending + && EnterForwarder ()) { + list->SL_TimerDpcPending = TRUE; + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + KeSetTimer (&list->SL_Timer, + *((PLARGE_INTEGER)&timeDiff), + &list->SL_TimerDpc); + goto ExitAllocator; // Spinlock is already released + } + } + break; + } // while + } // if (IsListEmpty) + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); + } +ExitAllocator: + LeaveForwarder (); +#undef list +} + + diff --git a/private/ntos/tdi/isn/fwd/packets.h b/private/ntos/tdi/isn/fwd/packets.h new file mode 100644 index 000000000..98de68377 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/packets.h @@ -0,0 +1,464 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\packets.h + +Abstract: + IPX Forwarder Driver packet allocator + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_PACKETS_ +#define _IPXFWD_PACKETS_ + +// Forward structure prototypes +struct _SEGMENT_LIST; +typedef struct _SEGMENT_LIST SEGMENT_LIST, *PSEGMENT_LIST; +struct _PACKET_SEGMENT; +typedef struct _PACKET_SEGMENT PACKET_SEGMENT, *PPACKET_SEGMENT; +struct _PACKET_TAG; +typedef struct _PACKET_TAG PACKET_TAG, *PPACKET_TAG; + +// Forwarder data associated with each packet it allocates +struct _PACKET_TAG { + union { + UCHAR PT_Identifier; // this should be IDENTIFIER_RIP + PPACKET_TAG PT_Next; // link in packet segment + }; + union { + PVOID SEND_RESERVED[SEND_RESERVED_COMMON_SIZE]; // needed by ipx + // for padding on ethernet + PINTERFACE_CB PT_SourceIf; // Source interface reference needed + // for spoofing keep-alives and + // queuing connection requests + }; + PPACKET_SEGMENT PT_Segment; // segment where it belongs + LONGLONG PT_PerfCounter; + ULONG PT_Flags; +#define PT_NB_DESTROY 0x1 // NB packet to be not requeued +#define PT_SOURCE_IF 0x2 // Spoofing packet with src if reference + PUCHAR PT_Data; // Data buffer + PNDIS_BUFFER PT_MacHdrBufDscr; // buffer descriptor for + // mac header buffer required + // by IPX + PINTERFACE_CB PT_InterfaceReference; // points to the interface CB where + // it is queued + LIST_ENTRY PT_QueueLink; // links this packet in send queue + IPX_LOCAL_TARGET PT_Target; // destination target for ipx + // stack + UCHAR PT_MacHeader[1];// Mac header buffer reserved for IPX +}; + +// Segment of preallocated packets complete with buffers +struct _PACKET_SEGMENT { + LIST_ENTRY PS_Link; // Link in segment list + PSEGMENT_LIST PS_SegmentList; // Segment list we belong to + PPACKET_TAG PS_FreeHead; // List of free packets in + // this segment + ULONG PS_BusyCount; // Count of packets allocated + // from this segment + NDIS_HANDLE PS_PacketPool; // Pool of NDIS packet + // descriptors used by the + // packets in this segment + NDIS_HANDLE PS_BufferPool; // Pool of NDIS buffer + // descriptors used by the + // packets in this segment + LONGLONG PS_FreeStamp; // Time when last packet was freed + union { + UCHAR PS_Buffers[1]; // Memory used by buffers + LONGLONG PS_BuffersAlign; + }; +}; + + +// List of segment with preallocated packets +struct _SEGMENT_LIST { + const ULONG SL_BlockSize; // Size of packet's buffer + LIST_ENTRY SL_Head; // Head of the segment list + ULONG SL_FreeCount; // Total number of free packets + // in all segment in the list + ULONG SL_BlockCount; // Number of packets per segment + ULONG SL_LowCount; // Free count at which we + // will preallocate new segment + LONG SL_RefCount; // Number of consumers that are + // using packets in this list + BOOLEAN SL_TimerDpcPending; + BOOLEAN SL_AllocatorPending; + WORK_QUEUE_ITEM SL_Allocator; // Allocation work item + KTIMER SL_Timer; // Timer to free unused segments + KDPC SL_TimerDpc; // DPC of the timer of unused segments + KSPIN_LOCK SL_Lock; // Access control +}; + + +// The number of rcv packets per segment (config parameter) +#define MIN_RCV_PKTS_PER_SEGMENT 8 +#define DEF_RCV_PKTS_PER_SEGMENT 64 +#define MAX_RCV_PKTS_PER_SEGMENT 256 +extern ULONG RcvPktsPerSegment; + +// Maximum size of memory that can be used to allocate packets (config +// param). 0 means no limit +extern ULONG MaxRcvPktsPoolSize; + +// There are currently three known frame sizes: ethernet-1500, +// token ring 4k - 4500, token ring 16k - 17986 +#define FRAME_SIZE_VARIATIONS 3 + +// List of packet segments for Ethernet packets +extern SEGMENT_LIST ListEther; +// List of packet segments for Token Ring 4K packets +extern SEGMENT_LIST ListTR4; +// List of packet segments for Token Ring 16K packets +extern SEGMENT_LIST ListTR16; +// Mapping from src and destination packet size requirments +// to the appropriate segment list +extern PSEGMENT_LIST SegmentMap[FRAME_SIZE_VARIATIONS][FRAME_SIZE_VARIATIONS]; +// Timeout for unused segment +extern const LONGLONG SegmentTimeout; +extern KSPIN_LOCK AllocatorLock; + +/*++ +******************************************************************* + I n i t i a l i z e P a c k e t A l l o c a t o r + +Routine Description: + Initializes packet allocator +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +// VOID +// InitializePacketAllocator ( +// void +// ); +#define InitializePacketAllocator() { \ + KeInitializeSpinLock(&AllocatorLock); \ + InitializeSegmentList(&ListEther); \ + InitializeSegmentList(&ListTR4); \ + InitializeSegmentList(&ListTR16); \ +} + +/*++ +******************************************************************* + D e l e t e P a c k e t A l l o c a t o r + +Routine Description: + Disposes of all resources in packet allocator +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +// VOID +// DeletePacketAllocator ( +// void +// ); +#define DeletePacketAllocator() { \ + DeleteSegmentList(&ListEther); \ + DeleteSegmentList(&ListTR4); \ + DeleteSegmentList(&ListTR16); \ +} + + +/*++ +******************************************************************* + A l l o c a t e P a c k e t + +Routine Description: + Allocate packet for source - destination combination +Arguments: + srcListId - identifies max frame size for source interface + dstListId - identifies max frame size for destination + packet - receives pointer to allocated packet or NULL if allocation + fails +Return Value: + None + +******************************************************************* +--*/ +// VOID +// AllocatePacket ( +// IN INT srcListId, +// IN INT dstListId, +// OUT PPACKET_TAG packet +// ); +#define AllocatePacket(srcListId,dstListId,packet) { \ + PSEGMENT_LIST list; \ + ASSERT ((srcListId>=0) && (srcListId=0) && (dstListIdPT_Segment->PS_SegmentList; \ + AllocatePacketFromList(list,dst); \ +} + + +/*++ +******************************************************************* + A l l o c a t e P a c k e t F r o m L i s t + +Routine Description: + Allocate packet from specified packet segment list +Arguments: + list - list from which to allocate + packet - receives pointer to allocated packet or NULL if allocation + fails +Return Value: + None + +******************************************************************* +--*/ +// VOID +// AllocatePacketFromList ( +// IN PSEGMENT_LIST list +// OUT PPACKET_TAG packet +// ); +#define AllocatePacketFromList(list,packet) { \ + PPACKET_SEGMENT segment; \ + KIRQL oldIRQL; \ + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); \ + do { \ + if (list->SL_FreeCount>0) { \ + segment = CONTAINING_RECORD (list->SL_Head.Flink, \ + PACKET_SEGMENT, PS_Link); \ + while (segment->PS_FreeHead==NULL) { \ + segment = CONTAINING_RECORD (segment->PS_Link.Flink, \ + PACKET_SEGMENT, PS_Link); \ + ASSERT (&segment->PS_Link!=&list->SL_Head); \ + } \ + list->SL_FreeCount -= 1; \ + if ((list->SL_FreeCountSL_LowCount) \ + && !list->SL_AllocatorPending \ + && EnterForwarder ()) { \ + list->SL_AllocatorPending = TRUE; \ + ExQueueWorkItem (&list->SL_Allocator, DelayedWorkQueue);\ + } \ + } \ + else { \ + segment = CreateSegment (list); \ + if (segment!=NULL) { \ + InsertTailList (&list->SL_Head, &segment->PS_Link); \ + segment->PS_SegmentList = list; \ + list->SL_FreeCount = list->SL_BlockCount-1; \ + } \ + else { \ + packet = NULL; \ + break; \ + } \ + } \ + packet = segment->PS_FreeHead; \ + segment->PS_FreeHead = packet->PT_Next; \ + segment->PS_BusyCount += 1; \ + packet->PT_Identifier = IDENTIFIER_RIP; \ + packet->PT_Flags = 0; \ + } \ + while (FALSE); \ + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); \ +} + +/*++ +******************************************************************* + F r e e P a c k e t + +Routine Description: + Free allocated packet +Arguments: + packet - packet to free +Return Value: + None + +******************************************************************* +--*/ +// VOID +// FreePacket ( +// IN PPACKET_TAG packet +// ); +#define FreePacket(packet) { \ + PPACKET_SEGMENT segment=packet->PT_Segment; \ + PSEGMENT_LIST list; \ + KIRQL oldIRQL; \ + list = segment->PS_SegmentList; \ + KeAcquireSpinLock (&list->SL_Lock, &oldIRQL); \ + packet->PT_Next = segment->PS_FreeHead; \ + segment->PS_FreeHead = packet; \ + list->SL_FreeCount += 1; \ + segment->PS_BusyCount -= 1; \ + if (segment->PS_BusyCount==0) { \ + if (list->SL_TimerDpcPending) { \ + KeQuerySystemTime ((PLARGE_INTEGER)&segment->PS_FreeStamp); \ + KeReleaseSpinLock (&list->SL_Lock, oldIRQL);\ + } \ + else if (EnterForwarder ()) { \ + list->SL_TimerDpcPending = TRUE; \ + KeReleaseSpinLock (&list->SL_Lock, oldIRQL);\ + KeSetTimer (&list->SL_Timer, \ + *((PLARGE_INTEGER)&SegmentTimeout), \ + &list->SL_TimerDpc); \ + } \ + else { \ + KeReleaseSpinLock (&list->SL_Lock, oldIRQL);\ + } \ + } \ + else { \ + KeReleaseSpinLock (&list->SL_Lock, oldIRQL); \ + } \ +} + + +/*++ +******************************************************************* + C r e a t e S e g m e n t + +Routine Description: + Allocates and initializes packet segment +Arguments: + list - segment list to which new segment will be added +Return Value: + Pointer to allocated segment, NULL if fails + +******************************************************************* +--*/ +PPACKET_SEGMENT +CreateSegment ( + PSEGMENT_LIST list + ); + +/*++ +******************************************************************* + D e l e t e S e g m e n t + +Routine Description: + Frees packet segment +Arguments: + segment - segment to free +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteSegment ( + PPACKET_SEGMENT segment + ); + +/*++ +******************************************************************* + R e g i s t e r P a c k e t C o n s u m e r + +Routine Description: + Registers a consumer (bound interface) of packets of the + given size +Arguments: + pktsize - maximum size of packets needed + listId - buffer to return packet list id where packets + of required size are located +Return Value: + STATUS_SUCCESS - registration succeded + STATUS_INSUFFICIENT_RESOURCES - not enogh resources to register + +******************************************************************* +--*/ +NTSTATUS +RegisterPacketConsumer ( + IN ULONG pktsize, + OUT INT *listID + ); + +/*++ +******************************************************************* + D e r e g i s t e r P a c k e t C o n s u m e r + +Routine Description: + Deregisters a consumer (bound interface) of packets of the + given size +Arguments: + listId - packet list id used by the consumer +Return Value: + None + +******************************************************************* +--*/ +VOID +DeregisterPacketConsumer ( + IN INT listID + ); + +/*++ +******************************************************************* + I n i t i a l i z e S e g m e n t L i s t + +Routine Description: + Initializes list of packet segments +Arguments: + list - list to initalize +Return Value: + None + +******************************************************************* +--*/ +VOID +InitializeSegmentList( + PSEGMENT_LIST list + ); + + +/*++ +******************************************************************* + D e l e t e S e g m e n t L i s t + +Routine Description: + Deletes list of packet segments +Arguments: + list - list to delete +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteSegmentList ( + PSEGMENT_LIST list + ); + +#endif + diff --git a/private/ntos/tdi/isn/fwd/precomp.h b/private/ntos/tdi/isn/fwd/precomp.h new file mode 100644 index 000000000..bc471bd40 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/precomp.h @@ -0,0 +1,61 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\precomp.h + +Abstract: + IPX Forwarder driver precompiled header file + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#define ISN_NT 1 +#define NT 1 + +#if DBG +#define DEBUG 1 +#endif + +// System includes +#include +#include +#include +#include +#include + +// IPX shared includes +#include "bind.h" +#include "ipxfwd.h" +#include "ipxfltif.h" + +// Constants and macros +#include "fwddefs.h" +#include "rwlock.h" + +// Internal module prototypes +#include "tables.h" +#include "registry.h" +#include "packets.h" +#include "ipxbind.h" +#include "rcvind.h" +#include "send.h" +#include "netbios.h" +#include "lineind.h" +#include "ddreqs.h" +#include "driver.h" +#include "filterif.h" +#include "debug.h" + +#pragma hdrstop + + + diff --git a/private/ntos/tdi/isn/fwd/rcvind.c b/private/ntos/tdi/isn/fwd/rcvind.c new file mode 100644 index 000000000..7c4bc2d39 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/rcvind.c @@ -0,0 +1,825 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\rcvind.c + +Abstract: + Receive indication processing + +Author: + + Vadim Eydelman + +Revision History: + +--*/ +#include "precomp.h" + +#if DBG +VOID +DbgFilterReceivedPacket(PUCHAR hdrp); +#endif + +// Doesn't allow accepting packets (for routing) from dial-in clients +BOOLEAN ThisMachineOnly = FALSE; + +/*++ +******************************************************************* + F w R e c e i v e + +Routine Description: + Called by the IPX stack to indicate that the IPX packet was + received by the NIC dirver. Only external destined packets are + indicated by this routine (with the exception of Netbios boradcasts + that indicated both for internal and external handlers) +Arguments: + MacBindingHandle - handle of NIC driver + MaxReceiveContext - NIC driver context + RemoteAddress - sender's address + MacOptions - + LookaheadBuffer - packet lookahead buffer that contains complete + IPX header + LookaheadBufferSize - its size (at least 30 bytes) + LookaheadBufferOffset - offset of lookahead buffer in the physical + packet +Return Value: + TRUE if we take the MDL chain to return later with NdisReturnPacket + +******************************************************************* +--*/ +BOOLEAN +IpxFwdReceive ( + NDIS_HANDLE MacBindingHandle, + NDIS_HANDLE MacReceiveContext, + ULONG Context, + PIPX_LOCAL_TARGET RemoteAddress, + ULONG MacOptions, + PUCHAR LookaheadBuffer, + UINT LookaheadBufferSize, + UINT LookaheadBufferOffset, + UINT PacketSize, + PMDL pMdl + ) { + PINTERFACE_CB srcIf, dstIf; + PPACKET_TAG pktTag; + PNDIS_PACKET pktDscr; + NDIS_STATUS status; + UINT BytesTransferred; + LARGE_INTEGER PerfCounter; + + // check that our configuration process has terminated OK + if (!EnterForwarder ()) { + return FALSE; + } + + if (!MeasuringPerformance) { + PerfCounter.QuadPart = 0; + } + else { +#if DBG + static LONGLONG LastCall = 0; + KIRQL oldIRQL; +#endif + PerfCounter = KeQueryPerformanceCounter (NULL); +#if DBG + KeAcquireSpinLock (&PerfCounterLock, &oldIRQL); + ASSERT (PerfCounter.QuadPart-LastCallNicId); + // Check if interface is valid + if (srcIf!=NULL) { + USHORT pktlen; + ULONG dstNet; + KIRQL oldIRQL; + + dstNet = GETULONG (LookaheadBuffer + IPXH_DESTNET); + pktlen = GETUSHORT(LookaheadBuffer + IPXH_LENGTH); + + // check if we got the whole IPX header in the lookahead buffer + if ((LookaheadBufferSize >= IPXH_HDRSIZE) + && (*(LookaheadBuffer + IPXH_XPORTCTL) < 16) + && (pktlen<=PacketSize)) { + // Lock interface CB to ensure coherency of information in it + KeAcquireSpinLock(&srcIf->ICB_Lock, &oldIRQL); + // Check if shoud accept packets on this interface + if (IS_IF_ENABLED(srcIf) + && (srcIf->ICB_Stats.OperationalState!=FWD_OPER_STATE_DOWN) + && (!ThisMachineOnly + || (srcIf->ICB_InterfaceType + !=FWD_IF_REMOTE_WORKSTATION))) { + // Check for looped back packets + if (IPX_NODE_CMP (RemoteAddress->MacAddress, + srcIf->ICB_LocalNode)!=0) { + + // Separate processing of netbios broadcast packets (20) + if (*(LookaheadBuffer + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) { + PFWD_ROUTE dstRoute; + INT srcListId, dstListId; + // Temp IPX bug fix, they shou;d ensure that + // we only get packets that can be routed + if ((dstNet==srcIf->ICB_Network) + || (dstNet==InternalInterface->ICB_Network)) { + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + ReleaseInterfaceReference (srcIf); + LeaveForwarder (); + return FALSE; + } +// ASSERT (dstNet!=srcIf->ICB_Network); +// ASSERT ((InternalInterface==NULL) +// || (InternalInterface->ICB_Network==0) +// || (dstNet!=InternalInterface->ICB_Network)); + // Check if needed route is in cash + if ((srcIf->ICB_CashedRoute!=NULL) + && (dstNet==srcIf->ICB_CashedRoute->FR_Network) + // If route was changed or deleted, this will fail + && (srcIf->ICB_CashedRoute->FR_InterfaceReference + ==srcIf->ICB_CashedInterface)) { + dstIf = srcIf->ICB_CashedInterface; + dstRoute = srcIf->ICB_CashedRoute; + AcquireInterfaceReference (dstIf); + AcquireRouteReference (dstRoute); + IpxFwdDbgPrint (DBG_RECV, DBG_INFORMATION, + ("IpxFwd: Destination in cash.\n")); + } + else { // Find and cash the route + dstIf = FindDestination (dstNet, + LookaheadBuffer+IPXH_DESTNODE, + &dstRoute + ); + + if (dstIf!=NULL) { // If route is found + IpxFwdDbgPrint (DBG_RECV, DBG_INFORMATION, + ("IpxFwd: Found destination %0lx.\n", dstIf)); + // Don't cash global wan clients and + // routes to the same net + if ((dstNet!=GlobalNetwork) + && (dstIf!=srcIf)) { + if (srcIf->ICB_CashedInterface!=NULL) + ReleaseInterfaceReference (srcIf->ICB_CashedInterface); + if (srcIf->ICB_CashedRoute!=NULL) + ReleaseRouteReference (srcIf->ICB_CashedRoute); + srcIf->ICB_CashedInterface = dstIf; + srcIf->ICB_CashedRoute = dstRoute; + AcquireInterfaceReference (dstIf); + AcquireRouteReference (dstRoute); + } + } + else { // No route + InterlockedIncrement (&srcIf->ICB_Stats.InNoRoutes); + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_RECV, DBG_WARNING, + ("IpxFwd: No route for packet on interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + ReleaseInterfaceReference (srcIf); + LeaveForwarder (); + return FALSE; + } + } + srcListId = srcIf->ICB_PacketListId; + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + + // Check if destination if can take the packet + if (IS_IF_ENABLED (dstIf) + // If interface is UP check packet againts actual size limit + && (((dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) + && (PacketSize<=dstIf->ICB_Stats.MaxPacketSize)) + // if sleeping (WAN), check we can allocate it from WAN list + || ((dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_SLEEPING) + && (PacketSize<=WAN_PACKET_SIZE)) + // otherwise, interface is down and we can't take the packet + ) ){ + FILTER_ACTION action; + action = FltFilter (LookaheadBuffer, LookaheadBufferSize, + srcIf->ICB_FilterInContext, + dstIf->ICB_FilterOutContext); + if (action==FILTER_PERMIT) { + InterlockedIncrement (&srcIf->ICB_Stats.InDelivers); + dstListId = dstIf->ICB_PacketListId; + // try to get a packet from the rcv pkt pool + AllocatePacket (srcListId, dstListId, pktTag); + if (pktTag!=NULL) { + // Set destination mac in local target if + // possible + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + if (dstIf->ICB_InterfaceType==FWD_IF_PERMANENT) { + // Permanent interface: send to the next + // hop router if net is not directly connected + // or to the dest node otherwise + if (dstNet!=dstIf->ICB_Network) { + IPX_NODE_CPY (pktTag->PT_Target.MacAddress, + dstRoute->FR_NextHopAddress); + } + else { + IPX_NODE_CPY (pktTag->PT_Target.MacAddress, + LookaheadBuffer+IPXH_DESTNODE); + } + } + else { // Demand dial interface: assumed to be + // point to point connection -> send to + // the other party if connection has already + // been made, otherwise wait till connected + if (dstIf->ICB_Stats.OperationalState + == FWD_OPER_STATE_UP) { + IPX_NODE_CPY (pktTag->PT_Target.MacAddress, + dstIf->ICB_RemoteNode); + } // Copy source mac address and nic id in case + // we need to spoof this packet + else if ((*(LookaheadBuffer+IPXH_PKTTYPE)==0) + && (pktlen==IPXH_HDRSIZE+2) + && ((LookaheadBufferSizePT_Target.MacAddress, + RemoteAddress->MacAddress); + pktTag->PT_SourceIf = srcIf; + AcquireInterfaceReference (srcIf); + pktTag->PT_Flags |= PT_SOURCE_IF; + } + + } + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + ReleaseRouteReference (dstRoute); + goto GetPacket; + } + else { // Allocation failure + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + } + } + else {// Filtered out + if (action==FILTER_DENY_OUT) + InterlockedIncrement (&dstIf->ICB_Stats.OutFiltered); + else { + ASSERT (action==FILTER_DENY_IN); + InterlockedIncrement (&srcIf->ICB_Stats.InFiltered); + } + IpxFwdDbgPrint (DBG_RECV, DBG_WARNING, + ("IpxFwd: Filtered out" + " packet on interface %ld (icb:%0lx)," + " dst-%ld (icb %08lx) %08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstIf->ICB_Index, dstIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + } + } + else { // Destination interface is down + InterlockedIncrement (&srcIf->ICB_Stats.InDelivers); + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + IpxFwdDbgPrint (DBG_RECV, DBG_WARNING, + ("IpxFwd: Dest interface %ld (icb %08lx) down" + " for packet on interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + dstIf->ICB_Index, dstIf, srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + } + ReleaseInterfaceReference (dstIf); + ReleaseRouteReference (dstRoute); + } + else { // if netbios + // check that this is a netbios bcast packet and + // didnt exceed the limit of routers to traverse + // and we can accept it on this interface + if (srcIf->ICB_NetbiosAccept + && (*(LookaheadBuffer + IPXH_XPORTCTL) < 8)) { + INT srcListId; + srcListId = srcIf->ICB_PacketListId; + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + // Check if packet is valid + if (IPX_NODE_CMP (LookaheadBuffer + IPXH_DESTNODE, + BROADCAST_NODE)==0) { + // Check if we haven't exceeded the quota + if (InterlockedDecrement (&NetbiosPacketsQuota)>=0) { + // try to get a packet from the rcv pkt pool + AllocatePacket (srcListId, srcListId, pktTag); + if (pktTag!=NULL) { + dstIf = srcIf; + AcquireInterfaceReference (dstIf); + goto GetPacket; + } + } + else {// Netbios quota exceded + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: Netbios quota exceded" + " for packet on interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + } + InterlockedIncrement (&NetbiosPacketsQuota); + } + else { // Bad netbios packet + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: Bad nb packet on interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + InterlockedIncrement (&srcIf->ICB_Stats.InHdrErrors); + } + } + else { // Netbios accept disabled or to many routers crossed + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: NB packet dropped on disabled interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + } + } // End netbios specific processing (else if netbios) + } + else { // Looped back packets discarded without counting + // (We shouldn't get them in IPX stack does the right job) + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + } + } + else { // Interface is down or disabled + KeReleaseSpinLock(&srcIf->ICB_Lock, oldIRQL); + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + IpxFwdDbgPrint (DBG_RECV, DBG_WARNING, + ("IpxFwd: Packet dropped on disabled interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + } + } + else { // Obvious header errors (shouldn't IPX do this for us ? + InterlockedIncrement (&srcIf->ICB_Stats.InHdrErrors); + IpxFwdDbgPrint (DBG_RECV, DBG_ERROR, + ("IpxFwd: Header errors in packet on interface %ld (icb:%0lx)," + " dst-%08lx:%02x%02x%02x%02x%02x%02x, type-%02x.\n", + srcIf->ICB_Index, srcIf, dstNet, + LookaheadBuffer[IPXH_DESTNODE], LookaheadBuffer[IPXH_DESTNODE+1], + LookaheadBuffer[IPXH_DESTNODE+2], LookaheadBuffer[IPXH_DESTNODE+3], + LookaheadBuffer[IPXH_DESTNODE+4], LookaheadBuffer[IPXH_DESTNODE+5], + LookaheadBuffer[IPXH_PKTTYPE])); + } + ReleaseInterfaceReference (srcIf); + } // We could not locate the interface from IPX supplied context: there + // is just a little time window when interface is deleted + // but IPX had already pushed the context on the stack + else { + IpxFwdDbgPrint (DBG_RECV, DBG_ERROR, + ("IpxFwd: Receive, type-%02x" + " - src interface context is invalid.\n", + LookaheadBuffer[IPXH_PKTTYPE])); + } + LeaveForwarder (); + return FALSE ; + +GetPacket: + + InterlockedIncrement (&srcIf->ICB_Stats.InDelivers); + ReleaseInterfaceReference (srcIf); + + pktDscr = CONTAINING_RECORD (pktTag, NDIS_PACKET, ProtocolReserved); + pktTag->PT_InterfaceReference = dstIf; + pktTag->PT_PerfCounter = PerfCounter.QuadPart; + + // try to get the packet data + IPXTransferData(&status, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset, // start of IPX header + PacketSize, // packet size starting at IPX header + pktDscr, + &BytesTransferred); + + if (status != NDIS_STATUS_PENDING) { + // complete the frame processing (LeaveForwarder will be called there) + IpxFwdTransferDataComplete(pktDscr, status, BytesTransferred); + } + return FALSE; +} + + +/*++ +******************************************************************* + F w T r a n s f e r D a t a C o m p l e t e + +Routine Description: + Called by the IPX stack when NIC driver completes data transger. +Arguments: + pktDscr - handle of NIC driver + status - result of the transfer + bytesTransferred - number of bytest trasferred +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdTransferDataComplete ( + PNDIS_PACKET pktDscr, + NDIS_STATUS status, + UINT bytesTransferred + ) { + PPACKET_TAG pktTag; + + pktTag = (PPACKET_TAG)(&pktDscr->ProtocolReserved); + + // If transfer failed, release the packet and interface + if (status==NDIS_STATUS_SUCCESS) { + if (*(pktTag->PT_Data + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + SendPacket (pktTag->PT_InterfaceReference, pktTag); + else + ProcessNetbiosPacket (pktTag->PT_InterfaceReference, pktTag); + + } + else { + IpxFwdDbgPrint (DBG_RECV, DBG_ERROR, + ("IpxFwd: Transfer data failed for packet %08lx on interface %08lx!\n", + pktTag, pktTag->PT_InterfaceReference)); + if (*(pktTag->PT_Data + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) { + InterlockedIncrement ( + &pktTag->PT_InterfaceReference->ICB_Stats.OutDiscards); + } + else { // For netbios packets interface reference is + // actually a source interface + InterlockedIncrement (&NetbiosPacketsQuota); + InterlockedIncrement ( + &pktTag->PT_InterfaceReference->ICB_Stats.InDiscards); + } + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); + FreePacket (pktTag); + } + + LeaveForwarder (); + return; +} + + +/*++ +******************************************************************* + F w R e c e i v e C o m p l e t e + +Routine Description: + + This routine receives control from the IPX driver after one or + more receive operations have completed and no receive is in progress. + It is called under less severe time constraints than IpxFwdReceive. + It is used to process netbios queue + +Arguments: + None +Return Value: + None +******************************************************************* +--*/ +VOID +IpxFwdReceiveComplete ( + USHORT NicId + ) { + + // check that our configuration process has terminated OK + if(!EnterForwarder ()) { + return; + } + IpxFwdDbgPrint (DBG_RECV, DBG_INFORMATION, ("IpxFwd: FwdReceiveComplete.\n")); + ScheduleNetbiosWorker (); + LeaveForwarder (); +} + +/*++ +******************************************************************* + I p x F w d I n t e r n a l R e c e i v e + +Routine Description: + Called by the IPX stack to indicate that the IPX packet destined + to local client was received by the NIC dirver. +Arguments: + Context - forwarder context associated with + the NIC (interface block pointer) + RemoteAddress - sender's address + LookaheadBuffer - packet lookahead buffer that contains complete + IPX header + LookaheadBufferSize - its size (at least 30 bytes) +Return Value: + STATUS_SUCCESS - the packet will be delivered to local destination + STATUS_UNSUCCESSFUL - the packet will be dropped + +******************************************************************* +--*/ +NTSTATUS +IpxFwdInternalReceive ( + IN ULONG Context, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PUCHAR LookAheadBuffer, + IN UINT LookAheadBufferSize + ) { + NTSTATUS status = STATUS_SUCCESS; + PINTERFACE_CB srcIf; + + if (!EnterForwarder ()) { + return STATUS_UNSUCCESSFUL; + } + if (Context!=VIRTUAL_NET_FORWARDER_CONTEXT) { + // Check if interface context supplied by IPX driver is valid + srcIf = InterfaceContextToReference ((PVOID)Context, RemoteAddress->NicId); + } + else { + srcIf = InternalInterface; + AcquireInterfaceReference (srcIf); + } + + if (srcIf!=NULL) { + // Check if we can accept on this interface + if (IS_IF_ENABLED (srcIf) + && (srcIf->ICB_Stats.OperationalState!=FWD_OPER_STATE_DOWN) + && ((*(LookAheadBuffer + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + || srcIf->ICB_NetbiosAccept)) { + // Check if we can accept on internal interface + if (IS_IF_ENABLED(InternalInterface)) { + FILTER_ACTION action; + action = FltFilter (LookAheadBuffer, LookAheadBufferSize, + srcIf->ICB_FilterInContext, + InternalInterface->ICB_FilterOutContext); + // Check the filter + if (action==FILTER_PERMIT) { + // Update source interface statistics + InterlockedIncrement (&srcIf->ICB_Stats.InDelivers); + // Handle NB packets separatedly + if (*(LookAheadBuffer + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) { + InterlockedIncrement ( + &InternalInterface->ICB_Stats.OutDelivers); + IpxFwdDbgPrint (DBG_INT_RECV, DBG_INFORMATION, + ("IpxFwd: FwdInternalReceive," + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x," + " type-%02x.\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5], + LookAheadBuffer[IPXH_PKTTYPE])); + } + else { + // Check if destination netbios name is staticly assigned to + // an external interface or netbios delivery options do not + // allow us to deliver this packet + PINTERFACE_CB dstIf; + USHORT dstSock = GETUSHORT (LookAheadBuffer+IPXH_DESTSOCK); + + InterlockedIncrement (&srcIf->ICB_Stats.NetbiosReceived); + // First try to find a static name if we have enough data + // in the lookahead buffer + if ((dstSock==IPX_NETBIOS_SOCKET) + && (LookAheadBufferSize>(NB_NAME+16))) + dstIf = FindNBDestination (LookAheadBuffer+(NB_NAME-IPXH_HDRSIZE)); + else if ((dstSock==IPX_SMB_NAME_SOCKET) + && (LookAheadBufferSize>(SMB_NAME+16))) + dstIf = FindNBDestination (LookAheadBuffer+(SMB_NAME-IPXH_HDRSIZE)); + else + dstIf = NULL; + // Now see, if we can deliver the packet + if ((((dstIf==NULL) || (dstIf==InternalInterface)) + && (InternalInterface->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL)) + || ((dstIf==InternalInterface) + && (InternalInterface->ICB_NetbiosDeliver==FWD_NB_DELIVER_STATIC))) { + InterlockedIncrement ( + &InternalInterface->ICB_Stats.NetbiosSent); + InterlockedIncrement ( + &InternalInterface->ICB_Stats.OutDelivers); + IpxFwdDbgPrint (DBG_INT_RECV, DBG_INFORMATION, + ("IpxFwd: FwdInternalReceive, NB" + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5])); + } + else { + InterlockedIncrement ( + &InternalInterface->ICB_Stats.OutDiscards); + IpxFwdDbgPrint (DBG_INT_RECV, DBG_WARNING, + ("IpxFwd: FwdInternalReceive, NB dropped because delivery disabled" + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5])); + status = STATUS_UNSUCCESSFUL; + } + if (dstIf!=NULL) + ReleaseInterfaceReference (dstIf); + } + } + else {// Filtered Out + if (action==FILTER_DENY_OUT) + InterlockedIncrement ( + &InternalInterface->ICB_Stats.OutFiltered); + else { + ASSERT (action==FILTER_DENY_IN); + InterlockedIncrement (&srcIf->ICB_Stats.InFiltered); + } + IpxFwdDbgPrint (DBG_INT_RECV, DBG_WARNING, + ("IpxFwd: FwdInternalReceive, filtered out" + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x," + " type-%02x.\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5], + LookAheadBuffer[IPXH_PKTTYPE])); + } + } + else {// Internal interface is disabled + InterlockedIncrement ( + &InternalInterface->ICB_Stats.OutDiscards); + status = STATUS_UNSUCCESSFUL; + IpxFwdDbgPrint (DBG_INT_RECV, DBG_WARNING, + ("IpxFwd: FwdInternalReceive, internal if disabled" + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x," + " type-%02x.\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5], + LookAheadBuffer[IPXH_PKTTYPE])); + } + } + else { // Disabled source interface + InterlockedIncrement (&srcIf->ICB_Stats.InDiscards); + IpxFwdDbgPrint (DBG_INT_RECV, DBG_ERROR, + ("IpxFwd: FwdInternalReceive, source if disabled" + " from %d(%lx)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x," + " type-%02x.\n", + srcIf->ICB_Index, srcIf, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5], + LookAheadBuffer[IPXH_PKTTYPE])); + status = STATUS_UNSUCCESSFUL; + } + ReleaseInterfaceReference (srcIf); + } + else { // Invalid source interface context + IpxFwdDbgPrint (DBG_INT_RECV, DBG_ERROR, + ("IpxFwd: FwdInternalReceive, source if context is invalid" + " from (%lx:%d)-%.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x," + " type-%02x.\n", + Context, RemoteAddress->NicId, + LookAheadBuffer[IPXH_SRCNET],LookAheadBuffer[IPXH_SRCNET+1], + LookAheadBuffer[IPXH_SRCNET+2],LookAheadBuffer[IPXH_SRCNET+3], + LookAheadBuffer[IPXH_SRCNODE],LookAheadBuffer[IPXH_SRCNODE+1], + LookAheadBuffer[IPXH_SRCNODE+2],LookAheadBuffer[IPXH_SRCNODE+3], + LookAheadBuffer[IPXH_SRCNODE+4],LookAheadBuffer[IPXH_SRCNODE+5], + LookAheadBuffer[IPXH_PKTTYPE])); + status = STATUS_UNSUCCESSFUL; + } + LeaveForwarder (); + return status; +} + +/*++ +******************************************************************* + D e l e t e R e c v Q u e u e + +Routine Description: + Initializes the netbios bradcast queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteRecvQueue ( + void + ) { +// while (!IsListEmpty (&RecvQueue)) { +// PPACKET_TAG pktTag = CONTAINING_RECORD (RecvQueue.Flink, +// PACKET_TAG, +// PT_QueueLink); +// RemoveEntryList (&pktTag->PT_QueueLink); +// if (pktTag->PT_InterfaceReference!=NULL) { +// ReleaseInterfaceReference (pktTag->PT_InterfaceReference); +// } +// FreePacket (pktTag); +// } +} +#if DBG + +ULONG DbgFilterTrap = 0; // 1 - on dst and src (net + node), + // 2 - on dst (net + node), + // 3 - on src (net + node), + // 4 - on dst (net + node + socket) + +UCHAR DbgFilterDstNet[4]; +UCHAR DbgFilterDstNode[6]; +UCHAR DbgFilterDstSocket[2]; +UCHAR DbgFilterSrcNet[4]; +UCHAR DbgFilterSrcNode[6]; +UCHAR DbgFilterSrcSocket[2]; +PUCHAR DbgFilterFrame; + +VOID +DbgFilterReceivedPacket(PUCHAR hdrp) +{ + switch(DbgFilterTrap) { + + case 1: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6) && + !memcmp(hdrp + IPXH_SRCNET, DbgFilterSrcNet, 4) && + !memcmp(hdrp + IPXH_SRCNODE, DbgFilterSrcNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 2: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 3: + + if(!memcmp(hdrp + IPXH_SRCNET, DbgFilterSrcNet, 4) && + !memcmp(hdrp + IPXH_SRCNODE, DbgFilterSrcNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 4: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6) && + !memcmp(hdrp + IPXH_DESTSOCK, DbgFilterDstSocket, 2)) { + + DbgBreakPoint(); + } + + break; + + default: + + break; + } + + DbgFilterFrame = hdrp; +} + +#endif + diff --git a/private/ntos/tdi/isn/fwd/rcvind.h b/private/ntos/tdi/isn/fwd/rcvind.h new file mode 100644 index 000000000..50f240bdd --- /dev/null +++ b/private/ntos/tdi/isn/fwd/rcvind.h @@ -0,0 +1,180 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\rcvind.h + +Abstract: + Receive indication processing + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_RCVIND +#define _IPXFWD_RCVIND + +// Doesn't allow accepting packets (for routing) from dial-in clients +extern BOOLEAN ThisMachineOnly; + +/*++ +******************************************************************* + I n i t i a l i z e R e c v Q u e u e + +Routine Description: + Initializes recv queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +//VOID +//DeleteRecvQueue ( +// void +// ) +#define InitializeRecvQueue() { \ +} + +/*++ +******************************************************************* + D e l e t e R e c v Q u e u e + +Routine Description: + Deletes recv queue +Arguments: + None +Return Value: + None + +******************************************************************* +--*/ +VOID +DeleteRecvQueue ( + void + ); + +/*++ +******************************************************************* + F w R e c e i v e + +Routine Description: + Called by the IPX stack to indicate that the IPX packet was + received by the NIC dirver. Only external destined packets are + indicated by this routine (with the exception of Netbios boradcasts + that indicated both for internal and external handlers) +Arguments: + MacBindingHandle - handle of NIC driver + MaxReceiveContext - NIC driver context + Context - forwarder context associated with + the NIC (interface block pointer) + RemoteAddress - sender's address + MacOptions - + LookaheadBuffer - packet lookahead buffer that contains complete + IPX header + LookaheadBufferSize - its size (at least 30 bytes) + LookaheadBufferOffset - offset of lookahead buffer in the physical + packet +Return Value: + None + +******************************************************************* +--*/ +BOOLEAN +IpxFwdReceive ( + NDIS_HANDLE MacBindingHandle, + NDIS_HANDLE MacReceiveContext, + ULONG Context, + PIPX_LOCAL_TARGET RemoteAddress, + ULONG MacOptions, + PUCHAR LookaheadBuffer, + UINT LookaheadBufferSize, + UINT LookaheadBufferOffset, + UINT PacketSize, + PMDL pMdl + + ); + + +/*++ +******************************************************************* + F w T r a n s f e r D a t a C o m p l e t e + +Routine Description: + Called by the IPX stack when NIC driver completes data transger. +Arguments: + pktDscr - handle of NIC driver + status - result of the transfer + bytesTransferred - number of bytest trasferred +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdTransferDataComplete ( + PNDIS_PACKET pktDscr, + NDIS_STATUS status, + UINT bytesTransferred + ); + + +/*++ +******************************************************************* + F w T r a n s f e r D a t a C o m p l e t e + +Routine Description: + + This routine receives control from the IPX driver after one or + more receive operations have completed and no receive is in progress. + It is called under less severe time constraints than IpxFwdReceive. + It is used to process netbios queue + +Arguments: + None +Return Value: + None +******************************************************************* +--*/ +VOID +IpxFwdReceiveComplete ( + USHORT NicId + ); + +/*++ +******************************************************************* + F w R e c e i v e + +Routine Description: + Called by the IPX stack to indicate that the IPX packet destined + to local client was received by the NIC dirver. +Arguments: + Context - forwarder context associated with + the NIC (interface block pointer) + RemoteAddress - sender's address + LookaheadBuffer - packet lookahead buffer that contains complete + IPX header + LookaheadBufferSize - its size (at least 30 bytes) +Return Value: + STATUS_SUCCESS - the packet will be delivered to local destination + STATUS_UNSUCCESSFUL - the packet will be dropped + +******************************************************************* +--*/ +NTSTATUS +IpxFwdInternalReceive ( + IN ULONG FwdAdapterContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PUCHAR LookAheadBuffer, + IN UINT LookAheadBufferSize + ); + +#endif + diff --git a/private/ntos/tdi/isn/fwd/registry.c b/private/ntos/tdi/isn/fwd/registry.c new file mode 100644 index 000000000..b38a3b4e1 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/registry.c @@ -0,0 +1,273 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: registry.c +// +// Description: routines for reading the registry configuration +// +// Author: Stefan Solomon (stefans) November 9, 1993. +// +// Revision History: +// Updated to read parameters of new forwarder driver (11/95) +// +//*** + +#include "precomp.h" + +NTSTATUS +SetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +/*++ +******************************************************************* + R e a d I p x D e v i c e N a m e + +Routine Description: + Allocates buffer and reads device name exported by the IPX stack + into it +Arguments: + FileName - pointer to buffer to hold the name +Return Value: + STATUS_SUCCESS - tables were created ok + STATUS_INSUFFICIENT_RESOURCES - resource allocation failed + STATUS_OBJECT_NAME_NOT_FOUND - if name value is not found +******************************************************************* +--*/ +NTSTATUS +ReadIpxDeviceName ( + PWSTR *FileName + ) { + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = L"NwLnkIpx\\Linkage"; + + // + // Set up QueryTable to do the following: + // + + // + // 1) Call SetIpxDeviceName for the string in "Export" + // + + QueryTable[0].QueryRoutine = SetIpxDeviceName; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = FileName; + QueryTable[0].DefaultType = 0; + + // + // 2) Stop + // + + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + + +/*++ +******************************************************************* + S e t I p x D e v i c e N a m e + +Routine Description: + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. +Arguments: + ValueName - The name of the value ("Export" -- ignored). + ValueType - The type of the value (REG_SZ -- ignored). + ValueData - The null-terminated data for the value. + ValueLength - The length of ValueData. + Context - NULL. + EntryContext - file name pointer. +Return Value: + STATUS_SUCCESS - name was allocated and copied OK + STATUS_INSUFFICIENT_RESOURCES - name allocation failed +******************************************************************* +--*/ +NTSTATUS +SetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) { + PWSTR *FileName = (PWSTR *)EntryContext; + + ASSERT (ValueType==REG_SZ); + *FileName = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, + ValueLength, FWD_POOL_TAG); + if (*FileName != NULL) { + RtlCopyMemory (*FileName, ValueData, ValueLength); + return STATUS_SUCCESS; + } + else + return STATUS_INSUFFICIENT_RESOURCES; + +} + +/*++ +******************************************************************* + G e t R o u t e r P a r a m e t e r s + +Routine Description: + Reads the parameters from the registry or sets the defaults +Arguments: + RegistryPath - where to read from. +Return Value: + STATUS_SUCCESS +******************************************************************* +--*/ +NTSTATUS +GetForwarderParameters ( + IN PUNICODE_STRING RegistryPath + ) { + NTSTATUS Status; + PWSTR RegistryPathBuffer; + PWSTR Parameters = L"Parameters"; + RTL_QUERY_REGISTRY_TABLE paramTable[11]; // table size = nr of params + 1 + + RegistryPathBuffer = (PWSTR)ExAllocatePool(NonPagedPool, RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + RtlZeroMemory(¶mTable[0], sizeof(paramTable)); + + paramTable[0].QueryRoutine = NULL; + paramTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + paramTable[0].Name = Parameters; + + paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[1].Name = L"MaxRcvPktPoolSize"; + paramTable[1].EntryContext = &MaxRcvPktsPoolSize; + paramTable[1].DefaultType = REG_DWORD; + paramTable[1].DefaultData = &MaxRcvPktsPoolSize; + paramTable[1].DefaultLength = sizeof(ULONG); + + paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[2].Name = L"RcvPktsPerSegment"; + paramTable[2].EntryContext = &RcvPktsPerSegment; + paramTable[2].DefaultType = REG_DWORD; + paramTable[2].DefaultData = &RcvPktsPerSegment; + paramTable[2].DefaultLength = sizeof(ULONG); + + paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[3].Name = L"RouteTableSegmentSize"; + paramTable[3].EntryContext = &RouteSegmentSize; + paramTable[3].DefaultType = REG_DWORD; + paramTable[3].DefaultData = &RouteSegmentSize; + paramTable[3].DefaultLength = sizeof(ULONG); + + paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[4].Name = L"MaxSendPktsQueued"; + paramTable[4].EntryContext = &MaxSendPktsQueued; + paramTable[4].DefaultType = REG_DWORD; + paramTable[4].DefaultData = &MaxSendPktsQueued; + paramTable[4].DefaultLength = sizeof(ULONG); + + paramTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[5].Name = L"ClientHashSize"; + paramTable[5].EntryContext = &ClientHashSize; + paramTable[5].DefaultType = REG_DWORD; + paramTable[5].DefaultData = &ClientHashSize; + paramTable[5].DefaultLength = sizeof(ULONG); + + paramTable[6].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[6].Name = L"InterfaceHashSize"; + paramTable[6].EntryContext = &InterfaceHashSize; + paramTable[6].DefaultType = REG_DWORD; + paramTable[6].DefaultData = &InterfaceHashSize; + paramTable[6].DefaultLength = sizeof(ULONG); + + paramTable[7].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[7].Name = L"MaxNetbiosPacketsQueued"; + paramTable[7].EntryContext = &MaxNetbiosPacketsQueued; + paramTable[7].DefaultType = REG_DWORD; + paramTable[7].DefaultData = &MaxNetbiosPacketsQueued; + paramTable[7].DefaultLength = sizeof(ULONG); + + paramTable[8].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[8].Name = L"SpoofingTimeout"; + paramTable[8].EntryContext = &SpoofingTimeout; + paramTable[8].DefaultType = REG_DWORD; + paramTable[8].DefaultData = &SpoofingTimeout; + paramTable[8].DefaultLength = sizeof(ULONG); + + paramTable[9].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[9].Name = L"DontSuppressNonAgentSapAdvertisements"; + paramTable[9].EntryContext = &DontSuppressNonAgentSapAdvertisements; + paramTable[9].DefaultType = REG_DWORD; + paramTable[9].DefaultData = &DontSuppressNonAgentSapAdvertisements; + paramTable[9].DefaultLength = sizeof(ULONG); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPathBuffer, + paramTable, + NULL, + NULL); + + if(!NT_SUCCESS(Status)) { + + IpxFwdDbgPrint (DBG_REGISTRY, DBG_WARNING, + ("IpxFwd: Missing Parameters key in the registry\n")); + } + + ExFreePool(RegistryPathBuffer); + + if ((RcvPktsPerSegment > MAX_RCV_PKTS_PER_SEGMENT) || + (RcvPktsPerSegment < MIN_RCV_PKTS_PER_SEGMENT)) { + + RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT; + } + + if ((RouteSegmentSize > MAX_ROUTE_SEGMENT_SIZE) || + (RouteSegmentSize < MIN_ROUTE_SEGMENT_SIZE)) { + + RouteSegmentSize = DEF_ROUTE_SEGMENT_SIZE; + } + else + RouteSegmentSize = ROUND_TO_PAGES(RouteSegmentSize); + + if ((InterfaceHashSize > MAX_INTERFACE_HASH_SIZE) || + (InterfaceHashSize < MIN_INTERFACE_HASH_SIZE)) { + + InterfaceHashSize = DEF_INTERFACE_HASH_SIZE; + } + + if ((ClientHashSize > MAX_CLIENT_HASH_SIZE) || + (ClientHashSize < MIN_CLIENT_HASH_SIZE)) { + + ClientHashSize = DEF_CLIENT_HASH_SIZE; + } + // even if the RtlQueryRegistryValues has failed, we return success and will + // use the defaults. + return STATUS_SUCCESS; +} + diff --git a/private/ntos/tdi/isn/fwd/registry.h b/private/ntos/tdi/isn/fwd/registry.h new file mode 100644 index 000000000..6cff0eef2 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/registry.h @@ -0,0 +1,61 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\registry.h + +Abstract: + IPX Forwarder Driver registry interface + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ +#ifndef _IPXFWD_REGISTRY_ +#define _IPXFWD_REGISTRY_ + +/*++ +******************************************************************* + R e a d I p x D e v i c e N a m e + +Routine Description: + Allocates buffer and reads device name exported by the IPX stack + into it +Arguments: + FileName - pointer to variable to hold the name buffer +Return Value: + STATUS_SUCCESS - tables were created ok + STATUS_INSUFFICIENT_RESOURCES - resource allocation failed + STATUS_OBJECT_NAME_NOT_FOUND - if name value is not found +******************************************************************* +--*/ +NTSTATUS +ReadIpxDeviceName ( + PWSTR *FileName + ); + +/*++ +******************************************************************* + G e t R o u t e r P a r a m e t e r s + +Routine Description: + Reads the parameters from the registry or sets the defaults +Arguments: + RegistryPath - where to read from. +Return Value: + STATUS_SUCCESS +******************************************************************* +--*/ +NTSTATUS +GetForwarderParameters ( + IN PUNICODE_STRING RegistryPath + ); + +#endif + diff --git a/private/ntos/tdi/isn/fwd/rwlock.h b/private/ntos/tdi/isn/fwd/rwlock.h new file mode 100644 index 000000000..d1a390651 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/rwlock.h @@ -0,0 +1,179 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\rwlock.h + +Abstract: + Reader-Writer lock macros + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_RWLOCK_ +#define _IPXFWD_RWLOCK_ + +typedef volatile LONG VOLCTR, *PVOLCTR; +typedef PVOLCTR RWCOOKIE, *PRWCOOKIE; + +// Reader- writer lock. +// Allows no-lock access to the tables for readers - they merely +// increment the counter to record their presence upon entrance +// and decrement the same counter as they leave. +// Writers are supposed to be serialized (externally) and their +// actions are limited to ATOMIC insertions of new elements and +// ATOMIC removals/replacements. The removals/replacements MUST +// be followed by a wait for all potential readers who might still +// be using the element that was removed/replaced + +typedef struct _RW_LOCK { + KEVENT Event; // Event to release waiting writer + VOLCTR Ctr1; // Two alternating + VOLCTR Ctr2; // reader counters + volatile PVOLCTR CurCtr; // Counter currently in use +} RW_LOCK, *PRW_LOCK; + + +/*++ +******************************************************************* + I n i t i a l i z e R W L o c k + +Routine Description: + Initializes RW lock +Arguments: + lock - pointer to lock to initialize +Return Value: + None +******************************************************************* +--*/ +//VOID +//InitializeRWLock ( +// PRW_LOCK lock +// ); +#define InitializeRWLock(lock) { \ + KeInitializeEvent (&(lock)->Event, \ + SynchronizationEvent, \ + FALSE); \ + (lock)->Ctr1 = (lock)->Ctr2 = 0; \ + (lock)->CurCtr = &(lock)->Ctr1; \ +} + +/*++ +******************************************************************* + A c q u i r e R e a d e r A c c e s s + +Routine Description: + Acquires reader access to resource protected by the lock +Arguments: + lock - pointer to lock + cookie - pointer to buffer to store lock state for subsequent + release operation +Return Value: + None +******************************************************************* +--*/ +//VOID +//AcquireReaderAccess ( +// IN PRW_LOCK lock, +// OUT RWCOOKIE cookie +// ); +#define AcquireReaderAccess(lock,cookie) \ + do { \ + register LONG local,local1; \ + cookie = (lock)->CurCtr; /*Get current counter pointer*/ \ + local = *(cookie); /*Copy counter value*/ \ + local1 = local + 1; \ + if ((local>=0) /*If counter is valid*/ \ + /*and it hasn't changed while*/ \ + /*we were checking and trying*/ \ + /*to increment it,*/ \ + && (InterlockedCompareExchange ( \ + (PVOID *)(cookie), \ + (PVOID)(local1), \ + (PVOID)local) \ + ==(PVOID)local)) \ + break; /*then we obtained the access*/ \ + } while (1) /*otherwise, we have to do it again (possibly with*/\ + /*the other counter if writer switched it on us)*/ + + +/*++ +******************************************************************* + R e l e a s e R e a d e r A c c e s s + +Routine Description: + Releases reader access to resource protected by the lock +Arguments: + lock - pointer to lock + cookie - lock state for subsequent stored during acquire operation +Return Value: + None +******************************************************************* +--*/ +//VOID +//ReleaseReaderAccess ( +// IN PRW_LOCK lock, +// IN RWCOOKIE cookie +// ); +#define ReleaseReaderAccess(lock,cookie) { \ + /*If counter drops below 0, we have to signal the writer*/ \ + if (InterlockedDecrement((PLONG)cookie)<0) { \ + LONG res; \ + ASSERT (*(cookie)==-1); \ + res = KeSetEvent (&(lock)->Event, 0, FALSE); \ + ASSERT (res==0); \ + } \ +} + +/*++ +******************************************************************* + W a i t F o r A l l R e a d e r s + +Routine Description: + Waits for all readers that were accessing the resource prior + to the call to exit (New readers are not included) +Arguments: + lock - pointer to lock +Return Value: + None +******************************************************************* +--*/ +//VOID +//WaitForAllReaders ( +// PRW_LOCK lock +// ); +#define WaitForAllReaders(lock) { \ + RWCOOKIE prevCtr = (lock)->CurCtr; \ + /*Switch the counter first*/ \ + if (prevCtr==&(lock)->Ctr1) { \ + (lock)->Ctr2 = 0; \ + (lock)->CurCtr = &(lock)->Ctr2; \ + } \ + else { \ + ASSERT (prevCtr==&(lock)->Ctr2); \ + (lock)->Ctr1 = 0; \ + (lock)->CurCtr = &(lock)->Ctr1; \ + } \ + /* If not all readers are gone, we'll have to wait for them*/ \ + if (InterlockedDecrement((PLONG)prevCtr)>=0) { \ + NTSTATUS status \ + = KeWaitForSingleObject ( \ + &(lock)->Event, \ + Executive, \ + KeGetPreviousMode(),\ + FALSE, \ + 0); \ + ASSERT (NT_SUCCESS(status)); \ + ASSERT (*prevCtr==-1); \ + } \ +} + +#endif diff --git a/private/ntos/tdi/isn/fwd/send.c b/private/ntos/tdi/isn/fwd/send.c new file mode 100644 index 000000000..35c3ea2d6 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/send.c @@ -0,0 +1,1253 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\send.c + +Abstract: + Send routines + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#include "precomp.h" + + +ULONG SpoofingTimeout=DEF_SPOOFING_TIMEOUT; +LIST_ENTRY SpoofingQueue; +KSPIN_LOCK SpoofingQueueLock; +WORK_QUEUE_ITEM SpoofingWorker; +BOOLEAN SpoofingWorkerActive = FALSE; +ULONG DontSuppressNonAgentSapAdvertisements = 0; + +#define IsLocalSapNonAgentAdvertisement(hdr,data,ln,ifCB) ( \ + (DontSuppressNonAgentSapAdvertisements==0) \ + && (GETUSHORT(hdr+IPXH_DESTSOCK)==IPX_SAP_SOCKET) \ + && (GETUSHORT(hdr+IPXH_SRCSOCK)!=IPX_SAP_SOCKET) \ + && (ln>=IPXH_HDRSIZE+2) \ + && (GETUSHORT(data)==2) \ + && ((IPX_NODE_CMP(hdr+IPXH_DESTNODE,BROADCAST_NODE)==0) \ + || (IPX_NODE_CMP(hdr+IPXH_DESTNODE,ifCB->ICB_RemoteNode)==0)) \ +) + +/*++ +******************************************************************* + D o S e n d + +Routine Description: + Prepares and sends packet. Interface lock must be help while + callin this routine +Arguments: + dstIf - over which interface to send + pktTag - packet to send +Return Value: + result returned by IPX + +******************************************************************* +--*/ +NDIS_STATUS +DoSend ( + PINTERFACE_CB dstIf, + PPACKET_TAG pktTag, + KIRQL oldIRQL + ) { + NDIS_STATUS status; + PNDIS_PACKET pktDscr; + PNDIS_BUFFER bufDscr, aDscr; + UINT dataLen; + ULONG dstNet = GETULONG (pktTag->PT_Data+IPXH_DESTNET); + + if (dstIf!=InternalInterface) { + ADAPTER_CONTEXT_TO_LOCAL_TARGET (dstIf->ICB_AdapterContext, + &pktTag->PT_Target); + } + else { + CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + VIRTUAL_NET_ADAPTER_CONTEXT, + &pktTag->PT_Target); + } + +#if DBG + // Keep track of packets being processed by IPX stack + InsertTailList (&dstIf->ICB_InSendQueue, &pktTag->PT_QueueLink); +#endif + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + + if (pktTag->PT_Flags&PT_SOURCE_IF) + ReleaseInterfaceReference (pktTag->PT_SourceIf); + pktTag->SEND_RESERVED[0] = pktTag->SEND_RESERVED[1] = 0; + pktDscr = CONTAINING_RECORD(pktTag, NDIS_PACKET, ProtocolReserved); + NdisQueryPacket(pktDscr, NULL, NULL, &bufDscr, NULL); +#if DBG + { // Verify packet integrity + PUCHAR dataPtr; + UINT bufLen; + ASSERT (NDIS_BUFFER_LINKAGE (bufDscr)==NULL); + NdisQueryBuffer (bufDscr, &dataPtr, &bufLen); + ASSERT (dataPtr==pktTag->PT_Data); + ASSERT (bufLen==pktTag->PT_Segment->PS_SegmentList->SL_BlockSize); + } +#endif + // Prepare packet for IPX stack (mac header buffer goes in + // front and packet length adjusted to reflect the size of the data + dataLen = GETUSHORT(pktTag->PT_Data+IPXH_LENGTH); + NdisAdjustBufferLength(bufDscr, dataLen); + NdisChainBufferAtFront(pktDscr, pktTag->PT_MacHdrBufDscr); + + + if (EnterForwarder ()) {// To make sure that we won't unload + // until IPX driver has a chance to call us back + status = IPXSendProc (&pktTag->PT_Target, pktDscr, dataLen, 0); + + if (status!=NDIS_STATUS_PENDING) { + LeaveForwarder (); // No callback + + // Restore original packet structure + NdisUnchainBufferAtFront (pktDscr, &aDscr); +#if DBG + // Make sure IPX stack did not mess our packet + ASSERT (aDscr==pktTag->PT_MacHdrBufDscr); + NdisQueryPacket(pktDscr, NULL, NULL, &aDscr, NULL); + ASSERT (aDscr==bufDscr); + ASSERT (NDIS_BUFFER_LINKAGE (aDscr)==NULL); +#endif + // Restore original packet size + NdisAdjustBufferLength(bufDscr, + pktTag->PT_Segment->PS_SegmentList->SL_BlockSize); +#if DBG + // Remove packet from temp queue + KeAcquireSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, &oldIRQL); + RemoveEntryList (&pktTag->PT_QueueLink); + KeReleaseSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, oldIRQL); +#endif + } + } + else { + // We are going down, restore the packet + NdisUnchainBufferAtFront (pktDscr, &aDscr); + NdisAdjustBufferLength(bufDscr, + pktTag->PT_Segment->PS_SegmentList->SL_BlockSize); + NdisRecalculatePacketCounts (pktDscr); + status = STATUS_UNSUCCESSFUL; +#if DBG + KeAcquireSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, &oldIRQL); + RemoveEntryList (&pktTag->PT_QueueLink); + KeReleaseSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, oldIRQL); +#endif + } + return status; +} + + +/*++ +******************************************************************* + P r o c e s s S e n t P a c k e t + +Routine Description: + Process completed sent packets +Arguments: + dstIf - interface over which packet was sent + pktTag - completed packet + status - result of send operation +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessSentPacket ( + PINTERFACE_CB dstIf, + PPACKET_TAG pktTag, + NDIS_STATUS status + ) { + KIRQL oldIRQL; + + // Packet processing is completed -> can take more packets + InterlockedIncrement (&dstIf->ICB_PendingQuota); + + if (*(pktTag->PT_Data+IPXH_PKTTYPE) == IPX_NETBIOS_TYPE) { + // Continue processing netbios packets + if (status==NDIS_STATUS_SUCCESS) { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: NB Packet %08lx sent.", pktTag)); + InterlockedIncrement (&dstIf->ICB_Stats.OutDelivers); + InterlockedIncrement (&dstIf->ICB_Stats.NetbiosSent); + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_ERROR, + ("IpxFwd: NB Packet %08lx send failed with error: %08lx.\n", + pktTag, status)); + } + // Queue nb packet for further processing (broadcast on all interfaces) + QueueNetbiosPacket (pktTag); + } + else { + // Destroy completed packet + if (status==NDIS_STATUS_SUCCESS) { + InterlockedIncrement (&dstIf->ICB_Stats.OutDelivers); + IpxFwdDbgPrint (DBG_SEND, DBG_INFORMATION, + ("IpxFwd: Packet %08lx sent.", pktTag)); + } + else { + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + IpxFwdDbgPrint (DBG_SEND, DBG_ERROR, + ("IpxFwd: Packet %08lx send failed with error: %08lx.\n", + pktTag, status)); + } + ReleaseInterfaceReference (dstIf); + if (MeasuringPerformance + && (pktTag->PT_PerfCounter!=0)) { + LARGE_INTEGER PerfCounter = KeQueryPerformanceCounter (NULL); + PerfCounter.QuadPart -= pktTag->PT_PerfCounter; + KeAcquireSpinLock (&PerfCounterLock, &oldIRQL); + ASSERT (PerfCounter.QuadPartICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + // Make sure we have not exceded the quota of pending packets on the interface + if (InterlockedDecrement (&dstIf->ICB_PendingQuota)>=0) { + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + // Decide what to do with the packet based on the interface state + switch (dstIf->ICB_Stats.OperationalState) { + case FWD_OPER_STATE_UP: + if (*(pktTag->PT_Data + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + NOTHING; + else { + PUTULONG (dstIf->ICB_Network, pktTag->PT_Data+IPXH_DESTNET); + } + status = DoSend (dstIf, pktTag, oldIRQL); + IpxFwdDbgPrint (DBG_SEND, DBG_INFORMATION, + ("IpxFwd: Sent external packet %08lx on if %ld.\n", + pktTag, dstIf->ICB_Index)); + break; + case FWD_OPER_STATE_SLEEPING: + if ((*(pktTag->PT_Data+IPXH_PKTTYPE)!=0) + || (GETUSHORT(pktTag->PT_Data+IPXH_LENGTH)!=IPXH_HDRSIZE+2) + || (*(pktTag->PT_Data+IPXH_HDRSIZE+1)!='?')) { + // Queue this packet on the interface until it is connected + // by Router Manager (DIM) if this is not a NCP keepalive + // (watchdog) + InsertTailList (&dstIf->ICB_ExternalQueue, &pktTag->PT_QueueLink); + if (!IS_IF_CONNECTING (dstIf)) { + // Ask for connection if interface is not in the connection + // queue yet + QueueConnectionRequest (dstIf, + CONTAINING_RECORD (pktTag, + NDIS_PACKET, + ProtocolReserved), + pktTag->PT_Data, + oldIRQL); + IpxFwdDbgPrint (DBG_DIALREQS, DBG_WARNING, + ("IpxFwd: Queued dd request on if %ld (ifCB:%08lx)" + " for packet to %02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%02x%02x" + " from %02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%02x%02x\n", + dstIf->ICB_Index, dstIf, + *(pktTag->PT_Data+6),*(pktTag->PT_Data+7), + *(pktTag->PT_Data+8),*(pktTag->PT_Data+9), + *(pktTag->PT_Data+10),*(pktTag->PT_Data+11), + *(pktTag->PT_Data+12),*(pktTag->PT_Data+13), + *(pktTag->PT_Data+14),*(pktTag->PT_Data+15), + *(pktTag->PT_Data+16),*(pktTag->PT_Data+17), + *(pktTag->PT_Data+18),*(pktTag->PT_Data+19), + *(pktTag->PT_Data+20),*(pktTag->PT_Data+21), + *(pktTag->PT_Data+22),*(pktTag->PT_Data+23), + *(pktTag->PT_Data+24),*(pktTag->PT_Data+25), + *(pktTag->PT_Data+26),*(pktTag->PT_Data+27), + *(pktTag->PT_Data+28),*(pktTag->PT_Data+29))); + } + else + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_SEND, DBG_INFORMATION, + ("IpxFwd: Queued external packet %08lx on if %ld.\n", + pktTag, dstIf->ICB_Index)); + if (*(pktTag->PT_Data + IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + NOTHING; + else if (!(pktTag->PT_Flags&PT_NB_DESTROY)) { + // If this nb packet is not to be destroyed after this + // send, we have to make a copy of it to send on + // other interfaces while the original is waiting + // for connection + PPACKET_TAG newPktTag; + DuplicatePacket (pktTag, newPktTag); + if (newPktTag!=NULL) { + UINT bytesCopied; + PNDIS_PACKET packet = CONTAINING_RECORD (pktTag, + NDIS_PACKET, + ProtocolReserved); + PNDIS_PACKET newPacket = CONTAINING_RECORD (newPktTag, + NDIS_PACKET, + ProtocolReserved); + NdisCopyFromPacketToPacket (newPacket, 0, + GETUSHORT(pktTag->PT_Data+IPXH_LENGTH), + packet, 0, &bytesCopied); + + ASSERT (bytesCopied==GETUSHORT(pktTag->PT_Data+IPXH_LENGTH)); + IpxFwdDbgPrint (DBG_NETBIOS, + DBG_INFORMATION, + ("IpxFwd: Duplicated queued nb packet" + " %08lx -> %08lx on if %ld.\n", + pktTag, newPktTag, dstIf->ICB_Index)); + AcquireInterfaceReference (dstIf); + newPktTag->PT_InterfaceReference = dstIf; + newPktTag->PT_PerfCounter = pktTag->PT_PerfCounter; + QueueNetbiosPacket (newPktTag); + // The original copy will have to be + // destroyed after it is sent on the + // connected interface + pktTag->PT_Flags |= PT_NB_DESTROY; + } + } + status = NDIS_STATUS_PENDING; + break; + } + else { // Process keepalives + LONGLONG curTime; + KeQuerySystemTime ((PLARGE_INTEGER)&curTime); + if (((curTime-dstIf->ICB_DisconnectTime)/10000000) < SpoofingTimeout) { + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_SPOOFING, DBG_INFORMATION, + ("IpxFwd: Queueing reply to keepalive from server" + " on if %ld (ifCB %lx)" + " at %02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%02x%02x.\n", + dstIf->ICB_Index, dstIf, + *(pktTag->PT_Data+IPXH_SRCNET),*(pktTag->PT_Data+IPXH_SRCNET+1), + *(pktTag->PT_Data+IPXH_SRCNET+2),*(pktTag->PT_Data+IPXH_SRCNET+3), + *(pktTag->PT_Data+IPXH_SRCNODE),*(pktTag->PT_Data+IPXH_SRCNODE+1), + *(pktTag->PT_Data+IPXH_SRCNODE+2),*(pktTag->PT_Data+IPXH_SRCNODE+3), + *(pktTag->PT_Data+IPXH_SRCNODE+4),*(pktTag->PT_Data+IPXH_SRCNODE+5), + *(pktTag->PT_Data+IPXH_SRCSOCK),*(pktTag->PT_Data+IPXH_SRCNODE+1))); + // Spoof the packet if timeout has not been exceeded + KeAcquireSpinLock (&SpoofingQueueLock, &oldIRQL); + InsertTailList (&SpoofingQueue, &pktTag->PT_QueueLink); + if (!SpoofingWorkerActive + && EnterForwarder()) { + SpoofingWorkerActive = TRUE; + ExQueueWorkItem (&SpoofingWorker, DelayedWorkQueue); + } + KeReleaseSpinLock (&SpoofingQueueLock, oldIRQL); + // We will actually send this packet though + // in other direction, so mark it as pending + // to prevent ProcessSentPacket to be called + status = NDIS_STATUS_PENDING; + break; + } + // else don't spoof (fall through and fail the packet) + } + case FWD_OPER_STATE_DOWN: + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + status = NDIS_STATUS_ADAPTER_NOT_READY; + IpxFwdDbgPrint (DBG_SEND, DBG_WARNING, + ("IpxFwd: Failed external packet %08lx on if %ld(down?).\n", + pktTag, dstIf->ICB_Index)); + break; + default: + ASSERTMSG ("Invalid operational state ", FALSE); + } + } + else { + IpxFwdDbgPrint (DBG_SEND, DBG_WARNING, + ("IpxFwd: Could not send packet %08lx on if %ld (quota exceeded).\n", + pktTag, dstIf->ICB_Index)); + status = NDIS_STATUS_RESOURCES; + } + + if (status!=NDIS_STATUS_PENDING) + ProcessSentPacket (dstIf, pktTag, status); +} + +/*++ +******************************************************************* + F w S e n d C o m p l e t e + +Routine Description: + Called by IPX stack when send completes asynchronously +Arguments: + pktDscr - descriptor of the completed packet + status - result of send operation +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdSendComplete ( + PNDIS_PACKET pktDscr, + NDIS_STATUS status + ) { + PPACKET_TAG pktTag; + PNDIS_BUFFER bufDscr; + + pktTag = (PPACKET_TAG)pktDscr->ProtocolReserved; + + NdisUnchainBufferAtFront (pktDscr, &bufDscr); + ASSERT (bufDscr==pktTag->PT_MacHdrBufDscr); + + NdisQueryPacket(pktDscr, + NULL, + NULL, + &bufDscr, + NULL); + NdisAdjustBufferLength(bufDscr, + pktTag->PT_Segment->PS_SegmentList->SL_BlockSize); + NdisRecalculatePacketCounts (pktDscr); +#if DBG + { + KIRQL oldIRQL; + KeAcquireSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, &oldIRQL); + RemoveEntryList (&pktTag->PT_QueueLink); + KeReleaseSpinLock (&pktTag->PT_InterfaceReference->ICB_Lock, oldIRQL); + } +#endif + ProcessSentPacket (pktTag->PT_InterfaceReference, pktTag, status); + LeaveForwarder (); // Entered before calling IpxSendPacket +} + + +/*++ +******************************************************************* + + F w I n t e r n a l S e n d + +Routine Description: + Filter and routes packets sent by IPX stack +Arguments: + LocalTarget - the NicId and next hop router MAC address + Context - preferred interface on which to send + Packet - packet to be sent + ipxHdr - pointer to ipx header inside the packet + PacketLength - length of the packet + fIterate - a flag to indicate if this is a packet for the + iteration of which the Fwd takes responsibility + - typically type 20 NetBIOS frames +Return Value: + + STATUS_SUCCESS - if the preferred NIC was OK and packet passed filtering + STATUS_NETWORK_UNREACHABLE - if the preferred was not OK or packet failed filtering + STATUS_PENDING - packet was queued until connection is established +******************************************************************* +--*/ +NTSTATUS +IpxFwdInternalSend ( + IN OUT PIPX_LOCAL_TARGET LocalTarget, + IN ULONG Context, + IN PNDIS_PACKET pktDscr, + IN PUCHAR ipxHdr, + IN PUCHAR data, + IN ULONG PacketLength, + IN BOOLEAN fIterate + ) { + PINTERFACE_CB dstIf = NULL, // Initialized to indicate + // first path through the iteration + // as well as the fact the we do not + // know it initially + stDstIf; // Static destination for + // NetBIOS names + PFWD_ROUTE fwRoute = NULL; + ULONG dstNet; + USHORT dstSock; + NTSTATUS status; + + if (!EnterForwarder()) + return STATUS_NETWORK_UNREACHABLE; + + if (IS_IF_ENABLED(InternalInterface) + && ((*(ipxHdr+IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + || InternalInterface->ICB_NetbiosAccept)) { + + do { // Big loop used to iterate over interfaces + status = STATUS_SUCCESS; // Assume success + if (!fIterate) { + dstNet = GETULONG (ipxHdr+IPXH_DESTNET); + + if (Context!=INVALID_CONTEXT_VALUE) { + if (Context!=VIRTUAL_NET_FORWARDER_CONTEXT) { + // IPX driver supplied interface context, just verify that it + // exists and can be used to reach the destination network + dstIf = InterfaceContextToReference ((PVOID)Context, + LocalTarget->NicId); + } + else { + dstIf = InternalInterface; + AcquireInterfaceReference (dstIf); + } + if (dstIf!=NULL) { + // It does exist + // First process direct connections + if ((dstNet==0) + || (dstNet==dstIf->ICB_Network)) { + NOTHING; + } + else { // Network is not connected directly + PINTERFACE_CB dstIf2; + // Verify the route + dstIf2 = FindDestination (dstNet, + ipxHdr+IPXH_DESTNODE, + &fwRoute); + if (dstIf==dstIf2) { + // Route OK, release the extra interface reference + ReleaseInterfaceReference (dstIf2); + } + else { + // Route not OK, release interface/route references + InterlockedIncrement (&InternalInterface->ICB_Stats.InNoRoutes); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Failed direct internal send on" + " if %ld to %08lx:%02x%02x%02x%02x%02x%02x" + " (no route).\n", + dstIf->ICB_Index, dstNet, + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + if (dstIf2!=NULL) { + ReleaseInterfaceReference (dstIf2); + } + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + } + else { + InterlockedIncrement (&InternalInterface->ICB_Stats.InDiscards); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Invalid interface context (%08lx)" + " from IPX driver on internal send to" + " %08lx:%02x%02x%02x%02x%02x%02x.\n", + Context, dstNet, + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + status = STATUS_NO_SUCH_DEVICE; + break; + } + } + else {// No interface context supplied by IPX driver, have to find the route + dstIf = FindDestination (dstNet, ipxHdr+IPXH_DESTNODE, + &fwRoute); + if (dstIf!=NULL) + NOTHING; + else { + InterlockedIncrement (&InternalInterface->ICB_Stats.InNoRoutes); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Failed internal send because no route to" + " %08lx:%02x%02x%02x%02x%02x%02x exists.\n", + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + InterlockedIncrement (&InternalInterface->ICB_Stats.InDelivers); + } + else { // Iterative sends + dstNet = 0; // Don't care, it must be a local send + if (*(ipxHdr+IPXH_PKTTYPE) == IPX_NETBIOS_TYPE) { + if (dstIf==NULL) { // First time through internal loop + dstSock = GETUSHORT (ipxHdr+IPXH_DESTSOCK); + + // See if we can get a static route for this packet + if (dstSock==IPX_NETBIOS_SOCKET) + stDstIf = FindNBDestination (data+(NB_NAME-IPXH_HDRSIZE)); + else if (dstSock==IPX_SMB_NAME_SOCKET) + stDstIf = FindNBDestination (data+(SMB_NAME-IPXH_HDRSIZE)); + else + stDstIf = NULL; + } + + if ((Context==INVALID_CONTEXT_VALUE) && (dstIf==NULL)) { + // First time through the loop, increment counters + InterlockedIncrement (&InternalInterface->ICB_Stats.InDelivers); + InterlockedIncrement (&InternalInterface->ICB_Stats.NetbiosSent); + + if (stDstIf!=NULL) { + dstIf = stDstIf; + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Allowed internal NB broadcast (1st iteration) on if %d (%lx)" + " to static name %.16s.\n", + dstIf->ICB_Index, dstIf, + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + } + else { // No static route + dstIf = GetNextInterfaceReference (NULL); + if (dstIf!=NULL) + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Allowed internal nb broadcast (1st iteration) on if %d (%lx)," + " to name %.16s.\n", + dstIf->ICB_Index, dstIf, + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Nb broadcast no destinations" + " to name %.16s.\n", + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + } + else { + // N-th iteration + if (stDstIf==NULL) { + if (dstIf==NULL) + dstIf = InterfaceContextToReference ((PVOID)Context, + LocalTarget->NicId); + dstIf = GetNextInterfaceReference (dstIf); + if (dstIf!=NULL) { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Allowed internal NB broadcast (1+ iteration)" + " on if %d (%lx, ctx: %08lx, nic: %d)" + " to name %.16s.\n", + dstIf->ICB_Index, dstIf, + Context, LocalTarget->NicId, + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: NB broadcast no more iterations" + " for ctx: %08lx, nic: %d" + " to name %.16s.\n", + Context, LocalTarget->NicId, + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Static NB broadcast (1+ iteration)" + " on if %d (%lx, ctx: %08lx, nic: %d)" + " to name %.16s.\n", + stDstIf->ICB_Index, stDstIf, + Context, LocalTarget->NicId, + (dstSock==IPX_NETBIOS_SOCKET) + ? data+(NB_NAME-IPXH_HDRSIZE) + : ((dstSock==IPX_SMB_NAME_SOCKET) + ? data+(SMB_NAME-IPXH_HDRSIZE) + : "Not a name frame") + )); + ReleaseInterfaceReference (stDstIf); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + } + else { + if ((dstIf==NULL) + && (Context!=INVALID_CONTEXT_VALUE)) + dstIf = InterfaceContextToReference ((PVOID)Context, + LocalTarget->NicId); + dstIf = GetNextInterfaceReference (dstIf); + if (dstIf!=NULL) { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: Allowed internal iterative send" + " on if %d (%lx, ctx: %08lx, nic: %d)" + " to %02x%02x%02x%02x%02x%02x.\n", + dstIf->ICB_Index, dstIf, + Context, LocalTarget->NicId, + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION, + ("IpxFwd: No destinations to internal iterative send" + " for ctx: %08lx, nic: %d" + " to %02x%02x%02x%02x%02x%02x.\n", + Context, LocalTarget->NicId, + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + } + + } // End iterative send processing + + // We were able to find a destination interface + if (IS_IF_ENABLED (dstIf) + && ((*(ipxHdr+IPXH_PKTTYPE) != IPX_NETBIOS_TYPE) + || (dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL) + || ((dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_IF_UP) + && (dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP)) + || ((stDstIf!=NULL) + && (dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_STATIC)))) { + KIRQL oldIRQL; + FILTER_ACTION action; + + // In/Out filter check and statistics update + + action = FltFilter (ipxHdr, IPXH_HDRSIZE, + InternalInterface->ICB_FilterInContext, + dstIf->ICB_FilterOutContext); + if (action==FILTER_PERMIT) { + NOTHING; + } + else { + InterlockedIncrement (&dstIf->ICB_Stats.OutFiltered); + status = STATUS_NETWORK_UNREACHABLE; + break; + } + + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + // All set, try to send now + switch (dstIf->ICB_Stats.OperationalState) { + case FWD_OPER_STATE_UP: + // Interface is up, let it go right away + // Set NIC ID + if (dstIf!=InternalInterface) { + ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + dstIf->ICB_AdapterContext, + LocalTarget); + } + else { + CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + VIRTUAL_NET_ADAPTER_CONTEXT, + LocalTarget); + } + // Set destination node + if (IsLocalSapNonAgentAdvertisement (ipxHdr,data,PacketLength,dstIf)) { + // Loop back sap ads from non-sap socket + IPX_NODE_CPY (&LocalTarget->MacAddress, + dstIf->ICB_LocalNode); + } + else if ((dstNet==0) || (dstNet==dstIf->ICB_Network)) { + // Direct connection: send to destination specified + // in the header + IPX_NODE_CPY (LocalTarget->MacAddress, + ipxHdr+IPXH_DESTNODE); + } + else { // Indirect connection: send to next hop router + if (dstIf->ICB_InterfaceType==FWD_IF_PERMANENT) { + ASSERT (fwRoute!=NULL); + IPX_NODE_CPY (LocalTarget->MacAddress, + fwRoute->FR_NextHopAddress); + } + else { + // Only one peer on the other side + IPX_NODE_CPY (LocalTarget->MacAddress, + dstIf->ICB_RemoteNode); + } + } + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + // Update statistics + InterlockedIncrement ( + &dstIf->ICB_Stats.OutDelivers); + if (*(ipxHdr+IPXH_PKTTYPE)==IPX_NETBIOS_TYPE) + InterlockedIncrement ( + &dstIf->ICB_Stats.NetbiosSent); + + IpxFwdDbgPrint (DBG_INT_SEND, DBG_INFORMATION, + ("IpxFwd: Allowed internal send:" + " %ld-%08lx:%02x%02x%02x%02x%02x%02x.\n", + dstIf->ICB_Index, dstNet, + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5])); + // status = STATUS_SUCCESS; // Let it go + break; + case FWD_OPER_STATE_SLEEPING: + // Interface is disconnected, queue the packet and try to connecte + if ((*(ipxHdr+IPXH_PKTTYPE)!=0) + || (*(ipxHdr+IPXH_LENGTH)!=IPXH_HDRSIZE+2) + || (*(data+1)!='?')) { + // Not a keep-alive packet, + if (((*(ipxHdr+IPXH_PKTTYPE)!=IPX_NETBIOS_TYPE)) + || (dstIf->ICB_NetbiosDeliver!=FWD_NB_DELIVER_IF_UP)) { + // Not a netbios broadcast or we are allowed to connect + // the interface to deliver netbios broadcasts + if (InterlockedDecrement (&dstIf->ICB_PendingQuota)>=0) { + PINTERNAL_PACKET_TAG pktTag; + // Create a queue element to enqueue the packet + pktTag = (PINTERNAL_PACKET_TAG)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (INTERNAL_PACKET_TAG), + FWD_POOL_TAG); + if (pktTag!=NULL) { + pktTag->IPT_Packet = pktDscr; + pktTag->IPT_Length = PacketLength; + pktTag->IPT_DataPtr = ipxHdr; + // Save next hop address if after connection is + // established we determine that destination net + // is not connected directly + if (fwRoute!=NULL) + IPX_NODE_CPY (pktTag->IPT_Target.MacAddress, + fwRoute->FR_NextHopAddress); + AcquireInterfaceReference (dstIf); // To make sure interface + // block won't go away until we are done with + // the packet + pktTag->IPT_InterfaceReference = dstIf; + InsertTailList (&dstIf->ICB_InternalQueue, + &pktTag->IPT_QueueLink); + if (!IS_IF_CONNECTING (dstIf)) { + QueueConnectionRequest (dstIf, pktDscr, ipxHdr, oldIRQL); + IpxFwdDbgPrint (DBG_DIALREQS, DBG_WARNING, + ("IpxFwd: Queued dd request on if %ld (ifCB:%08lx)" + " for internal packet" + " to %02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%02x%02x" + " from socket:%02x%02x\n", + dstIf->ICB_Index, dstIf, + *(ipxHdr+6),*(ipxHdr+7), + *(ipxHdr+8),*(ipxHdr+9), + *(ipxHdr+10),*(ipxHdr+11), + *(ipxHdr+12),*(ipxHdr+13), + *(ipxHdr+14),*(ipxHdr+15), + *(ipxHdr+16),*(ipxHdr+17), + *(ipxHdr+28),*(ipxHdr+29))); + } + else + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_INFORMATION, + ("IpxFwd: Queueing internal send packet %08lx on if %ld.\n", + pktTag, dstIf->ICB_Index)); + status = STATUS_PENDING; + break; + } + else { + IpxFwdDbgPrint (DBG_INT_SEND, DBG_ERROR, + ("IpxFwd: Could not allocate" + " internal packet tag.\n")); + } + } + InterlockedIncrement (&dstIf->ICB_PendingQuota); + } + else { + IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING, + ("IpxFwd: Droped internal NB packet" + " because FWD_NB_DELIVER_IF_UP.\n")); + } + } + else { // Process keep-alives + LONGLONG curTime; + KeQuerySystemTime ((PLARGE_INTEGER)&curTime); + if (((curTime-dstIf->ICB_DisconnectTime)/10000000) < SpoofingTimeout) { + PPACKET_TAG pktTag; + // Spoofing timeout has not been exceeded, + // Create a reply packet + AllocatePacket (WanPacketListId, + WanPacketListId, + pktTag); + if (pktTag!=NULL) { + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + PUTUSHORT (0xFFFF, pktTag->PT_Data+IPXH_CHECKSUM); + PUTUSHORT (IPXH_HDRSIZE+2, pktTag->PT_Data+IPXH_LENGTH); + *(pktTag->PT_Data+IPXH_XPORTCTL) = 0; + *(pktTag->PT_Data+IPXH_PKTTYPE) = 0; + memcpy (pktTag->PT_Data+IPXH_DESTADDR, + ipxHdr+IPXH_SRCADDR, + 12); + memcpy (pktTag->PT_Data+IPXH_SRCADDR, + ipxHdr+IPXH_DESTADDR, + 12); + *(pktTag->PT_Data+IPXH_HDRSIZE) = *data; + *(pktTag->PT_Data+IPXH_HDRSIZE+1) = 'Y'; + // Destination for this packet will have to + // be the first active LAN adapter in the system + // SHOULD BE REMOVED WHEN LOOPBACK SUPPORT US ADDED BY IPX + + pktTag->PT_InterfaceReference = NULL; + IpxFwdDbgPrint (DBG_SPOOFING, DBG_INFORMATION, + ("IpxFwd: Queueing reply to keepalive from internal server" + " at %02x%02x.\n",*(ipxHdr+IPXH_DESTSOCK),*(ipxHdr+IPXH_DESTSOCK+1))); + // Enqueue to spoofing queue to be sent back + // to the server + KeAcquireSpinLock (&SpoofingQueueLock, &oldIRQL); + InsertTailList (&SpoofingQueue, &pktTag->PT_QueueLink); + // Start worker if not running already + if (!SpoofingWorkerActive + && EnterForwarder()) { + SpoofingWorkerActive = TRUE; + ExQueueWorkItem (&SpoofingWorker, DelayedWorkQueue); + } + KeReleaseSpinLock (&SpoofingQueueLock, oldIRQL); + status = STATUS_DROP_SILENTLY; + break; + } + else { + IpxFwdDbgPrint (DBG_SPOOFING, DBG_ERROR, + ("IpxFwd: Could not allocate" + " packet tag for spoofing.\n")); + } + } + else { + IpxFwdDbgPrint (DBG_SPOOFING, DBG_WARNING, + ("IpxFwd: Internal spoofing" + " timeout exceded.\n")); + } + } + case FWD_OPER_STATE_DOWN: + // Interface down or send failed + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + if (*(ipxHdr+IPXH_PKTTYPE)!=IPX_NETBIOS_TYPE) + InterlockedIncrement ( + &dstIf->ICB_Stats.OutDiscards); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Internal send not allowed" + " on if %ld (down?).\n", dstIf->ICB_Index)); + status = STATUS_NETWORK_UNREACHABLE; + break; + default: + ASSERTMSG ("Invalid operational state ", FALSE); + } + } + else {// Interface is disabled + if (*(ipxHdr+IPXH_PKTTYPE)!=IPX_NETBIOS_TYPE) + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Internal send not allowed" + " on because dst if (or Netbios deliver on it) %ld (ifCB: %08lx) is disabled.\n", + dstIf->ICB_Index, dstIf)); + status = STATUS_NETWORK_UNREACHABLE; + } + + + } + while (fIterate && (status!=STATUS_SUCCESS) && (status!=STATUS_PENDING)); + + if (dstIf!=NULL) + ReleaseInterfaceReference (dstIf); + if (fwRoute!=NULL) + ReleaseRouteReference (fwRoute); + } + else { // Internal interface is disabled + IpxFwdDbgPrint (DBG_INT_SEND, DBG_WARNING, + ("IpxFwd: Internal send not allowed" + " because internal if (or Netbios accept on it) is disabled.\n")); + InterlockedIncrement ( + &InternalInterface->ICB_Stats.InDiscards); + status = STATUS_NETWORK_UNREACHABLE; + } + + LeaveForwarder (); + return status; +} + + +/*++ +******************************************************************* + + P r o c e s s I n t e r n a l Q u e u e + +Routine Description: + Processes packets in the interface internal queue. + Called when connection request completes +Arguments: + dstIf - interface to process +Return Value: + None +******************************************************************* +--*/ +VOID +ProcessInternalQueue ( + PINTERFACE_CB dstIf + ) { + KIRQL oldIRQL; + LIST_ENTRY tempQueue; + + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + InsertHeadList (&dstIf->ICB_InternalQueue, &tempQueue); + RemoveEntryList (&dstIf->ICB_InternalQueue); + InitializeListHead (&dstIf->ICB_InternalQueue); + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + + while (!IsListEmpty (&tempQueue)) { + PINTERNAL_PACKET_TAG pktTag; + PLIST_ENTRY cur; + NTSTATUS status; + + cur = RemoveHeadList (&tempQueue); + pktTag = CONTAINING_RECORD (cur, INTERNAL_PACKET_TAG, IPT_QueueLink); + InterlockedIncrement (&dstIf->ICB_PendingQuota); + + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + if (IS_IF_ENABLED(dstIf) + && (dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP)) { + IPX_NODE_CPY (pktTag->IPT_Target.MacAddress, + dstIf->ICB_RemoteNode); + ADAPTER_CONTEXT_TO_LOCAL_TARGET ( + dstIf->ICB_AdapterContext, + &pktTag->IPT_Target); + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + InterlockedIncrement (&dstIf->ICB_Stats.OutDelivers); + if (*(pktTag->IPT_DataPtr + IPXH_PKTTYPE) == IPX_NETBIOS_TYPE) { + InterlockedIncrement (&dstIf->ICB_Stats.NetbiosSent); + } + status = STATUS_SUCCESS; + } + else { + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + status = STATUS_NETWORK_UNREACHABLE; + } + + IPXInternalSendCompletProc (&pktTag->IPT_Target, + pktTag->IPT_Packet, + pktTag->IPT_Length, + status); + IpxFwdDbgPrint (DBG_INT_SEND, + NT_SUCCESS (status) ? DBG_INFORMATION : DBG_WARNING, + ("IpxFwd: Returned internal packet %08lx" + " for send on if %ld with status %08lx.\n", + pktTag, dstIf->ICB_Index, status)); + ReleaseInterfaceReference (pktTag->IPT_InterfaceReference); + ExFreePool (pktTag); + } +} + + + +/*++ +******************************************************************* + + P r o c e s s E x t e r n a l Q u e u e + +Routine Description: + Processes packets in the interface external queue. + Called when connection request completes +Arguments: + dstIf - interface to process +Return Value: + None +******************************************************************* +--*/ +VOID +ProcessExternalQueue ( + PINTERFACE_CB dstIf + ) { + KIRQL oldIRQL; + LIST_ENTRY tempQueue; + + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + InsertHeadList (&dstIf->ICB_ExternalQueue, &tempQueue); + RemoveEntryList (&dstIf->ICB_ExternalQueue); + InitializeListHead (&dstIf->ICB_ExternalQueue); + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + + while (!IsListEmpty (&tempQueue)) { + PPACKET_TAG pktTag; + PLIST_ENTRY cur; + NDIS_STATUS status; + + cur = RemoveHeadList (&tempQueue); + pktTag = CONTAINING_RECORD (cur, PACKET_TAG, PT_QueueLink); + + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + if (IS_IF_ENABLED(dstIf) + && (dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP)) { + IPX_NODE_CPY (pktTag->PT_Target.MacAddress, + dstIf->ICB_RemoteNode); + if (*(pktTag->PT_Data + IPXH_PKTTYPE) == IPX_NETBIOS_TYPE) { + PUTULONG (dstIf->ICB_Network, pktTag->PT_Data+IPXH_DESTNET); + } + status = DoSend (dstIf, pktTag, oldIRQL); + IpxFwdDbgPrint (DBG_SEND, DBG_INFORMATION, + ("IpxFwd: Sent queued external packet %08lx if %ld.\n", + pktTag, dstIf->ICB_Index)); + } + else { + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + IpxFwdDbgPrint (DBG_SEND, DBG_WARNING, + ("IpxFwd: Dropped queued external packet %08lx on dead if %ld.\n", + pktTag, dstIf->ICB_Index)); + status = STATUS_UNSUCCESSFUL; + } + + if (status!=STATUS_PENDING) + ProcessSentPacket (dstIf, pktTag, status); + } +} + + +/*++ +******************************************************************* + + S p o o f e r + +Routine Description: + Processes packets in spoofing queue +Arguments: + None +Return Value: + None +******************************************************************* +--*/ +VOID +Spoofer ( + PVOID Context + ) { + KIRQL oldIRQL; + NTSTATUS status; + UNREFERENCED_PARAMETER (Context); + + KeAcquireSpinLock (&SpoofingQueueLock, &oldIRQL); + // Keep going till queue is empty + while (!IsListEmpty (&SpoofingQueue)) { + PINTERFACE_CB dstIf; + PPACKET_TAG pktTag = CONTAINING_RECORD (SpoofingQueue.Flink, + PACKET_TAG, + PT_QueueLink); + RemoveEntryList (&pktTag->PT_QueueLink); + KeReleaseSpinLock (&SpoofingQueueLock, oldIRQL); + dstIf = pktTag->PT_InterfaceReference; + if (dstIf==NULL) { + // Replies for internal server require first active LAN adapter + // SHOULD BE REMOVED WHEN LOOPBACK SUPPORT US ADDED BY IPX + while ((dstIf=GetNextInterfaceReference (dstIf))!=NULL) { + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + if (IS_IF_ENABLED (dstIf) + && (dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) + && (dstIf->ICB_InterfaceType==FWD_IF_PERMANENT)) { + pktTag->PT_InterfaceReference = dstIf; + IPX_NODE_CPY (&pktTag->PT_Target.MacAddress, dstIf->ICB_LocalNode); + status = DoSend (dstIf, pktTag, oldIRQL); // releases spin lock + if (status!=STATUS_PENDING) + ProcessSentPacket (dstIf, pktTag, status); + break; + } + else + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + } + if (dstIf==NULL) { + FreePacket (pktTag); + } + + } + else { // Reply for external server, interface is already known + UCHAR addr[12]; + FILTER_ACTION action; + pktTag->PT_Flags &= (~PT_SOURCE_IF); + + // Switch source and destination + memcpy (addr, pktTag->PT_Data+IPXH_DESTADDR, 12); + memcpy (pktTag->PT_Data+IPXH_DESTADDR, + pktTag->PT_Data+IPXH_SRCADDR, 12); + memcpy (pktTag->PT_Data+IPXH_SRCADDR, addr, 12); + // Say yes in reply + *(pktTag->PT_Data+IPXH_HDRSIZE+1) = 'Y'; + + action = FltFilter (pktTag->PT_Data, + GETUSHORT (pktTag->PT_Data+IPXH_LENGTH), + dstIf->ICB_FilterInContext, + pktTag->PT_SourceIf->ICB_FilterOutContext); + if (action==FILTER_PERMIT) { + + // Release destination if and use source as destination + ReleaseInterfaceReference (dstIf); + dstIf = pktTag->PT_InterfaceReference = pktTag->PT_SourceIf; + // Send the packet if we can + KeAcquireSpinLock (&dstIf->ICB_Lock, &oldIRQL); + if (IS_IF_ENABLED (dstIf) + && (dstIf->ICB_Stats.OperationalState==FWD_OPER_STATE_UP)) { + status = DoSend (dstIf, pktTag, oldIRQL); + if (status!=STATUS_PENDING) + ProcessSentPacket (dstIf, pktTag, status); + } + else { + KeReleaseSpinLock (&dstIf->ICB_Lock, oldIRQL); + InterlockedIncrement (&dstIf->ICB_Stats.OutDiscards); + ReleaseInterfaceReference (dstIf); + FreePacket (pktTag); + } + } + else { + if (action==FILTER_DENY_OUT) + InterlockedIncrement (&pktTag->PT_SourceIf->ICB_Stats.OutFiltered); + else { + ASSERT (action==FILTER_DENY_IN); + InterlockedIncrement (&dstIf->ICB_Stats.InFiltered); + } + ReleaseInterfaceReference (dstIf); + ReleaseInterfaceReference (pktTag->PT_SourceIf); + FreePacket (pktTag); + } + } + KeAcquireSpinLock (&SpoofingQueueLock, &oldIRQL); + } // end while + SpoofingWorkerActive = FALSE; + KeReleaseSpinLock (&SpoofingQueueLock, oldIRQL); + LeaveForwarder (); +} + + diff --git a/private/ntos/tdi/isn/fwd/send.h b/private/ntos/tdi/isn/fwd/send.h new file mode 100644 index 000000000..e46651e13 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/send.h @@ -0,0 +1,220 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\send.c + +Abstract: + Send routines + +Author: + + Vadim Eydelman + +Revision History: + +--*/ +#ifndef IPXFWD_SEND +#define IPXFWD_SEND + +typedef struct _INTERNAL_PACKET_TAG { + LIST_ENTRY IPT_QueueLink; + PNDIS_PACKET IPT_Packet; + PUCHAR IPT_DataPtr; + ULONG IPT_Length; + PINTERFACE_CB IPT_InterfaceReference; + IPX_LOCAL_TARGET IPT_Target; +} INTERNAL_PACKET_TAG, *PINTERNAL_PACKET_TAG; + + +#define DEF_SPOOFING_TIMEOUT (120*60) // Seconds +extern ULONG SpoofingTimeout; +extern LIST_ENTRY SpoofingQueue; +extern KSPIN_LOCK SpoofingQueueLock; +extern WORK_QUEUE_ITEM SpoofingWorker; +extern BOOLEAN SpoofingWorkerActive; +extern ULONG DontSuppressNonAgentSapAdvertisements; +VOID +Spoofer ( + PVOID Context + ); + +#define InitializeSendQueue() { \ + InitializeListHead (&SpoofingQueue); \ + KeInitializeSpinLock (&SpoofingQueueLock); \ + ExInitializeWorkItem (&SpoofingWorker, Spoofer, NULL); \ + SpoofingWorkerActive = FALSE; \ +} + +#define DeleteSendQueue() { \ + while (!IsListEmpty (&SpoofingQueue)) { \ + PPACKET_TAG pktTag = CONTAINING_RECORD (SpoofingQueue.Flink, \ + PACKET_TAG, \ + PT_QueueLink); \ + RemoveEntryList (&pktTag->PT_QueueLink); \ + if (pktTag->PT_InterfaceReference!=NULL) \ + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); \ + FreePacket (pktTag); \ + } \ +} + + +/*++ +******************************************************************* + S e n d P a c k e t + +Routine Description: + Enqueues packets to be sent by IPX stack +Arguments: + dstIf - over which interface to send + pktTag - packet to send +Return Value: + None + +******************************************************************* +--*/ +VOID +SendPacket ( + PINTERFACE_CB dstIf, + PPACKET_TAG pktTag + ); + +/*++ +******************************************************************* + F w S e n d C o m p l e t e + +Routine Description: + Called by IPX stack when send completes asynchronously +Arguments: + pktDscr - descriptor of the completed packet + status - result of send operation +Return Value: + None + +******************************************************************* +--*/ +VOID +IpxFwdSendComplete ( + PNDIS_PACKET pktDscr, + NDIS_STATUS NdisStatus + ); + +/*++ +******************************************************************* + + F w I n t e r n a l S e n d + +Routine Description: + Filter and routes packets sent by IPX stack +Arguments: + LocalTarget - the NicId and next hop router MAC address + Context - preferred interface on which to send + Packet - packet to be sent + ipxHdr - pointer to ipx header inside the packet + PacketLength - length of the packet + fIterate - a flag to indicate if this is a packet for the + iteration of which the Fwd takes responsibility + - typically type 20 NetBIOS frames + +Return Value: + + STATUS_SUCCESS - if the preferred NIC was OK and packet passed filtering + STATUS_NETWORK_UNREACHABLE - if the preferred was not OK or packet failed filtering + STATUS_PENDING - packet was queued until connection is established +******************************************************************* +--*/ +NTSTATUS +IpxFwdInternalSend ( + IN OUT PIPX_LOCAL_TARGET LocalTarget, + IN ULONG Context, + IN PNDIS_PACKET pktDscr, + IN PUCHAR ipxHdr, + IN PUCHAR data, + IN ULONG PacketLength, + IN BOOLEAN fIterate + ); + +/*++ +******************************************************************* + + P r o c e s s I n t e r n a l Q u e u e + +Routine Description: + Processes packets in the interface internal queue. + Called when connection request completes +Arguments: + dstIf - interface to process +Return Value: + None +******************************************************************* +--*/ +VOID +ProcessInternalQueue ( + PINTERFACE_CB dstIf + ); + + +/*++ +******************************************************************* + + P r o c e s s E x t e r n a l Q u e u e + +Routine Description: + Processes packets in the interface external queue. + Called when connection request completes +Arguments: + dstIf - interface to process +Return Value: + None +******************************************************************* +--*/ +VOID +ProcessExternalQueue ( + PINTERFACE_CB dstIf + ); +/*++ +******************************************************************* + D o S e n d + +Routine Description: + Prepares and sends packet. Interface lock must be help while + callin this routine +Arguments: + dstIf - over which interface to send + pktTag - packet to send +Return Value: + result returned by IPX + +******************************************************************* +--*/ +NDIS_STATUS +DoSend ( + PINTERFACE_CB dstIf, + PPACKET_TAG pktTag, + KIRQL oldIRQL + ); + +/*++ +******************************************************************* + P r o c e s s S e n t P a c k e t + +Routine Description: + Process completed sent packets +Arguments: + dstIf - interface over which packet was sent + pktTag - completed packet + status - result of send operation +Return Value: + None + +******************************************************************* +--*/ +VOID +ProcessSentPacket ( + PINTERFACE_CB dstIf, + PPACKET_TAG pktTag, + NDIS_STATUS status + ); +#endif diff --git a/private/ntos/tdi/isn/fwd/sources.inc b/private/ntos/tdi/isn/fwd/sources.inc new file mode 100644 index 000000000..3a7cd7ab5 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/sources.inc @@ -0,0 +1,61 @@ +!IF 0 + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: Vadim Eydelman + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ndis + +TARGETNAME=nwlnkfwd +TARGETTYPE=DRIVER + +TARGETLIBS=\ + $(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\driver.c \ + ..\ipxbind.c \ + ..\rcvind.c \ + ..\send.c \ + ..\registry.c \ + ..\lineind.c \ + ..\ddreqs.c \ + ..\netbios.c \ + ..\packets.c \ + ..\tables.c \ + ..\debug.c \ + ..\filterif.c \ + ..\nwlnkfwd.rc + +RELATIVE_DEPTH=..\.. +ALT_PROJECT_TARGET=Routing + + \ No newline at end of file diff --git a/private/ntos/tdi/isn/fwd/tables.c b/private/ntos/tdi/isn/fwd/tables.c new file mode 100644 index 000000000..baec3cced --- /dev/null +++ b/private/ntos/tdi/isn/fwd/tables.c @@ -0,0 +1,1512 @@ + +#include "precomp.h" + +// Memory zone for interfaces +ZONE_HEADER InterfaceZone; +// Segment size in interface sone +ULONG InterfaceSegmentSize= + sizeof(INTERFACE_CB)*NUM_INTERFACES_PER_SEGMENT + +sizeof (ZONE_SEGMENT_HEADER); +KSPIN_LOCK InterfaceZoneLock; + +// Interface tables +LIST_ENTRY *InterfaceIndexHash; // Hash by interface index +PINTERFACE_CB *ClientNodeHash; // Hash by node on qlobal net +INTERFACE_CB TheInternalInterface; // The internal interface +PINTERFACE_CB InternalInterface=&TheInternalInterface; +KSPIN_LOCK InterfaceTableLock; // Protection for interface hash tables + +// Memory Zone for routes +ZONE_HEADER RouteZone; +// Segment size in route sone +ULONG RouteSegmentSize=DEF_ROUTE_SEGMENT_SIZE; +KSPIN_LOCK RouteZoneLock; + +// Route tables +PFWD_ROUTE *RouteHash; +PFWD_ROUTE GlobalRoute; +ULONG GlobalNetwork; + + +// NB Route table +PNB_ROUTE *NBRouteHash; + + +// Reader-writer lock to wait for all readers to drain when +// updating the route tables +RW_LOCK RWLock; +// Mutex to serialize writers to route tables +FAST_MUTEX WriterMutex; + + +// Sizes of the tables +ULONG RouteHashSize; // Must be specified +ULONG InterfaceHashSize=DEF_INTERFACE_HASH_SIZE; +ULONG ClientHashSize=DEF_CLIENT_HASH_SIZE; +ULONG NBRouteHashSize=DEF_NB_ROUTE_HASH_SIZE; + +//*** max send pkts queued limit: over this limit the send pkts get discarded +ULONG MaxSendPktsQueued = MAX_SEND_PKTS_QUEUED; +INT WanPacketListId = -1; + +// Initial memory block allocated for the tables +CHAR *TableBlock = NULL; + +// Hash functions +#define InterfaceIndexHashFunc(Interface) (Interface%InterfaceHashSize) +#define ClientNodeHashFunc(Node64) ((UINT)(Node64%ClientHashSize)) +#define NetworkNumberHashFunc(Network) (Network%RouteHashSize) +#define NetbiosNameHashFunc(Name128) ((UINT)(Name128[0]+Name128[1])%NBRouteHashSize) + +/*++ +******************************************************************* + A l l o c a t e R o u t e + +Routine Description: + Allocates memory for route from memory zone reserved + for route storage. Extends zone if there are no + free blocks in currently allocated segements. +Arguments: + None +Return Value: + Pointer to allocated route + +******************************************************************* +--*/ +PFWD_ROUTE +AllocateRoute ( + void + ) { + PFWD_ROUTE fwRoute; + KIRQL oldIRQL; + + KeAcquireSpinLock (&RouteZoneLock, &oldIRQL); + // Check if there are free blocks in the zone + if (ExIsFullZone (&RouteZone)) { + // Try to allocate new segment if not + NTSTATUS status; + PVOID segment = ExAllocatePoolWithTag + (NonPagedPool, RouteSegmentSize, FWD_POOL_TAG); + if (segment==NULL) { + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Can't allocate route zone segment.\n")); + return NULL; + } + status = ExExtendZone (&RouteZone, segment, RouteSegmentSize); + ASSERTMSG ("Could not extend RouteZone ", NT_SUCCESS (status)); + } + fwRoute = (PFWD_ROUTE)ExAllocateFromZone (&RouteZone); + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + return fwRoute; +} + +/*++ +******************************************************************* + F r e e R o u t e + +Routine Description: + Releases memory allocated for route to route memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeRoute ( + PFWD_ROUTE fwRoute + ) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_INFORMATION, + ("IpxFwd: Freeing route block %08lx.\n", fwRoute)); + ASSERT (fwRoute->FR_InterfaceReference==NULL); + ExInterlockedFreeToZone(&RouteZone,fwRoute,&RouteZoneLock); +} + + +/*++ +******************************************************************* + A l l o c a t e I n t e r f a c e + +Routine Description: + Allocates memory for interface from memory zone reserved + for interface storage. Extends zone if there are no + free blocks in currently allocated segements. +Arguments: + None +Return Value: + Pointer to allocated route + +******************************************************************* +--*/ +PINTERFACE_CB +AllocateInterface ( + void + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + + KeAcquireSpinLock (&RouteZoneLock, &oldIRQL); + // Check if there are free blocks in the zone + if (ExIsFullZone (&InterfaceZone)) { + // Try to allocate new segment if not + NTSTATUS status; + PVOID segment = ExAllocatePoolWithTag + (NonPagedPool, InterfaceSegmentSize, FWD_POOL_TAG); + if (segment==NULL) { + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Can't allocate interface zone segment.\n")); + return NULL; + } + status = ExExtendZone (&InterfaceZone, segment, InterfaceSegmentSize); + ASSERTMSG ("Could not extend InterfaceZone ", NT_SUCCESS (status)); + } + ifCB = (PINTERFACE_CB)ExAllocateFromZone (&InterfaceZone); + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + return ifCB; +} + +/*++ +******************************************************************* + F r e e I n t e r f a c e + +Routine Description: + Releases memory allocated for interface to interface memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeInterface ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Freeing icb %08lx.\n", ifCB)); + + ASSERT(ifCB->ICB_Stats.OperationalState==FWD_OPER_STATE_DOWN); + KeAcquireSpinLock (&InterfaceZoneLock, &oldIRQL); + ExFreeToZone(&InterfaceZone, ifCB); + KeReleaseSpinLock (&InterfaceZoneLock, oldIRQL); +} + +/*++ +******************************************************************* + C r e a t e T a b l e s + +Routine Description: + Allocates and intializes all hash tables and related structures +Arguments: + None +Return Value: + STATUS_SUCCESS - tables were created ok + STATUS_INSUFFICIENT_RESOURCES - resource allocation failed +******************************************************************* +--*/ +NTSTATUS +CreateTables ( + void + ) { + UINT i; + CHAR *segment; + NTSTATUS status; + ULONG blockSize; + + ASSERT (TableBlock==NULL); + + blockSize = ROUND_TO_PAGES ( + InterfaceHashSize*sizeof(*InterfaceIndexHash) + +ClientHashSize*sizeof(*ClientNodeHash) + +RouteHashSize*sizeof(*RouteHash) + +NBRouteHashSize*sizeof(*NBRouteHash) + +InterfaceSegmentSize + +RouteSegmentSize + ); + + // Allocate first segment for route zone + TableBlock = segment = (CHAR *)ExAllocatePoolWithTag ( + NonPagedPool, blockSize, FWD_POOL_TAG); + if (segment!=NULL) { + InterfaceIndexHash = (LIST_ENTRY *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(InterfaceIndexHash+InterfaceHashSize),ULONGLONG); + + ClientNodeHash = (PINTERFACE_CB *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(ClientNodeHash+ClientHashSize),ULONGLONG); + + RouteHash = (PFWD_ROUTE *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(RouteHash + RouteHashSize),ULONGLONG); + + NBRouteHash = (PNB_ROUTE *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(NBRouteHash + NBRouteHashSize),ULONGLONG); + + status = ExInitializeZone (&InterfaceZone, + ALIGN_UP(sizeof (INTERFACE_CB),ULONGLONG), + segment, + InterfaceSegmentSize); + ASSERTMSG ("Could not initalize InterfaceZone ", + NT_SUCCESS (status)); + segment = (CHAR *)ALIGN_UP((ULONG)(segment+InterfaceSegmentSize),ULONGLONG); + + status = ExInitializeZone (&RouteZone, + ALIGN_UP(sizeof (FWD_ROUTE), ULONGLONG), + segment, + blockSize - (segment - TableBlock)); + + ASSERTMSG ("Could not initalize RouteZone ", NT_SUCCESS (status)); + + + // No global route yet + GlobalRoute = NULL; + GlobalNetwork = 0xFFFFFFFF; + + InternalInterface = &TheInternalInterface; + InitICB (InternalInterface, + FWD_INTERNAL_INTERFACE_INDEX, + FWD_IF_PERMANENT, + TRUE, + FWD_NB_DELIVER_ALL); +#if DBG + InitializeListHead (&InternalInterface->ICB_InSendQueue); +#endif + + KeInitializeSpinLock (&InterfaceTableLock); + KeInitializeSpinLock (&InterfaceZoneLock); + KeInitializeSpinLock (&RouteZoneLock); + InitializeRWLock (&RWLock); + ExInitializeFastMutex (&WriterMutex); + + // Initialize hash tables buckets + for (i=0; iFR_Next; + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + } + } + // Don't forget about global route + if (GlobalRoute!=NULL) { + GlobalRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (GlobalRoute); + GlobalRoute = NULL; + GlobalNetwork = 0xFFFFFFFF; + } + + // Now we should be able to release all interfaces + for (i=0; iICB_IndexHashLink); + if (ifCB->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) { + switch (ifCB->ICB_InterfaceType) { + case FWD_IF_PERMANENT: + DeregisterPacketConsumer (ifCB->ICB_PacketListId); + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + break; + default: + ASSERTMSG ("Invalid interface type ", FALSE); + break; + } + if (ifCB->ICB_CashedInterface!=NULL) + ReleaseInterfaceReference (ifCB->ICB_CashedInterface); + ifCB->ICB_CashedInterface = NULL; + if (ifCB->ICB_CashedRoute!=NULL) + ReleaseRouteReference (ifCB->ICB_CashedRoute); + ifCB->ICB_CashedRoute = NULL; + if (ifCB->ICB_Network==GlobalNetwork) + DeleteGlobalNetClient (ifCB); + IPXCloseAdapterProc (ifCB->ICB_AdapterContext); + ReleaseInterfaceReference (ifCB); // Binding reference + } + + if (IS_IF_CONNECTING (ifCB)) { + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + } + + while (!IsListEmpty (&ifCB->ICB_ExternalQueue)) { + PPACKET_TAG pktTag; + + pktTag = CONTAINING_RECORD (ifCB->ICB_ExternalQueue.Flink, + PACKET_TAG, PT_QueueLink); + RemoveEntryList (&pktTag->PT_QueueLink); + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); + FreePacket (pktTag); + } + + while (!IsListEmpty (&ifCB->ICB_InternalQueue)) { + PINTERNAL_PACKET_TAG pktTag; + + pktTag = CONTAINING_RECORD (ifCB->ICB_InternalQueue.Flink, + INTERNAL_PACKET_TAG, IPT_QueueLink); + RemoveEntryList (&pktTag->IPT_QueueLink); + IPXInternalSendCompletProc (&pktTag->IPT_Target, + pktTag->IPT_Packet, + pktTag->IPT_Length, + STATUS_NETWORK_UNREACHABLE); + ReleaseInterfaceReference (pktTag->IPT_InterfaceReference); + ExFreePool (pktTag); + } + + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + if (ifCB->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (ifCB->ICB_NBRoutes, ifCB->ICB_NBRouteCount); + ifCB->ICB_NBRoutes = NULL; + } + ReleaseInterfaceReference (ifCB); + } + } + + if (InternalInterface->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (InternalInterface->ICB_NBRoutes, + InternalInterface->ICB_NBRouteCount); + InternalInterface->ICB_NBRoutes = NULL; + } + if (InternalInterface->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) { + InternalInterface->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + ReleaseInterfaceReference (InternalInterface); // Binding reference + } + ReleaseInterfaceReference (InternalInterface); + + + + // Release extra memory segments used for route table entries + segment = PopEntryList (&RouteZone.SegmentList); + while (RouteZone.SegmentList.Next!=NULL) { + ExFreePool (segment); + segment = PopEntryList (&RouteZone.SegmentList); + } + + // Release extra memory segments used for interface table entries + segment = PopEntryList (&InterfaceZone.SegmentList); + while (InterfaceZone.SegmentList.Next!=NULL) { + ExFreePool (segment); + segment = PopEntryList (&InterfaceZone.SegmentList); + } + + ExFreePool (TableBlock); + TableBlock = NULL; + return STATUS_SUCCESS; +} + +/*++ +******************************************************************* + L o c a t e I n t e r f a c e + +Routine Description: + Finds interface control block in interface + index hash table. Optionally returns the + insertion point pointer if interface block + with given index is not in the table. +Arguments: + InterfaceIndex - unique id of the interface + insertBefore - buffer to place the pointer to + hash table element where interface + block should be inserted if it is not + already in the table +Return Value: + Pointer to interface control block if found + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +LocateInterface ( + ULONG InterfaceIndex, + PLIST_ENTRY *insertBefore OPTIONAL + ) { + PLIST_ENTRY cur; + PINTERFACE_CB ifCB; + PLIST_ENTRY HashList; + + ASSERT (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX); + + // Find hash bucket + HashList = &InterfaceIndexHash[InterfaceIndexHashFunc(InterfaceIndex)]; + cur = HashList->Flink; + // Walk the list + while (cur!=HashList) { + ifCB = CONTAINING_RECORD(cur, INTERFACE_CB, ICB_IndexHashLink); + + if (ifCB->ICB_Index==InterfaceIndex) + // Found, return it (insertion point is irrelevant) + return ifCB; + else if (ifCB->ICB_Index>InterfaceIndex) + // No chance to find it + break; + cur = cur->Flink; + } + // Return insertion point if asked + if (ARGUMENT_PRESENT(insertBefore)) + *insertBefore = cur; + return NULL; +} + +/*++ +******************************************************************* + L o c a t e C l i e n t I n t e r f a c e + +Routine Description: + Finds interface control block in client + node hash bucket. Optionally returns the + insertion point pointer if interface block + with given node is not in the table +Arguments: + ClientNode - node address of the client on global network + insertBefore - buffer to place the pointer to + hash table element where interface + block should be inserted if it is not + already in the table +Return Value: + Pointer to interface control block if found + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +LocateClientInterface ( + ULONGLONG *NodeAddress64, + PINTERFACE_CB **prevLink OPTIONAL + ) { + PINTERFACE_CB cur, *prev; + + prev = &ClientNodeHash[ClientNodeHashFunc (*NodeAddress64)]; + cur = *prev; + while (cur!=NULL) { + if (*NodeAddress64==cur->ICB_ClientNode64[0]) + break; + else if (*NodeAddress64>cur->ICB_ClientNode64[0]) { + // No chance to find it + cur = NULL; + break; + } + prev = &cur->ICB_NodeHashLink; + cur = cur->ICB_NodeHashLink; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + return cur; +} + +/*++ +******************************************************************* + L o c a t e R o u t e + +Routine Description: + Finds route block in network number + hash table. Optionally returns the + insertion point pointer if route + for given destination netowrk is not in the table +Arguments: + Network - destination netowork number + insertBefore - buffer to place the pointer to + hash table element where route + block should be inserted if it is not + already in the table +Return Value: + Pointer to route block if found + NULL otherwise +******************************************************************* +--*/ +PFWD_ROUTE +LocateRoute ( + ULONG Network, + PFWD_ROUTE **prevLink OPTIONAL + ) { + PFWD_ROUTE cur, *prev; + + prev = &RouteHash[NetworkNumberHashFunc(Network)]; + cur = *prev; + + while (cur!=NULL) { + if (cur->FR_Network==Network) + break; + else if (cur->FR_Network>Network) { + cur = NULL; + // No chance to find it + break; + } + prev = &cur->FR_Next; + cur = *prev; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + + return cur; +} + +/*++ +******************************************************************* + L o c a t e N B R o u t e + +Routine Description: + Finds nb route block in nb name + hash table. Optionally returns the + insertion point pointer if nb route + for given name is not in the table +Arguments: + Name - netbios name + insertBefore - buffer to place the pointer to + hash table element where route + block should be inserted if it is not + already in the table +Return Value: + Pointer to nb route block if found + NULL otherwise +******************************************************************* +--*/ +PNB_ROUTE +LocateNBRoute ( + ULONGLONG *Name128, + PNB_ROUTE **prevLink OPTIONAL + ) { + PNB_ROUTE cur, *prev; + + prev = &NBRouteHash[NetbiosNameHashFunc(Name128)]; + cur = *prev; + + while (cur!=NULL) { + if ((cur->NBR_Name128[0]==Name128[0]) + && (cur->NBR_Name128[1]==Name128[1])) + break; + else if ((cur->NBR_Name128[0]>Name128[0]) + || ((cur->NBR_Name128[0]==Name128[0]) + && (cur->NBR_Name128[1]>Name128[1]))) { + cur = NULL; + // No chance to find it + break; + } + prev = &cur->NBR_Next; + cur = *prev; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + + return cur; +} + +/*++ +******************************************************************* + G e t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference interface based on its index +Arguments: + InterfaceIndex - unique id of the interface +Return Value: + Pointer to interface control block if there is one in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetInterfaceReference ( + ULONG InterfaceIndex + ) { + KIRQL oldIRQL; + PINTERFACE_CB ifCB; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + + if (ifCB!=NULL) + AcquireInterfaceReference (ifCB); + else { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Could not get interface reference %ld.\n", InterfaceIndex)); + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return ifCB; +} + +/*++ +******************************************************************* + G e t N e x t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference to the next interface in the table + Reference to the provided interface is released +Arguments: + ifCB - interface to start with or NULL to start from the + beginning of the interface table +Return Value: + Pointer to interface control block if thare are any more interfaces + in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetNextInterfaceReference ( + PINTERFACE_CB ifCB + ) { + PLIST_ENTRY cur; + PLIST_ENTRY HashList; + KIRQL oldIRQL; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (ifCB!=NULL) { + // Find hash bucket + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + HashList = &InterfaceIndexHash[InterfaceIndexHashFunc(ifCB->ICB_Index)]; + if (LocateInterface (ifCB->ICB_Index, &cur)!=NULL) + cur = ifCB->ICB_IndexHashLink.Flink; + ReleaseInterfaceReference (ifCB); + ifCB = NULL; + } + else + cur = HashList = InterfaceIndexHash-1; + + if (cur==HashList) { + do { + HashList += 1; + if (HashList==&InterfaceIndexHash[InterfaceHashSize]) { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return NULL; + } + } while (IsListEmpty (HashList)); + cur = HashList->Flink; + } + ifCB = CONTAINING_RECORD (cur, INTERFACE_CB, ICB_IndexHashLink); + AcquireInterfaceReference (ifCB); + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + + return ifCB; +} + + +/*++ +******************************************************************* + A d d I n t e r f a c e + +Routine Description: + Adds interface control block to the table. +Arguments: + InterfaceIndex - unique if of the interface + Info - interface paramters +Return Value: + STATUS_SUCCESS - interface added ok + STATUS_UNSUCCESSFUL - interface is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + interface CB +******************************************************************* +--*/ +NTSTATUS +AddInterface ( + ULONG InterfaceIndex, + UCHAR InterfaceType, + BOOLEAN NetbiosAccept, + UCHAR NetbiosDeliver + ) { + PINTERFACE_CB ifCB; + PLIST_ENTRY cur; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + ifCB = LocateInterface (InterfaceIndex, &cur); + if (ifCB==NULL) { + ifCB = AllocateInterface (); + if (ifCB!=NULL) + NOTHING; + else { + status = STATUS_INSUFFICIENT_RESOURCES; + goto AddEnd; + } + } + else { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld is already in the table!\n", InterfaceIndex)); + status = STATUS_UNSUCCESSFUL; + goto AddEnd; + } + } + else + ifCB = InternalInterface; + + InitICB (ifCB, InterfaceIndex,InterfaceType,NetbiosAccept,NetbiosDeliver); +#if DBG + InitializeListHead (&ifCB->ICB_InSendQueue); +#endif + + switch (InterfaceType) { + case FWD_IF_PERMANENT: + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + ASSERT (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX); + if (WanPacketListId==-1) { + status = RegisterPacketConsumer ( + WAN_PACKET_SIZE, + &WanPacketListId); + if (!NT_SUCCESS (status)) { + WanPacketListId = -1; + break; + } + } + ifCB->ICB_PacketListId = WanPacketListId; + break; + } + + if (NT_SUCCESS (status)) { + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + InsertTailList (cur, &ifCB->ICB_IndexHashLink); + } + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Adding interface %d (icb: %08lx, plid: %d)\n", + InterfaceIndex, ifCB, ifCB->ICB_PacketListId)); + } + else + FreeInterface (ifCB); + +AddEnd: + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return status; +} + + +/*++ +******************************************************************* + A d d G l o b a l N e t C l i e n t + +Routine Description: + Adds interface control block to the table of + clients on the global network (should be done when + client connects) +Arguments: + ifCB - interface control block to add to the table +Return Value: + STATUS_SUCCESS - interface was added ok + STATUS_UNSUCCESSFUL - another interface with the same + node address is already in the table +******************************************************************* +--*/ +NTSTATUS +AddGlobalNetClient ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + RWCOOKIE cookie; + PINTERFACE_CB *prev; + NTSTATUS status = STATUS_SUCCESS; + + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + + AcquireReaderAccess (&RWLock, cookie); + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (LocateClientInterface (ifCB->ICB_ClientNode64, &prev)==NULL) { + ifCB->ICB_NodeHashLink = *prev; + *prev = ifCB; + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + AcquireInterfaceReference (ifCB); // To make sure that + // interface block does not + // get deleted until it is + // removed from the node table + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Adding interface %ld (icb: %08lx)" + " to global client table.\n", ifCB->ICB_Index, ifCB)); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx)" + " is already in the global client table.\n", + ifCB->ICB_Index, ifCB)); + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +/*++ +******************************************************************* + D e l e t e G l o b a l N e t C l i e n t + +Routine Description: + Removes interface control block from the table of + clients on the global network (should be done when + client disconnects) +Arguments: + ifCB - interface control block to remove from the table +Return Value: + STATUS_SUCCESS - interface was removed ok +******************************************************************* +--*/ +NTSTATUS +DeleteGlobalNetClient ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + RWCOOKIE cookie; + PINTERFACE_CB cur, *prev; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Deleting interface %ld (icb: %08lx)" + " from global client table.\n", ifCB->ICB_Index, ifCB)); + + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + + AcquireReaderAccess (&RWLock, cookie); + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + cur = LocateClientInterface (ifCB->ICB_ClientNode64, &prev); + ASSERT (cur==ifCB); + *prev = ifCB->ICB_NodeHashLink; + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; +} + + +/*++ +******************************************************************* + D e l e t e I n t e r f a c e + +Routine Description: + Deletes interface control block (the block is not actually + disposed of until all references to it are released). +Arguments: + InterfaceIndex - unique if of the interface +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFUL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteInterface ( + ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + if (ifCB!=NULL) { + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + RemoveEntryList (&ifCB->ICB_IndexHashLink); + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + if (ifCB->ICB_Stats.OperationalState == FWD_OPER_STATE_UP) { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx) was still bound" + " when asked to delete it.\n", + ifCB->ICB_Index, ifCB)); + UnbindInterface (ifCB); + } + else if (IS_IF_CONNECTING (ifCB)) { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx) was still being connected" + " when asked to delete it.\n", + ifCB->ICB_Index, ifCB)); + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Deleting interface %ld (icb: %08lx).\n", + ifCB->ICB_Index, ifCB)); + + if (ifCB->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (ifCB->ICB_NBRoutes, ifCB->ICB_NBRouteCount); + ifCB->ICB_NBRoutes = NULL; + } + + FltInterfaceDeleted (ifCB); + ReleaseInterfaceReference (ifCB); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Could not delete interface %ld because it is not found.\n", + InterfaceIndex)); + status = STATUS_UNSUCCESSFUL; + } + return status; + +} + +/*++ +******************************************************************* + A d d R o u t e + +Routine Description: + Adds route to the hash table and finds and stores the reference + to the associated interface control block in the route. +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - route was added ok + STATUS_UNSUCCESSFUL - route is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route block +******************************************************************* +--*/ +NTSTATUS +AddRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ) { + PFWD_ROUTE fwRoute; + PFWD_ROUTE *prev; + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIRQL; + + // Assume success, allocate route and intialize it + // (the goal is to spend as little time as possible + // inside exclusive usage zone) + fwRoute = AllocateRoute (); + if (fwRoute!=NULL) { + fwRoute->FR_Network = Network; + IPX_NODE_CPY (fwRoute->FR_NextHopAddress, NextHopAddress); + fwRoute->FR_TickCount = TickCount; + fwRoute->FR_HopCount = HopCount; + fwRoute->FR_ReferenceCount = 0; + + if (InterfaceIndex!=0xFFFFFFFF) { + // See if interface is there + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + fwRoute->FR_InterfaceReference + = LocateInterface (InterfaceIndex, NULL); + else + fwRoute->FR_InterfaceReference = InternalInterface; + if (fwRoute->FR_InterfaceReference!=NULL) { + AcquireInterfaceReference (fwRoute->FR_InterfaceReference); + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + + ExAcquireFastMutex (&WriterMutex); + // Check if route is already there + if (LocateRoute (Network, &prev)==NULL) { + fwRoute->FR_Next = *prev; + *prev = fwRoute; + } + else { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + fwRoute->FR_InterfaceReference = NULL; + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Route for net %08lx" + " is already in the table!\n", Network)); + status = STATUS_UNSUCCESSFUL; + } + + ExReleaseFastMutex (&WriterMutex); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld for route for net %08lx" + " is not in the table!\n", InterfaceIndex, Network)); + status = STATUS_UNSUCCESSFUL; + } + } + else { + ExAcquireFastMutex (&WriterMutex); + // Just check if we do not have it already + if (GlobalRoute==NULL) { + fwRoute->FR_InterfaceReference = GLOBAL_INTERFACE_REFERENCE; + GlobalNetwork = Network; + GlobalRoute = fwRoute; + } + else { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Route for global net %08lx" + " is already in the table!\n", Network)); + status = STATUS_UNSUCCESSFUL; + } + ExReleaseFastMutex (&WriterMutex); + } + + if (NT_SUCCESS (status)) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Adding route for net %08lx" + " (rb: %08lx, NHA: %02x%02x%02x%02x%02x%02x," + " if: %ld, icb: %08lx).\n", + Network, fwRoute, + NextHopAddress[0], NextHopAddress[1], + NextHopAddress[2], NextHopAddress[3], + NextHopAddress[4], NextHopAddress[5], + InterfaceIndex, fwRoute->FR_InterfaceReference)); + } + else { + FreeRoute (fwRoute); + } + } + else + status = STATUS_INSUFFICIENT_RESOURCES; + + return status; +} + +/*++ +******************************************************************* + D e l e t e R o u t e + +Routine Description: + Deletes route from the hash table and releases the reference + to the interface control block associated with the route. +Arguments: + Network - route's destination network +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteRoute ( + ULONG Network + ) { + PFWD_ROUTE fwRoute, *prev; + NTSTATUS status = STATUS_SUCCESS; + + ExAcquireFastMutex (&WriterMutex); + + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + fwRoute = GlobalRoute; + GlobalNetwork = 0xFFFFFFFF; + GlobalRoute = NULL; + } + else if ((fwRoute=LocateRoute (Network, &prev))!=NULL) { + *prev = fwRoute->FR_Next; + } + + if (fwRoute!=NULL) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Deleting route for net %08lx (rb: %08lx).\n", + Network, fwRoute)); + WaitForAllReaders (&RWLock); + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + } + else { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Could not delete route for net %08lx because it is not in the table.\n", + Network)); + status = STATUS_UNSUCCESSFUL; + } + + ExReleaseFastMutex (&WriterMutex); + return status; +} + + +/*++ +******************************************************************* + U p d a t e R o u t e + +Routine Description: + Updates route in the hash table +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFUL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +UpdateRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ) { + PFWD_ROUTE fwRoute = NULL, newRoute, *prev; + PINTERFACE_CB ifCB = NULL; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + + ExAcquireFastMutex (&WriterMutex); + + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + ASSERT (InterfaceIndex==0xFFFFFFFF); + fwRoute = GlobalRoute; + } + else { + ASSERT (InterfaceIndex!=0xFFFFFFFF); + fwRoute = LocateRoute (Network, &prev); + } + + if (fwRoute!=NULL) { + if (InterfaceIndex!=0xFFFFFFFF) { + if (fwRoute->FR_InterfaceReference->ICB_Index!=InterfaceIndex) { + // Get a reference to new interface + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + if (ifCB!=NULL) { + AcquireInterfaceReference (ifCB); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + status = STATUS_UNSUCCESSFUL; + goto ExitUpdate; + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + } + else { + ifCB = fwRoute->FR_InterfaceReference; + AcquireInterfaceReference (ifCB); + } + } + else + ifCB = GLOBAL_INTERFACE_REFERENCE; + newRoute = AllocateRoute (); + if (newRoute!=NULL) { + newRoute->FR_Network = Network; + IPX_NODE_CPY (newRoute->FR_NextHopAddress, NextHopAddress); + newRoute->FR_TickCount = TickCount; + newRoute->FR_HopCount = HopCount; + newRoute->FR_ReferenceCount = 0; + newRoute->FR_InterfaceReference = ifCB; + // Lock the table only when updating it + if (InterfaceIndex!=0xFFFFFFFF) { + newRoute->FR_Next = fwRoute->FR_Next; + *prev = newRoute; + } + else + GlobalRoute = newRoute; + + WaitForAllReaders (&RWLock) + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + + } + else + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + status = STATUS_UNSUCCESSFUL; + +ExitUpdate: + ExReleaseFastMutex (&WriterMutex); + return status; +} + + +/*++ +******************************************************************* + F i n d D e s t i n a t i o n + +Routine Description: + Finds destination interface for IPX address and + returns reference to its control block. +Arguments: + Network - destination network + Node - destination node (needed in case of global client) + Route - buffer to hold reference to route block +Return Value: + Reference to destination interface CB + NULL if route it not found +******************************************************************* +--*/ +PINTERFACE_CB +FindDestination ( + IN ULONG Network, + IN PUCHAR Node, + OUT PFWD_ROUTE *Route + ) { + PFWD_ROUTE fwRoute; + PINTERFACE_CB ifCB; + RWCOOKIE cookie; + + AcquireReaderAccess (&RWLock, cookie); + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + if (Node!=NULL) { // If caller did not specify node, + // we can't find the route + union { + ULONGLONG Node64[1]; + UCHAR Node[6]; + } u; + u.Node64[0] = 0; + IPX_NODE_CPY (u.Node, Node); + + ifCB = LocateClientInterface (u.Node64, NULL); + if (ifCB!=NULL) { + AcquireRouteReference (GlobalRoute); + *Route = GlobalRoute; + AcquireInterfaceReference (ifCB); + } + else + *Route = NULL; + } + else { + ifCB = NULL; + *Route = NULL; + } + } + else { + *Route = fwRoute = LocateRoute (Network, NULL); + if (fwRoute!=NULL) { + AcquireRouteReference (fwRoute); + ifCB = fwRoute->FR_InterfaceReference; + AcquireInterfaceReference (ifCB); + } + else + ifCB = NULL; + } + ReleaseReaderAccess (&RWLock, cookie); + return ifCB; +} + +/*++ +******************************************************************* + A d d N B R o u t e s + +Routine Description: + Adds netbios names associated with interface to netbios + route hash table +Arguments: + ifCB - interface with which names are associated + Names - array of names + Count - number of names in the array + routeArray - buffer to place allocated array of routes +Return Value: + STATUS_SUCCESS - names were added ok + STATUS_UNSUCCESSFUL - one of the names is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route array +******************************************************************* +--*/ +NTSTATUS +AddNBRoutes ( + PINTERFACE_CB ifCB, + FWD_NB_NAME Names[], + ULONG Count, + PNB_ROUTE *routeArray + ) { + PNB_ROUTE nbRoutes, *prev; + NTSTATUS status = STATUS_SUCCESS; + + nbRoutes = (PNB_ROUTE)ExAllocatePoolWithTag ( + NonPagedPool, sizeof (NB_ROUTE)*Count, FWD_POOL_TAG); + if (nbRoutes!=NULL) { + ULONG i; + + ExAcquireFastMutex (&WriterMutex); + + for (i=0; iICB_Index, ifCB)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + return status; +} + +/*++ +******************************************************************* + D e l e t e N B R o u t e s + +Routine Description: + Deletes nb routes in the array from the route table and frees + the array +Arguments: + nbRoutes - array of routes + Count - number of routes in the array +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteNBRoutes ( + PNB_ROUTE nbRoutes, + ULONG Count + ) { + PNB_ROUTE *prev; + NTSTATUS status = STATUS_SUCCESS; + ULONG i; + + ExAcquireFastMutex (&WriterMutex); + for (i=0; iNBR_Destination; + AcquireInterfaceReference (ifCB); + } + else + ifCB = NULL; + ReleaseReaderAccess (&RWLock, cookie); + return ifCB; +} + diff --git a/private/ntos/tdi/isn/fwd/tables.h b/private/ntos/tdi/isn/fwd/tables.h new file mode 100644 index 000000000..6f86e52a8 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/tables.h @@ -0,0 +1,708 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntos\tdi\isn\fwd\tables.h + +Abstract: + IPX Forwarder Driver Tables + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFWD_TABLES_ +#define _IPXFWD_TABLES_ + +// Ranges and defaults for registry configurable parameters +#define MIN_ROUTE_SEGMENT_SIZE PAGE_SIZE +#define MAX_ROUTE_SEGMENT_SIZE (PAGE_SIZE*8) +#define DEF_ROUTE_SEGMENT_SIZE MIN_ROUTE_SEGMENT_SIZE + +#define MIN_INTERFACE_HASH_SIZE 31 +#define MAX_INTERFACE_HASH_SIZE 257 +#define DEF_INTERFACE_HASH_SIZE MAX_INTERFACE_HASH_SIZE + +#define MIN_CLIENT_HASH_SIZE 31 +#define MAX_CLIENT_HASH_SIZE 257 +#define DEF_CLIENT_HASH_SIZE MAX_CLIENT_HASH_SIZE + +#define MIN_NB_ROUTE_HASH_SIZE 31 +#define MAX_NB_ROUTE_HASH_SIZE 1023 +#define DEF_NB_ROUTE_HASH_SIZE 257 + +#define MAX_SEND_PKTS_QUEUED 256 // No defined range + +#define NUM_INTERFACES_PER_SEGMENT 16 // Not configurable currently +#define NUM_NB_ROUTES_PER_SEGMENT 16 // Not configurable currently + + +// Special number reserved for routes that point to +// client on global net +#define GLOBAL_INTERFACE_REFERENCE ((PINTERFACE_CB)-1) + +// All types of WAN are emulated as ETHERNET by NDIS +#define WAN_PACKET_SIZE 1500 + + +#define INVALID_NETWORK_NUMBER 0xFFFFFFFF +#define INVALID_NIC_ID 0xFFFF + + +// Interface control block +struct _INTERFACE_CB; +typedef struct _INTERFACE_CB INTERFACE_CB, *PINTERFACE_CB; +struct _FWD_ROUTE; +typedef struct _FWD_ROUTE FWD_ROUTE, *PFWD_ROUTE; +struct _NB_ROUTE; +typedef struct _NB_ROUTE NB_ROUTE, *PNB_ROUTE; + +struct _FWD_ROUTE { + ULONG FR_Network; // Dest network + USHORT FR_TickCount; // Route params + USHORT FR_HopCount; // + UCHAR FR_NextHopAddress[6]; // Next hop router + PINTERFACE_CB FR_InterfaceReference; // Associated if CB + // or NULL if global + // network for clients + LONG FR_ReferenceCount; // Number of external + // references for this + // block (must keep the + // it till all of them + // are released + PFWD_ROUTE FR_Next; // Next route in the + // table +}; + +struct _INTERFACE_CB { + ULONG ICB_Index; // Unique ID + ULONG ICB_Network; // Network we boud to + union { + ULONGLONG ICB_ClientNode64[1];// For clients on + // global net (faster + // comparisons and + // hashing using 64 + // bit support) + UCHAR ICB_RemoteNode[6]; // Peer node for demand + // dial connections + }; + UCHAR ICB_LocalNode[6]; // Node we bound to + USHORT ICB_Flags; +#define FWD_IF_ENABLED 0x0001 +#define SET_IF_ENABLED(ifCB) ifCB->ICB_Flags |= FWD_IF_ENABLED; +#define SET_IF_DISABLED(ifCB) ifCB->ICB_Flags &= ~FWD_IF_ENABLED; +#define IS_IF_ENABLED(ifCB) (ifCB->ICB_Flags&FWD_IF_ENABLED) + +#define FWD_IF_CONNECTING 0x0002 +#define SET_IF_CONNECTING(ifCB) ifCB->ICB_Flags |= FWD_IF_CONNECTING; +#define SET_IF_NOT_CONNECTING(ifCB) ifCB->ICB_Flags &= ~FWD_IF_CONNECTING; +#define IS_IF_CONNECTING(ifCB) (ifCB->ICB_Flags&FWD_IF_CONNECTING) + USHORT ICB_NicId; // Nic id we bound to + UCHAR ICB_InterfaceType; + UCHAR ICB_NetbiosDeliver; + BOOLEAN ICB_NetbiosAccept; + + PNB_ROUTE ICB_NBRoutes; // Array of associated + // NB routes + ULONG ICB_NBRouteCount; // Number of nb routes + + LONGLONG ICB_DisconnectTime; // Time when if was disconnected + FWD_IF_STATS ICB_Stats; // Accumulated + PFWD_ROUTE ICB_CashedRoute; // MRU dest route + PINTERFACE_CB ICB_CashedInterface;// MRU dest if + NIC_HANDLE ICB_AdapterContext; // IPX stack supplied + PVOID ICB_FilterInContext; + PVOID ICB_FilterOutContext; + LONG ICB_PendingQuota; // Remaining quota of + // packets that can be + // pending on + // the interface + LIST_ENTRY ICB_ExternalQueue; // Queue of external (received) + // packets + LIST_ENTRY ICB_InternalQueue; // Queue of internal (send) + // requests +#if DBG + LIST_ENTRY ICB_InSendQueue; // packets being + // sent by ipx +#endif + INT ICB_PacketListId; // ID of the packet list + // (for the max frame size + // on this interface) + LIST_ENTRY ICB_IndexHashLink; // Link in interface idx hash + LIST_ENTRY ICB_ConnectionLink; // Link in connection queue + PNDIS_PACKET ICB_ConnectionPacket; // Packet that caused connection + // request + PUCHAR ICB_ConnectionData; // Pointer into packet to + // place where actual data + // (header) starts + PINTERFACE_CB ICB_NodeHashLink; // Link in client node hash + ULONG ICB_ReferenceCount; // Number of routes that + // point to this CB + KSPIN_LOCK ICB_Lock; // Protects state, + // queues +}; + +#define InitICB(ifCB,IfIndex,IfType,NbAccept,NbDeliver) { \ + (ifCB)->ICB_Index = IfIndex; \ + (ifCB)->ICB_Network = INVALID_NETWORK_NUMBER; \ + (ifCB)->ICB_Flags = 0; \ + (ifCB)->ICB_NicId = INVALID_NIC_ID; \ + (ifCB)->ICB_InterfaceType = IfType; \ + (ifCB)->ICB_NetbiosAccept = NbAccept; \ + (ifCB)->ICB_NetbiosDeliver = NbDeliver; \ + memset (&(ifCB)->ICB_Stats, 0, sizeof (FWD_IF_STATS));\ + KeInitializeSpinLock (&(ifCB)->ICB_Lock); \ + (ifCB)->ICB_CashedInterface = NULL; \ + (ifCB)->ICB_CashedRoute = NULL; \ + (ifCB)->ICB_ReferenceCount = 0; \ + (ifCB)->ICB_FilterInContext = NULL; \ + (ifCB)->ICB_FilterOutContext = NULL; \ + (ifCB)->ICB_ClientNode64[0] = 0; \ + (ifCB)->ICB_NBRoutes = NULL; \ + (ifCB)->ICB_PacketListId = -1; \ + InitializeListHead (&(ifCB)->ICB_InternalQueue); \ + InitializeListHead (&(ifCB)->ICB_ExternalQueue); \ + (ifCB)->ICB_PendingQuota = MaxSendPktsQueued; \ + switch ((ifCB)->ICB_InterfaceType) { \ + case FWD_IF_PERMANENT: \ + (ifCB)->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN;\ + break; \ + case FWD_IF_DEMAND_DIAL: \ + case FWD_IF_LOCAL_WORKSTATION: \ + case FWD_IF_REMOTE_WORKSTATION: \ + (ifCB)->ICB_Stats.OperationalState = FWD_OPER_STATE_SLEEPING;\ + KeQuerySystemTime ((PLARGE_INTEGER)&(ifCB)->ICB_DisconnectTime);\ + (ifCB)->ICB_DisconnectTime -= (LONGLONG)SpoofingTimeout*10000000;\ + break; \ + } \ +} + + +// Routes for netbios names (staticly seeded to reduce +// internet broadcast traffic) +struct _NB_ROUTE { + union { + ULONGLONG NBR_Name128[2]; + UCHAR NBR_Name[16]; // Netbios name of destination + }; + PINTERFACE_CB NBR_Destination; // Interface to send to + PNB_ROUTE NBR_Next; // Next route in the name list +}; + + +// List used to allocate packets destined to WAN interfaces +extern INT WanPacketListId; +// Max number of outstanding sends +extern ULONG MaxSendPktsQueued; + +// Segment sizes +extern ULONG RouteSegmentSize; +extern ULONG InterfaceSegmentSize; +extern ULONG NBNameSegementSize; + +// Sizes of hash tables +extern ULONG RouteHashSize; +extern ULONG InterfaceHashSize; +extern ULONG ClientHashSize; +extern ULONG NBRouteHashSize; + +// Number of global client network +extern ULONG GlobalNetwork; +// Interface reserved for internal network +extern PINTERFACE_CB InternalInterface; + +/*++ +******************************************************************* + C r e a t e T a b l e s + +Routine Description: + Allocates and intializes all hash tables and related structures +Arguments: + None +Return Value: + STATUS_SUCCESS - tables were created ok + STATUS_INSUFFICIENT_RESOURCES - resource allocation failed +******************************************************************* +--*/ +NTSTATUS +CreateTables ( + void + ); + +/*++ +******************************************************************* + D e l e t e T a b l e s + +Routine Description: + Releases resources allocated for all hash tables +Arguments: + None +Return Value: + STATUS_SUCCESS - tables were freed ok +******************************************************************* +--*/ +NTSTATUS +DeleteTables ( + void + ); + +/*++ +******************************************************************* + F r e e I n t e r f a c e + +Routine Description: + Releases memory allocated for interface to interface memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeInterface ( + PINTERFACE_CB ifCB + ); + +/*++ +******************************************************************* + F r e e R o u t e + +Routine Description: + Releases memory allocated for route to route memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeRoute ( + PFWD_ROUTE fwRoute + ); +/*++ +******************************************************************* + A c q u i r e I n t e r f a c e R e f e r e n c e + +Routine Description: + Increments refernce count of interface control block + ICB can't be freed until all references to it are released. + The caller of this routine should have already had a reference + to the interface or must hold an InterfaceLock +Arguments: + ifCB - interface control block to reference +Return Value: + None +******************************************************************* +--*/ +//VOID +//AcquireInterfaceReference ( +// PINTERFACE_CB ifCB +// ); +#if DBG +#define AcquireInterfaceReference(ifCB) \ + do { \ + ASSERTMSG ("Referenced ifCB is dead ", \ + InterlockedIncrement(&ifCB->ICB_ReferenceCount)>0); \ + } while (0) +#else +#define AcquireInterfaceReference(ifCB) \ + InterlockedIncrement(&ifCB->ICB_ReferenceCount) +#endif +/*++ +******************************************************************* + R e l e a s e I n t e r f a c e R e f e r e n c e + +Routine Description: + Decrements refernce count of interface control block +Arguments: + ifCB - interface control block to release +Return Value: + None +******************************************************************* +--*/ +//PINTERFACE_CB +//ReleaseInterfaceReference ( +// PINTERFACE_CB ifCB +// ); +// if it drops below 0, it has alredy been removed from the table +#define ReleaseInterfaceReference(ifCB) ( \ + (InterlockedDecrement (&ifCB->ICB_ReferenceCount)>=0) \ + ? ifCB \ + : (FreeInterface (ifCB), (ifCB = NULL)) \ +) + +/*++ +******************************************************************* + I n t e r f a c e C o n t e x t T o R e f e r e n c e + +Routine Description: + Verifies that context supplied by the IPX stack is a valid + interface block and is still bound to the adapter with which + it is associated in the IPX stack +Arguments: + ifCB - interface control block to reference + NicId - id of the adapter to which interface is bound +Return Value: + None +******************************************************************* +--*/ +//PINTERFACE_CB +//InterfaceContextToReference ( +// PVOID Context +// ); +#define InterfaceContextToReference(Context,NicId) ( \ + (InterlockedIncrement(&((PINTERFACE_CB)Context)->ICB_ReferenceCount)>0) \ + ? ((NicId==((PINTERFACE_CB)Context)->ICB_NicId) \ + ? (PINTERFACE_CB)Context \ + : (ReleaseInterfaceReference(((PINTERFACE_CB)Context)), NULL)) \ + : NULL \ + ) + +/*++ +******************************************************************* + G e t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference interface based on its index +Arguments: + InterfaceIndex - unique id of the interface +Return Value: + Pointer to interface control block if there is one in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetInterfaceReference ( + ULONG InterfaceIndex + ); + + +/*++ +******************************************************************* + G e t N e x t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference to the next interface in the table + Reference to the provided interface is released +Arguments: + ifCB - interface to start with or NULL to start from the + beginning of the interface table +Return Value: + Pointer to interface control block if thare are any more interfaces + in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetNextInterfaceReference ( + PINTERFACE_CB ifCB + ); + +/*++ +******************************************************************* + A d d I n t e r f a c e + +Routine Description: + Adds interface control block to the table. +Arguments: + InterfaceIndex - unique if of the interface + Info - interface paramters +Return Value: + STATUS_SUCCESS - interface added ok + STATUS_UNSUCCESSFULL - interface is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + interface CB +******************************************************************* +--*/ +NTSTATUS +AddInterface ( + ULONG InterfaceIndex, + UCHAR InterfaceType, + BOOLEAN NetbiosAccept, + UCHAR NetbiosDeliver + ); + +/*++ +******************************************************************* + A d d G l o b a l N e t C l i e n t + +Routine Description: + Adds interface control block to the table of + clients on the global network (should be done when + client connects) +Arguments: + ifCB - interface control block to add to the table +Return Value: + STATUS_SUCCESS - interface was added ok + STATUS_UNSUCCESSFULL - another interface with the same + node address is already in the table +******************************************************************* +--*/ +NTSTATUS +AddGlobalNetClient ( + PINTERFACE_CB ifCB + ); + +/*++ +******************************************************************* + D e l e t e G l o b a l N e t C l i e n t + +Routine Description: + Removes interface control block from the table of + clients on the global network (should be done when + client disconnects) +Arguments: + ifCB - interface control block to remove from the table +Return Value: + STATUS_SUCCESS - interface was removed ok +******************************************************************* +--*/ +NTSTATUS +DeleteGlobalNetClient ( + PINTERFACE_CB ifCB + ); + +/*++ +******************************************************************* + D e l e t e I n t e r f a c e + +Routine Description: + Deletes interface control block (the block is not actually + disposed of until all references to it are released). +Arguments: + InterfaceIndex - unique if of the interface +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFULL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteInterface ( + ULONG InterfaceIndex + ); + +/*++ +******************************************************************* + A d d R o u t e + +Routine Description: + Adds route to the hash table and finds and stores the reference + to the associated interface control block in the route. +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - route was added ok + STATUS_UNSUCCESSFUL - route is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route block +******************************************************************* +--*/ +NTSTATUS +AddRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ); + +/*++ +******************************************************************* + D e l e t e R o u t e + +Routine Description: + Deletes route from the hash table and releases the reference + to the interface control block associated with the route. +Arguments: + Network - route's destination network +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteRoute ( + ULONG Network + ); + +/*++ +******************************************************************* + U p d a t e R o u t e + +Routine Description: + Updates route in the hash table +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFUL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +UpdateRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ); + +/*++ +******************************************************************* + F i n d D e s t i n a t i o n + +Routine Description: + Finds destination interface for IPX address and + returns reference to its control block. +Arguments: + Network - destination network + Node - destination node (needed in case of global client) + Route - buffer to place route reference +Return Value: + Reference to destination interface CB + NULL if route it not found +******************************************************************* +--*/ +PINTERFACE_CB +FindDestination ( + IN ULONG Network, + IN PUCHAR Node, + OUT PFWD_ROUTE *Route + ); +/*++ +******************************************************************* + A c q u i r e R o u t e R e f e r e n c e + +Routine Description: + Increments refernce count of the route block + Route block can't be freed until all references to it are released. + The caller of this routine should have already had a reference + to the route or must hold an TableWriteLock +Arguments: + fwRoute - route block to reference +Return Value: + None +******************************************************************* +--*/ +//VOID +//AcquireRouteReference ( +// PFW+ROUTE fwRoute +// ); +#define AcquireRouteReference(fwRoute) \ + InterlockedIncrement(&fwRoute->FR_ReferenceCount) + + + +/*++ +******************************************************************* + R e l e a s e R o u t e R e f e r e n c e + +Routine Description: + Decrements refernce count of route block +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +//VOID +//ReleaseRouteReference ( +// PFW_ROUTE fwRoute +// ); +// if it drops below 0, it has alredy been removed from the table +#define ReleaseRouteReference(fwRoute) { \ + if (InterlockedDecrement (&fwRoute->FR_ReferenceCount)<0) { \ + FreeRoute (fwRoute); \ + fwRoute = NULL; \ + } \ +} + + +/*++ +******************************************************************* + A d d N B R o u t e s + +Routine Description: + Adds netbios names associated with interface to netbios + route hash table +Arguments: + ifCB - interface with which names are associated + Names - array of names + Count - number of names in the array + routeArray - buffer to place pointer to allocated array of routes +Return Value: + STATUS_SUCCESS - names were added ok + STATUS_UNSUCCESSFUL - one of the names is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route array +******************************************************************* +--*/ +NTSTATUS +AddNBRoutes ( + PINTERFACE_CB ifCB, + FWD_NB_NAME Names[], + ULONG Count, + PNB_ROUTE *routeArray + ); + +/*++ +******************************************************************* + D e l e t e N B R o u t e s + +Routine Description: + Deletes nb routes in the array from the route table and frees + the array +Arguments: + nbRoutes - array of routes + Count - number of routes in the array +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteNBRoutes ( + PNB_ROUTE nbRoutes, + ULONG Count + ); + +/*++ +******************************************************************* + F i n d N B D e s t i n a t i o n + +Routine Description: + Finds destination interface for nb name and + returns reference to its control block. +Arguments: + Name - name to look for +Return Value: + Reference to destination interface CB + NULL if route it not found +******************************************************************* +--*/ +PINTERFACE_CB +FindNBDestination ( + IN PUCHAR Name + ); +#endif diff --git a/private/ntos/tdi/isn/fwd/up/makefile b/private/ntos/tdi/isn/fwd/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/fwd/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/fwd/up/sources b/private/ntos/tdi/isn/fwd/up/sources new file mode 100644 index 000000000..229bd8e34 --- /dev/null +++ b/private/ntos/tdi/isn/fwd/up/sources @@ -0,0 +1,31 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +BINPLACE_FLAGS=$(BINPLACE_FLAGS) -d dump\up + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/inc/bind.h b/private/ntos/tdi/isn/inc/bind.h new file mode 100644 index 000000000..27a98d7c5 --- /dev/null +++ b/private/ntos/tdi/isn/inc/bind.h @@ -0,0 +1,843 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + bind.h + +Abstract: + + Private include file for the ISN transport. It defines the + structures used for binding between IPX and the upper drivers. + +Author: + + Adam Barr (adamba) 04-Oct-1993 + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#ifndef _ISN_BIND_ +#define _ISN_BIND_ + +// +// Retrieve the common definitions. +// + +#include + + +// +// Define the IOCTL used for binding between the upper +// drivers and IPX. +// + +#define _IPX_CONTROL_CODE(request,method) \ + CTL_CODE(FILE_DEVICE_TRANSPORT, request, method, FILE_ANY_ACCESS) + +#define IOCTL_IPX_INTERNAL_BIND _IPX_CONTROL_CODE( 0x1234, METHOD_BUFFERED ) + + +// +// [FW] Error codes - reusing NTSTATUS codes +// + +#define STATUS_ADAPTER_ALREADY_OPENED STATUS_UNSUCCESSFUL +#define STATUS_ADAPTER_ALREADY_CLOSED STATUS_UNSUCCESSFUL +#define STATUS_FILTER_FAILED STATUS_UNSUCCESSFUL +#define STATUS_DROP_SILENTLY STATUS_UNSUCCESSFUL + +// +// Identifier for the drivers in ISN. +// + +#define IDENTIFIER_NB 0 +#define IDENTIFIER_SPX 1 +#define IDENTIFIER_RIP 2 +#define IDENTIFIER_IPX 3 + +#ifdef _PNP_POWER +// +// This the number of PVOIDs in the beginning of the SEND_RESERVED +// section of a packet header, to be set aside by the ISN clients (NB/SPX) +// for IPX's private use. +// +#define SEND_RESERVED_COMMON_SIZE 8 +#endif + +// +// Definition of a RIP router table entry. +// + +typedef struct _IPX_ROUTE_ENTRY { + UCHAR Network[4]; + USHORT NicId; + UCHAR NextRouter[6]; + NDIS_HANDLE NdisBindingContext; + USHORT Flags; + USHORT Timer; + UINT Segment; + USHORT TickCount; + USHORT HopCount; + LIST_ENTRY AlternateRoute; + LIST_ENTRY NicLinkage; + struct { + LIST_ENTRY Linkage; + ULONG Reserved[1]; + } PRIVATE; +} IPX_ROUTE_ENTRY, * PIPX_ROUTE_ENTRY; + +// +// Definition of the Flags values. +// + +#define IPX_ROUTER_PERMANENT_ENTRY 0x0001 // entry should never be deleted +#define IPX_ROUTER_LOCAL_NET 0x0002 // locally attached network +#define IPX_ROUTER_SCHEDULE_ROUTE 0x0004 // call ScheduleRouteHandler after using +#define IPX_ROUTER_GLOBAL_WAN_NET 0x0008 // this is for rip's global network number + + +// +// Definition of the structure provided on a find +// route/find route completion call. +// + +// +// [SA] Bug #15094 added node number to the structure. +// + +// +// [FW] Added Hop and Tick counts so this structure can be passed +// as such to the Forwarder - hop and tick counts are queried in actions +// + +typedef struct _IPX_FIND_ROUTE_REQUEST { + UCHAR Network[4]; + UCHAR Node[6] ; + IPX_LOCAL_TARGET LocalTarget; + USHORT TickCount; // [FW] + USHORT HopCount; // [FW] + UCHAR Identifier; + UCHAR Type; + UCHAR Reserved1[2]; + PVOID Reserved2; + LIST_ENTRY Linkage; +} IPX_FIND_ROUTE_REQUEST, *PIPX_FIND_ROUTE_REQUEST; + +// +// Definitions for the Type value. +// + +#define IPX_FIND_ROUTE_NO_RIP 1 // fail if net is not in database +#define IPX_FIND_ROUTE_RIP_IF_NEEDED 2 // return net if in database, otherwise RIP out +#define IPX_FIND_ROUTE_FORCE_RIP 3 // re-RIP even if net is in database + + +// +// Structure used when querying the line information +// for a specific NID ID. +// + +typedef struct _IPX_LINE_INFO { + UINT LinkSpeed; + UINT MaximumPacketSize; + UINT MaximumSendSize; + UINT MacOptions; +} IPX_LINE_INFO, *PIPX_LINE_INFO; + + + +// +// Functions provided by the upper driver. +// + +// +// [FW] Added the ForwarderAdapterContext to the paramters +// SPX/NB can ignore this for now +// + +typedef BOOLEAN +(*IPX_INTERNAL_RECEIVE) ( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterContext, // [FW] + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl +); + +typedef VOID +(*IPX_INTERNAL_RECEIVE_COMPLETE) ( + IN USHORT NicId +); + +// +// [FW] Status and ScheduleRoute removed from the bind input +// [ZZZZZZZZZ] + +typedef VOID +(*IPX_INTERNAL_STATUS) ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength +); + +typedef VOID +(*IPX_INTERNAL_SCHEDULE_ROUTE) ( + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef VOID +(*IPX_INTERNAL_SEND_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE_COMPLETE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute +); + +typedef VOID +(*IPX_INTERNAL_LINE_UP) ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData +); + +typedef VOID +(*IPX_INTERNAL_LINE_DOWN) ( + IN USHORT NicId, + IN ULONG FwdAdapterContext +); + +#if defined(_PNP_POWER) + +// +// following opcodes are used when calling the +// above handler. +// +typedef enum _IPX_PNP_OPCODE { + IPX_PNP_ADD_DEVICE, // 0 - addition of the first adapter + IPX_PNP_DELETE_DEVICE, // 1 - deletion of the last adapter + IPX_PNP_TRANSLATE_DEVICE, // 2 - translate device resource + IPX_PNP_TRANSLATE_ADDRESS, // 3 - translate address resource + IPX_PNP_ADDRESS_CHANGE, // 4 - Adapter address or Reserved address changed + IPX_PNP_MAX_OPCODES, // 5 +} IPX_PNP_OPCODE, *PIPX_PNP_OPCODE; + +// +// PnP event notification handler. +// +typedef VOID +(*IPX_INTERNAL_PNP_NOTIFICATION) ( + IN IPX_PNP_OPCODE PnPOpcode, + IN OUT PVOID PnpData +); + +// +// Pointer to this structure is passed in PnPData portion of +// the above handler when the opcode is ADD_DEVICE or DELETE_DEVICE. +// +typedef struct _IPX_PNP_INFO { + ULONG NetworkAddress; + UCHAR NodeAddress[6]; + BOOLEAN NewReservedAddress; // where the above is a new reserved + // address for the Ipx clients. + BOOLEAN FirstORLastDevice; // is this a first card arrival or last card deletion. + IPX_LINE_INFO LineInfo; // New LineInfo. + NIC_HANDLE NicHandle; +} IPX_PNP_INFO, *PIPX_PNP_INFO; + +#endif _PNP_POWER + +// +// [FW] New entry points provided by the Kernel Forwarder. +// These are not filled in by NB and SPX. +// + +/*++ + +Routine Description: + + This routine is provided by the Kernel Forwarder to filter packets being sent out + by NB/SPX/TDI thru' IPX - does not include those sent out by the Forwarder (external sends) + +Arguments: + + LocalTarget - the NicId and next hop router MAC address + + FwdAdapterContext - Forwarder's context - preferred NIC if not INVALID_CONTEXT_VALUE + + Packet - packet to be sent out + + IpxHeader - points to the IPX header + + Data - points to after the IPX header - needed in spoofing of keepalives. + + PacketLength - length of the packet + + fIterate - a flag to indicate if this is a packet for the iteration of which the + Fwd takes responsibility - typically type 20 NetBIOS frames + + +Return Value: + + STATUS_SUCCESS - if the preferred NIC was OK and packet passed filtering + STATUS_NETWORK_UNREACHABLE - if the preferred was not OK or packet failed filtering + STATUS_PENDING - if preferred NIC was OK but line down + + BUGBUG: Forwarder should give us a different status than STATUS_NETWORK_UNREACHABLE for changed NIC +--*/ +typedef NTSTATUS +(*IPX_FW_INTERNAL_SEND) ( + IN OUT PIPX_LOCAL_TARGET LocalTarget, + IN ULONG FwdAdapterContext, + IN PNDIS_PACKET Packet, + IN PUCHAR IpxHeader, + IN PUCHAR Data, + IN ULONG PacketLength, + IN BOOLEAN fIterate +); + +/*++ + +Routine Description: + + This routine is provided by the Kernel Forwarder to find the route to a given node and network + +Arguments: + + Network - the destination network + + Node - destination node + + RouteEntry - filled in by the Forwarder if a route exists + +Return Value: + + STATUS_SUCCESS + STATUS_NETWORK_UNREACHABLE - if the findroute failed + +--*/ +typedef NTSTATUS +(*IPX_FW_FIND_ROUTE) ( + IN PUCHAR Network, + IN PUCHAR Node, + OUT PIPX_FIND_ROUTE_REQUEST RouteEntry +); + +/*++ + +Routine Description: + + This routine is provided by the Kernel Forwarder to find the route to a given node and network + +Arguments: + + FwdAdapterContext - Forwarder's context + + RemoteAddress - the address the packet came on + + LookAheadBuffer - packet header that came in + + LookAheadBufferSize - size of the lookaheadbuffer + +Return Value: + + STATUS_SUCCESS + STATUS_FILTER_FAILED - if the packet was not allowed by the filter + +--*/ +typedef NTSTATUS +(*IPX_FW_INTERNAL_RECEIVE) ( + IN ULONG FwdAdapterContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PUCHAR LookAheadBuffer, + IN UINT LookAheadBufferSize +); + +// +// Input to the bind IOCTL +// + +// +// [FW] Removed the status and schedule route handlers +// +typedef struct _IPX_INTERNAL_BIND_INPUT { + USHORT Version; + UCHAR Identifier; + BOOLEAN BroadcastEnable; + UINT LookaheadRequired; + UINT ProtocolOptions; + IPX_INTERNAL_RECEIVE ReceiveHandler; + IPX_INTERNAL_RECEIVE_COMPLETE ReceiveCompleteHandler; + IPX_INTERNAL_STATUS StatusHandler; + IPX_INTERNAL_SEND_COMPLETE SendCompleteHandler; + IPX_INTERNAL_TRANSFER_DATA_COMPLETE TransferDataCompleteHandler; + IPX_INTERNAL_FIND_ROUTE_COMPLETE FindRouteCompleteHandler; + IPX_INTERNAL_LINE_UP LineUpHandler; + IPX_INTERNAL_LINE_DOWN LineDownHandler; + IPX_INTERNAL_SCHEDULE_ROUTE ScheduleRouteHandler; +#if defined(_PNP_POWER) + IPX_INTERNAL_PNP_NOTIFICATION PnPHandler; +#endif _PNP_POWER + IPX_FW_INTERNAL_SEND InternalSendHandler; + IPX_FW_FIND_ROUTE FindRouteHandler; + IPX_FW_INTERNAL_RECEIVE InternalReceiveHandler; + ULONG RipParameters; +} IPX_INTERNAL_BIND_INPUT, * PIPX_INTERNAL_BIND_INPUT; + +#if defined(_PNP_POWER) +#define ISN_VERSION 2 +#endif _PNP_POWER + + +// +// Bit mask values for RipParameters. +// + +#define IPX_RIP_PARAM_GLOBAL_NETWORK 0x00000001 // single network for all WANS + + + +// +// Functions provided by the lower driver. +// + +typedef NDIS_STATUS +(*IPX_INTERNAL_SEND) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest +); + +typedef NTSTATUS +(*IPX_INTERNAL_QUERY) ( + IN ULONG InternalQueryType, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif _PNP_POWER + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA)( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +// +// Definitions of the internal query types. In all cases +// STATUS_SUCCESS is returned if the request succeeds, and +// STATUS_BUFFER_TOO_SMALL is returned, and BufferLengthNeeded +// set if specified, if the buffer is too short. Other +// return codes are defined below. The routine never pends. +// + +// +// This is used to query the line info. NicId specifies which one +// to query. Buffer contains an IPX_LINE_INFO structure which is +// used to return the information. Other return values: +// +// STATUS_INVALID_PARAMETER - NicId is invalid. +// + +#define IPX_QUERY_LINE_INFO 1 + +// +// This is used to query the maximum NicId. NicId is unused. The +// Buffer contains a USHORT which is used to return the information. +// + +#define IPX_QUERY_MAXIMUM_NIC_ID 2 + +// +// This is used to determine if the IPX address specified was sent +// by our local machine. If the address is the source address of a +// received frame, NicId should be the ID that was indicated; otherwise +// it should be set to 0. Buffer holds a TDI_ADDRESS_IPX. This +// call returns STATUS_SUCCESS if the address is local, and +// STATUS_NO_SUCH_DEVICE if not. +// + +#define IPX_QUERY_IS_ADDRESS_LOCAL 3 + +// +// This is used to query the receive buffer space of a given NicId. +// Buffer contains a ULONG which is used to return the information. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_RECEIVE_BUFFER_SPACE 4 + +// +// This is used to query the local IPX address of a given NicId. +// Buffer contains a TDI_ADDRESS_IPX structure (the Socket is +// returned as 0). If it is queried on net 0 it returns the +// virtual network if there is one, otherwise STATUS_INVALID_PARAMETER. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_IPX_ADDRESS 5 + +// +// This is used to return the source routing information for +// a give remote address. NicId will be the NIC the packet was +// received from. The IPX_SOURCE_ROUTING_QUERY is contained +// in Buffer. Always returns STATUS_SUCCESS, although the +// SourceRoutingLength may be 0 for unknown remotes. +// +// The source routing is return in the direction it was received +// from the remote, not the direction used in replying. The +// MaximumSendSize includes the IPX header (as it does in +// IPX_LINE_INFO). +// + +#define IPX_QUERY_SOURCE_ROUTING 6 + +typedef struct _IPX_SOURCE_ROUTING_INFO { + USHORT Identifier; // input: the caller's IDENTIFIER_SPX, _NB, etc. + UCHAR RemoteAddress[6]; // input: the remote address + UCHAR SourceRouting[18]; // output: room for the maximum source route + USHORT SourceRoutingLength; // output: the valid length of source route + ULONG MaximumSendSize; // output: based on nic and source routing +} IPX_SOURCE_ROUTING_INFO, * PIPX_SOURCE_ROUTING_INFO; + +// +// This is used to query the maximum NicId over which outgoing type +// 20 packets should be sent. It will be less than or equal to +// the IPX_QUERY_MAXIMUM_NIC_ID value. What's excluded are down wan +// lines and dialin wan lines if DisableDialinNetbios bit 1 is set. +// + +#define IPX_QUERY_MAX_TYPE_20_NIC_ID 7 + +#if defined(_PNP_POWER) + +// +// This are used by NB to pass down these TDI queries which cannot +// be completed in NB. +// + +#define IPX_QUERY_DATA_LINK_ADDRESS 8 +#define IPX_QUERY_NETWORK_ADDRESS 9 + +#endif _PNP_POWER + +// +// Output of a non-RIP bind. +// + +typedef struct _IPX_INTERNAL_BIND_OUTPUT { + USHORT Version; + UCHAR Node[6]; + UCHAR Network[4]; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_LINE_INFO LineInfo; + IPX_INTERNAL_SEND SendHandler; + IPX_INTERNAL_FIND_ROUTE FindRouteHandler; + IPX_INTERNAL_QUERY QueryHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; +} IPX_INTERNAL_BIND_OUTPUT, * PIPX_INTERNAL_BIND_OUTPUT; + + + +// +// Lower driver functions provided only for RIP. +// + +typedef UINT +(*IPX_INTERNAL_GET_SEGMENT) ( + IN UCHAR Network[4] +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_ROUTE) ( + IN UINT Segment, + IN UCHAR Network[4] +); + +typedef BOOLEAN +(*IPX_INTERNAL_ADD_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef BOOLEAN +(*IPX_INTERNAL_DELETE_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_FIRST_ROUTE) ( + IN UINT Segment +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_NEXT_ROUTE) ( + IN UINT Segment +); + +typedef VOID +(*IPX_INTERNAL_INCREMENT_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +typedef ULONG +(*IPX_INTERNAL_QUERY_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif + +); + +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to open an adapter + +Arguments: + + AdapterIndex - index of the adapter to open (NICid for now - will change to a struct + with a version number, signature and the NicId + FwdAdapterContext - Forwarder's context + IpxAdapterContext - our context (for now we use the NICid - for pnp will change + this to contain a signature and version #) + +Return Value: + + STATUS_INVALID_HANDLE if the AdapterIndex handle was invalid + STATUS_ADAPTER_ALREADY_OPENED if the Adapter is being opened a second time + STATUS_SUCCESS + +--*/ +typedef NTSTATUS +(*IPX_FW_OPEN_ADAPTER) ( + IN NIC_HANDLE AdapterIndex, + IN ULONG FwdAdapterContext, + OUT PNIC_HANDLE IpxAdapterContext +); + +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to close an adapter + +Arguments: + + IpxAdapterContext - our context (for now we use the NICid - for pnp will change + this to contain a signature and version#) + +Return Value: + + STATUS_ADAPTER_ALREADY_CLOSED - if the adapter is being closed a second time + STATUS_SUCCESS + +--*/ +typedef NTSTATUS +(*IPX_FW_CLOSE_ADAPTER) ( + IN NIC_HANDLE IpxAdapterContext +); + +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to indicate that a pending + internal send to it has completed. + +Arguments: + + LocalTarget - if Status is OK, this has the local target for the send. + + Packet - A pointer to the NDIS_PACKET that we sent. + + PacketLength - length of the packet (including the IPX header) + + Status - the completion status of the send - STATUS_SUCCESS or STATUS_NETWORK_UNREACHABLE + +Return Value: + + none. + +--*/ +typedef VOID +(*IPX_FW_INTERNAL_SEND_COMPLETE) ( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN NTSTATUS Status +); + +// +// Describes a single network. +// + +typedef struct _IPX_NIC_DATA { + USHORT NicId; + UCHAR Node[6]; + UCHAR Network[4]; + IPX_LINE_INFO LineInfo; + NDIS_MEDIUM DeviceType; + ULONG EnableWanRouter; +} IPX_NIC_DATA, * PIPX_NIC_DATA; + + +// +// Describes all networks. +// + +typedef struct _IPX_NIC_INFO_BUFFER { + USHORT NicCount; + USHORT VirtualNicId; + UCHAR VirtualNetwork[4]; + IPX_NIC_DATA NicData[1]; +} IPX_NIC_INFO_BUFFER, * PIPX_NIC_INFO_BUFFER; + + +// +// Output from a RIP bind (the actual structure size is +// based on the number of IPX_NIC_DATA elements in the +// final IPX_NIC_INFO_BUFFER structure). +// + +typedef struct _IPX_INTERNAL_BIND_RIP_OUTPUT { + USHORT Version; + USHORT MaximumNicCount; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_INTERNAL_SEND SendHandler; + UINT SegmentCount; + KSPIN_LOCK * SegmentLocks; + IPX_INTERNAL_GET_SEGMENT GetSegmentHandler; + IPX_INTERNAL_GET_ROUTE GetRouteHandler; + IPX_INTERNAL_ADD_ROUTE AddRouteHandler; + IPX_INTERNAL_DELETE_ROUTE DeleteRouteHandler; + IPX_INTERNAL_GET_FIRST_ROUTE GetFirstRouteHandler; + IPX_INTERNAL_GET_NEXT_ROUTE GetNextRouteHandler; + IPX_INTERNAL_INCREMENT_WAN_INACTIVITY IncrementWanInactivityHandler; + IPX_INTERNAL_QUERY_WAN_INACTIVITY QueryWanInactivityHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; + IPX_FW_OPEN_ADAPTER OpenAdapterHandler; + IPX_FW_CLOSE_ADAPTER CloseAdapterHandler; + IPX_FW_INTERNAL_SEND_COMPLETE InternalSendCompleteHandler; + IPX_NIC_INFO_BUFFER NicInfoBuffer; +} IPX_INTERNAL_BIND_RIP_OUTPUT, * PIPX_INTERNAL_BIND_RIP_OUTPUT; + +// +// [FW] Used by the forwarder to fill up the localtarget +// + +#ifdef _PNP_LATER +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = _nic; \ + _nichandle.Signature = IPX_BINDING_SIGNATURE; \ + if (_nic == 0) { \ + _nichandle.Version = 0; \ + } else { \ + _nichandle.Version = IpxDevice->Bindings[_nic].Version; \ + } + +#else + +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = (USHORT)_nic; + +#endif + +// +// VOID +// ADAPTER_CONTEXT_TO_LOCAL_TARGET( +// IN NIC_HANDLE _context; +// IN PIPX_LOCAL_TARGET _localtarget; +// ); +// +#define ADAPTER_CONTEXT_TO_LOCAL_TARGET(_context, _localtarget) \ + (_localtarget)->NicHandle.NicId = (_context).NicId; + +// +// VOID +// CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET( +// IN NIC_HANDLE _context; +// IN PIPX_LOCAL_TARGET _localtarget; +// ); +// +#define CONSTANT_ADAPTER_CONTEXT_TO_LOCAL_TARGET(_context, _localtarget) \ + (_localtarget)->NicHandle.NicId = (USHORT)(_context); + + +// +// [FW] Used to indicate to the Forwarder that a preferred NIC is not given +// in InternalSend +// +#define INVALID_CONTEXT_VALUE 0xffffffff + +// +// [FW] This is the value returned (in FindRoute) to IPX from the Forwarder in case of a demand dial Nic. +// On an InternalSend, this is passed up to the FWD, which brings up the line and returns the good LocalTarget +// +#define DEMAND_DIAL_ADAPTER_CONTEXT 0xffffffff + +// +// Adapter context used by the FWD to represent a send to the virtual net. +// IPX maps this to the loopback adapter. +// +#define VIRTUAL_NET_ADAPTER_CONTEXT 0xfffffffe // -2 + +// +// Context passed up to the FWD on a loopback send. +// +#define VIRTUAL_NET_FORWARDER_CONTEXT 0xfffffffe // -2 + +// +// Special NIC id used by NB/SPX to send packets over all NICs. +// +#define ITERATIVE_NIC_ID 0xfffd // -3 + +#endif // _ISN_BIND_ + diff --git a/private/ntos/tdi/isn/inc/ioctls.h b/private/ntos/tdi/isn/inc/ioctls.h new file mode 100644 index 000000000..e7dd7b81a --- /dev/null +++ b/private/ntos/tdi/isn/inc/ioctls.h @@ -0,0 +1,155 @@ +#define VER_IOCH "@(#)MCS ipx/h/ioctls.h 1.00.00 - 08 APR 1993"; + +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Driver for Windows NT +* +* Module: ipx/h/ioctls.h +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* IOCTL defines +* +****************************************************************************/ + +/** Ioctls for IPX - (X) = User callable **/ + +/** + ioctls will values 100 - 150 were added for the NT port. +**/ + +#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8)) +#define MIPX_SETNODEADDR I_MIPX | 0 /* Set the node address */ +#define MIPX_SETNETNUM I_MIPX | 1 /* Set the network number */ +#define MIPX_SETPTYPE I_MIPX | 2 /* (X) Set the packet type */ +#define MIPX_SENTTYPE I_MIPX | 3 /* (X) Set the xport type */ +#define MIPX_SETPKTSIZE I_MIPX | 4 /* Set the packet size */ +#define MIPX_SETSAP I_MIPX | 5 /* Set the sap/type field */ +#define MIPX_SENDOPTS I_MIPX | 6 /* (X) Send options on recv */ +#define MIPX_NOSENDOPTS I_MIPX | 7 /* (X) Don't send options on recv */ +#define MIPX_SENDSRC I_MIPX | 8 /* (X) Send source address up */ +#define MIPX_NOSENDSRC I_MIPX | 9 /* (X) Don't Send source address up */ +#define MIPX_CONVBCAST I_MIPX | 10 /* Convert TKR bcast to func addr */ +#define MIPX_NOCONVBCAST I_MIPX | 11 /* Don't cnvrt TKR bcast to funcaddr */ +#define MIPX_SETCARDTYPE I_MIPX | 12 /* Set 802.3 or ETH type */ +#define MIPX_STARGROUP I_MIPX | 13 /* This is stargroup */ +#define MIPX_SWAPLENGTH I_MIPX | 14 /* Set flag for swapping 802.3 length */ +#define MIPX_SENDDEST I_MIPX | 15 /* (X) Send dest. address up */ +#define MIPX_NOSENDDEST I_MIPX | 16 /* (X) Don't send dest. address up */ +#define MIPX_SENDFDEST I_MIPX | 17 /* (X) Send final dest. address up */ +#define MIPX_NOSENDFDEST I_MIPX | 18 /* (X) Don't send final dest. up */ + +/** Added for NT port **/ + +#define MIPX_SETVERSION I_MIPX | 100 /* Set card version */ +#define MIPX_GETSTATUS I_MIPX | 101 +#define MIPX_SENDADDROPT I_MIPX | 102 /* (X) Send ptype w/addr on recv */ +#define MIPX_NOSENDADDROPT I_MIPX | 103 /* (X) Stop sending ptype on recv */ +#define MIPX_CHECKSUM I_MIPX | 104 /* Enable/Disable checksum */ +#define MIPX_GETPKTSIZE I_MIPX | 105 /* Get max packet size */ +#define MIPX_SENDHEADER I_MIPX | 106 /* Send header with data */ +#define MIPX_NOSENDHEADER I_MIPX | 107 /* Don't send header with data */ +#define MIPX_SETCURCARD I_MIPX | 108 /* Set current card for IOCTLs */ +#define MIPX_SETMACTYPE I_MIPX | 109 /* Set the Cards MAC type */ +#define MIPX_DOSROUTE I_MIPX | 110 /* Do source routing on this card*/ +#define MIPX_NOSROUTE I_MIPX | 111 /* Don't source routine the card*/ +#define MIPX_SETRIPRETRY I_MIPX | 112 /* Set RIP retry count */ +#define MIPX_SETRIPTO I_MIPX | 113 /* Set RIP timeout */ +#define MIPX_SETTKRSAP I_MIPX | 114 /* Set the token ring SAP */ +#define MIPX_SETUSELLC I_MIPX | 115 /* Put LLC hdr on packets */ +#define MIPX_SETUSESNAP I_MIPX | 116 /* Put SNAP hdr on packets */ +#define MIPX_8023LEN I_MIPX | 117 /* 1=make even, 0=dont make even*/ +#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/ +#define MIPX_NOSENDPTYPE I_MIPX | 119 /* Don't send ptype in options */ +#define MIPX_FILTERPTYPE I_MIPX | 120 /* Filter on recv ptype */ +#define MIPX_NOFILTERPTYPE I_MIPX | 121 /* Don't Filter on recv ptype */ +#define MIPX_SETSENDPTYPE I_MIPX | 122 /* Set pkt type to send with */ +#define MIPX_GETCARDINFO I_MIPX | 123 /* Get info on a card */ +#define MIPX_SENDCARDNUM I_MIPX | 124 /* Send card num up in options */ +#define MIPX_NOSENDCARDNUM I_MIPX | 125 /* Dont send card num in options*/ +#define MIPX_SETROUTER I_MIPX | 126 /* Set router enabled flag */ +#define MIPX_SETRIPAGE I_MIPX | 127 /* Set RIP age timeout */ +#define MIPX_SETRIPUSAGE I_MIPX | 128 /* Set RIP usage timeout */ +#define MIPX_SETSROUTEUSAGE I_MIPX| 129 /* Set the SROUTE usage timeout */ +#define MIPX_SETINTNET I_MIPX | 130 /* Set internal network number */ +#define MIPX_NOVIRTADDR I_MIPX | 131 /* Turn off virtual net num */ +#define MIPX_VIRTADDR I_MIPX | 132 /* Turn on virtual net num */ +#define MIPX_SETBCASTFLAG I_MIPX | 133 /* Turn on bcast flag in addr */ +#define MIPX_NOBCASTFLAG I_MIPX | 134 /* Turn off bcast flag in addr */ +#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */ +#define MIPX_SETDELAYTIME I_MIPX | 136 /* Set cards delay time */ +#define MIPX_SETROUTEADV I_MIPX | 137 /* Route advertise timeout */ +#define MIPX_SETSOCKETS I_MIPX | 138 /* Set default sockets */ +#define MIPX_SETLINKSPEED I_MIPX | 139 /* Set the link speed for a card*/ +#define MIPX_SETWANFLAG I_MIPX | 140 +#define MIPX_GETCARDCHANGES I_MIPX | 141 /* Wait for card changes */ +#define MIPX_GETMAXADAPTERS I_MIPX | 142 +#define MIPX_REUSEADDRESS I_MIPX | 143 +#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */ + +/** For Source Routing Support **/ + +#define MIPX_SRCLEAR I_MIPX | 200 /* Clear the source routing table*/ +#define MIPX_SRDEF I_MIPX | 201 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRBCAST I_MIPX | 202 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRMULTI I_MIPX | 203 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRREMOVE I_MIPX | 204 /* Remove a node from the table */ +#define MIPX_SRLIST I_MIPX | 205 /* Get the source routing table */ +#define MIPX_SRGETPARMS I_MIPX | 206 /* Get source routing parms */ + +#define MIPX_SETSHOULDPUT I_MIPX | 210 /* Turn on should put call */ +#define MIPX_DELSHOULDPUT I_MIPX | 211 /* Turn off should put call */ +#define MIPX_GETSHOULDPUT I_MIPX | 212 /* Get ptr to mipx_shouldput */ + +/** Added for ISN **/ + +#define MIPX_RCVBCAST I_MIPX | 300 /* (X) Enable broadcast reception */ +#define MIPX_NORCVBCAST I_MIPX | 301 /* (X) Disable broadcast reception */ +#define MIPX_ADAPTERNUM I_MIPX | 302 /* Get maximum adapter number */ +#define MIPX_NOTIFYCARDINFO I_MIPX | 303 /* Pend until card info changes */ +#define MIPX_LOCALTARGET I_MIPX | 304 /* Get local target for address */ +#define MIPX_NETWORKINFO I_MIPX | 305 /* Return info about remote net */ +#define MIPX_ZEROSOCKET I_MIPX | 306 /* Use 0 as source socket on sends */ + +/** Ioctls for SPX **/ + +#define I_MSPX (('S' << 24) | ('P' << 16) | ('P' << 8)) +#define MSPX_SETADDR I_MSPX | 0 /* Set the network address */ +#define MSPX_SETPKTSIZE I_MSPX | 1 /* Set the packet size per card */ +#define MSPX_SETDATASTREAM I_MSPX | 2 /* Set datastream type */ + +/** Added for NT port **/ + +#define MSPX_SETASLISTEN I_MSPX | 100 /* Set as a listen socket */ +#define MSPX_GETSTATUS I_MSPX | 101 /* Get running status */ +#define MSPX_GETQUEUEPTR I_MSPX | 102 /* Get ptr to the streams queue */ +#define MSPX_SETDATAACK I_MSPX | 103 /* Set DATA ACK option */ +#define MSPX_NODATAACK I_MSPX | 104 /* Turn off DATA ACK option */ +#define MSPX_SETMAXPKTSOCK I_MSPX | 105 /* Set the packet size per socket */ +#define MSPX_SETWINDOWCARD I_MSPX | 106 /* Set window size for card */ +#define MSPX_SETWINDOWSOCK I_MSPX | 107 /* Set window size for 1 socket */ +#define MSPX_SENDHEADER I_MSPX | 108 /* Send header with data */ +#define MSPX_NOSENDHEADER I_MSPX | 109 /* Don't send header with data */ +#define MSPX_GETPKTSIZE I_MSPX | 110 /* Get the packet size per card */ +#define MSPX_SETCONNCNT I_MSPX | 111 /* Set the conn req count */ +#define MSPX_SETCONNTO I_MSPX | 112 /* Set the conn req timeout */ +#define MSPX_SETALIVECNT I_MSPX | 113 /* Set the keepalive count */ +#define MSPX_SETALIVETO I_MSPX | 114 /* Set the keepalive timeout */ +#define MSPX_SETALWAYSEOM I_MSPX | 115 /* Turn on always EOM flag */ +#define MSPX_NOALWAYSEOM I_MSPX | 116 /* Turn off always EOM flag */ diff --git a/private/ntos/tdi/isn/inc/ipxfltif.h b/private/ntos/tdi/isn/inc/ipxfltif.h new file mode 100644 index 000000000..e02591e61 --- /dev/null +++ b/private/ntos/tdi/isn/inc/ipxfltif.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + private\inc\ipxfltif.h + +Abstract: + IPX Filter driver interface with forwarder + + +Author: + + Vadim Eydelman + +Revision History: + +--*/ + +#ifndef _IPXFLTIF_ +#define _IPXFLTIF_ + + // No filter context means that packets should not + // be passed for filtering +#define NO_FILTER_CONTEXT ((PVOID)0) + + + // Forwarder Driver Entry Points: + // ============================== +/*++ + S E T _ I F _ I N _ C O N T E X T _ H A N D L E R + +Routine Description: + Associates filter driver context with + the packets received on the interface +Arguments: + InterfaceIndex - index of the interface + ifInContext - filter driver context +Return Value: + STATUS_SUCCESS - context associated ok + STATUS_UNSUCCESSFUL - interface does not exist +--*/ +typedef +NTSTATUS +(*PSET_IF_IN_CONTEXT_HANDLER) ( + IN ULONG InterfaceIndex, + IN PVOID ifInContext + ); + +/*++ + S E T _ I F _ O U T _ C O N T E X T _ H A N D L E R + +Routine Description: + Associates filter driver context with + the packets sent on the interface +Arguments: + InterfaceIndex - index of the interface + ifOutContext - filter driver context +Return Value: + STATUS_SUCCESS - context associated ok + STATUS_UNSUCCESSFUL - interface does not exist +--*/ +typedef +NTSTATUS +(*PSET_IF_OUT_CONTEXT_HANDLER) ( + IN ULONG InterfaceIndex, + IN PVOID ifOutContext + ); + +typedef enum { + FILTER_DENY_IN = -2, + FILTER_DENY_OUT = -1, + FILTER_DENY = 1, + FILTER_PERMIT = 0 +} FILTER_ACTION; +#define NOT_FILTER_ACTION(action) (!action) +#define IS_FILTERED(action) (action!=FILTER_PERMIT) + + + // Forwarder Driver Entry Points: + // ============================== +/*++ + F i l t e r H a n d l e r + +Routine Description: + + Filters the packet supplied by the forwarder + +Arguments: + ipxHdr - pointer to packet header + ipxHdrLength - size of the header buffer (must be at least 30) + ifInContext - context associated with interface on which packet + was received + ifOutContext - context associated with interface on which packet + will be sent +Return Value: + FILTER_PERMIT - packet should be passed on by the forwarder + FILTER_DENY_IN - packet should be dropped because of input filter + FILTER_DENY_OUT - packet should be dropped because of output filter + +--*/ +typedef +FILTER_ACTION +(*PFILTER_HANDLER) ( + IN PUCHAR ipxHdr, + IN ULONG ipxHdrLength, + IN PVOID ifInContext, + IN PVOID ifOutContex + ); + +/*++ + I n t e r f a c e D e l e t e d H a n d l e r + +Routine Description: + + Frees interface filters blocks when forwarder indicates that + interface is deleted +Arguments: + ifInContext - context associated with input filters block + ifOutContext - context associated with output filters block +Return Value: + None + +--*/ +typedef +VOID +(*PINTERFACE_DELETED_HANDLER) ( + IN PVOID ifInContext, + IN PVOID ifOutContext + ); + +// Binds filter driver to forwarder +// IPX_FLT_BIND_INPUT should be passed in the input buffer and +// IPX_FLT_BINF_OUTPUT will be returned in the output buffer +#define IOCTL_FWD_INTERNAL_BIND_FILTER \ + CTL_CODE(FILE_DEVICE_IPXFWD,IPXFWD_IOCTL_INDEX+16,METHOD_BUFFERED,FILE_ANY_ACCESS) + +typedef struct _IPX_FLT_BIND_INPUT { + PFILTER_HANDLER FilterHandler; + PINTERFACE_DELETED_HANDLER InterfaceDeletedHandler; +} IPX_FLT_BIND_INPUT, *PIPX_FLT_BIND_INPUT; + +typedef struct _IPX_FLT_BIND_OUTPUT { + ULONG Size; + PSET_IF_IN_CONTEXT_HANDLER SetIfInContextHandler; + PSET_IF_OUT_CONTEXT_HANDLER SetIfOutContextHandler; +} IPX_FLT_BIND_OUTPUT, *PIPX_FLT_BIND_OUTPUT; + +#endif + diff --git a/private/ntos/tdi/isn/inc/isn.h b/private/ntos/tdi/isn/inc/isn.h new file mode 100644 index 000000000..7b7e23601 --- /dev/null +++ b/private/ntos/tdi/isn/inc/isn.h @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + isn.h + +Abstract: + + Private include file for the ISN transport. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include +#include +#include +#include +#include + diff --git a/private/ntos/tdi/isn/ipx/action.c b/private/ntos/tdi/isn/ipx/action.c new file mode 100644 index 000000000..0a57bad98 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/action.c @@ -0,0 +1,2921 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAction + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include + +// +// Line ups when indicated up should have this length subtracted from the +// max. send size that ndis indicated to us for the line +// +#define HDR_LEN_802_3 14 +#define ASYNC_MEDIUM_HDR_LEN HDR_LEN_802_3 + +typedef struct _GET_PKT_SIZE { + ULONG Unknown; + ULONG MaxDatagramSize; +} GET_PKT_SIZE, *PGET_PKT_SIZE; + + +// +// These structures are used to set and query information +// about our source routing table. +// + +typedef struct _SR_GET_PARAMETERS { + ULONG BoardNumber; // 0-based + ULONG SrDefault; // 0 = single route, 1 = all routes + ULONG SrBroadcast; + ULONG SrMulticast; +} SR_GET_PARAMETERS, *PSR_GET_PARAMETERS; + +typedef struct _SR_SET_PARAMETER { + ULONG BoardNumber; // 0-based + ULONG Parameter; // 0 = single route, 1 = all routes +} SR_SET_PARAMETER, *PSR_SET_PARAMETER; + +typedef struct _SR_SET_REMOVE { + ULONG BoardNumber; // 0-based + UCHAR MacAddress[6]; // remote to drop routing for +} SR_SET_REMOVE, *PSR_SET_REMOVE; + +typedef struct _SR_SET_CLEAR { + ULONG BoardNumber; // 0-based +} SR_SET_CLEAR, *PSR_SET_CLEAR; + +#include + +typedef struct _ISN_ACTION_GET_DETAILS { + USHORT NicId; // passed by caller, returns count if it is 0 + BOOLEAN BindingSet; // returns TRUE if in a set + UCHAR Type; // 1 = lan, 2 = up wan, 3 = down wan + ULONG FrameType; // returns 0 through 3 + ULONG NetworkNumber; // returns virtual net if NicId is 0 + UCHAR Node[6]; // adapter MAC address + WCHAR AdapterName[64]; // terminated with Unicode NULL +} ISN_ACTION_GET_DETAILS, *PISN_ACTION_GET_DETAILS; + + + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiAction request for the transport + provider. + +Arguments: + + Device - The device for the operation. + + Request - Describes the action request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS_FILE AddressFile; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + union { + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PISN_ACTION_GET_NETWORK_INFO GetNetworkInfo; + PISN_ACTION_GET_DETAILS GetDetails; + PSR_GET_PARAMETERS GetSrParameters; + PSR_SET_PARAMETER SetSrParameter; + PSR_SET_REMOVE SetSrRemove; + PSR_SET_CLEAR SetSrClear; + PIPX_ADDRESS_DATA IpxAddressData; + PGET_PKT_SIZE GetPktSize; + PIPX_NETNUM_DATA IpxNetnumData; + PIPX_QUERY_WAN_INACTIVITY QueryWanInactivity; + PIPXWAN_CONFIG_DONE IpxwanConfigDone; + } u; // BUGBUG: Make these unaligned?? + PIPX_ROUTE_ENTRY RouteEntry; + PNWLINK_ACTION NwlinkAction; + ULONG Segment; + ULONG AdapterNum; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + IPX_FIND_ROUTE_REQUEST routeEntry; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + IPX_DEBUG (ACTION, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + IPX_DEBUG (ACTION, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + //DbgPrint("NwlinkAction->Option is (%x)\n", NwlinkAction->Option); + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MIPX_SETSENDPTYPE: + + // + // IPX_PTYPE: Data is a single byte packet type. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_SETSENDPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->DefaultPacketType = NwlinkAction->Data[0]; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_FILTERPTYPE: + + // + // IPX_FILTERPTYPE: Data is a single byte to filter on. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_FILTERPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->FilteredType = NwlinkAction->Data[0]; + AddressFile->FilterOnPacketType = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_NOFILTERPTYPE: + + // + // IPX_STOPFILTERPTYPE. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOFILTERPTYPE\n", AddressFile)); + AddressFile->FilterOnPacketType = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->ReceiveIpxHeader || AddressFile->IsSapSocket); + break; + + case MIPX_SENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (TRUE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (FALSE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ReceiveFlagsAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + +#if 0 + case MIPX_SETNIC: + + // + // IPX_NIC_ADDRESS TRUE + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SETNIC\n", AddressFile)); + AddressFile->NicAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSETNIC: + + // + // IPX_NIC_ADDRESS (FALSE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSETNIC\n", AddressFile)); + AddressFile->NicAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ReceiveFlagsAddressing || + AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket || + AddressFile->NicAddressing); + break; +#endif + + case MIPX_SETRCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SETRCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NORCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_SENDHEADER: + + // + // IPX_RECVHDR (TRUE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDHEADER: + + // + // IPX_RECVHDR (FALSE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_RCVBCAST: + + // + // Broadcast reception enabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_RCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (!AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = TRUE; + IpxAddBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_NORCVBCAST: + + // + // Broadcast reception disabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETPKTSIZE: + + // + // IPX_MAXSIZE. + // + // BUGBUG: Figure out what the first length is for. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETPKTSIZE\n", AddressFile)); + if (DataLength >= sizeof(GET_PKT_SIZE)) { + u.GetPktSize = (PGET_PKT_SIZE)(NwlinkAction->Data); + u.GetPktSize->Unknown = 0; + u.GetPktSize->MaxDatagramSize = Device->Information.MaxDatagramSize; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = Device->SapNicCount; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM2: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM2\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = MIN (Device->MaxBindings, Device->ValidBindings); + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_GETCARDINFO: + case MIPX_GETCARDINFO2: + + // + // GETCARDINFO is IPX_ADDRESS. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETCARDINFO (%d)\n", + AddressFile, *(UNALIGNED UINT *)NwlinkAction->Data)); + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if (((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) || + ((NwlinkAction->Option == MIPX_GETCARDINFO2) && (AdapterNum <= (ULONG) MIN (Device->MaxBindings, Device->ValidBindings)))) { + +#ifdef _PNP_POWER +// Get lock + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(Device, AdapterNum); +#else + Binding = Device->Bindings[AdapterNum]; +#endif + if (Binding == NULL) { + + // + // This should be a binding in the WAN range + // of an adapter which is currently not + // allocated. We scan back to the previous + // non-NULL binding, which should be on the + // same adapter, and return a down line with + // the same characteristics as that binding. + // + + UINT i = AdapterNum; + + do { + --i; +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + } while (Binding == NULL); + + CTEAssert (Binding->Adapter->MacInfo.MediumAsync); + CTEAssert (i >= Binding->Adapter->FirstWanNicId); + CTEAssert (AdapterNum <= Binding->Adapter->LastWanNicId); + + u.IpxAddressData->status = FALSE; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + + } else { + + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Device->WanGlobalNetworkNumber)) { + + // + // In this case we make it look like one big wan + // net, so the line is "up" or "down" depending + // on whether we have given him the first indication + // or not. + // + + u.IpxAddressData->status = Device->GlobalNetworkIndicated; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Device->GlobalWanNetwork; + + } else { + + u.IpxAddressData->status = Binding->LineUp; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + } + + } + + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + + Adapter = Binding->Adapter; + u.IpxAddressData->wan = Adapter->MacInfo.MediumAsync; + u.IpxAddressData->maxpkt = + (NwlinkAction->Option == MIPX_GETCARDINFO) ? + Binding->AnnouncedMaxDatagramSize : + Binding->RealMaxDatagramSize; + u.IpxAddressData->linkspeed = Binding->MediumSpeed; +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + } else { +#if 1 + // + // Support the old format query for now. + // + + typedef struct _IPX_OLD_ADDRESS_DATA { + UINT adapternum; + UCHAR netnum[4]; + UCHAR nodenum[6]; + } IPX_OLD_ADDRESS_DATA, *PIPX_OLD_ADDRESS_DATA; + + if (DataLength >= sizeof(IPX_OLD_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if ((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, AdapterNum)) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[AdapterNum]) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_INVALID_PARAMETER; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } +#else + Status = STATUS_BUFFER_TOO_SMALL; +#endif + } + break; + + case MIPX_NOTIFYCARDINFO: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOTIFYCARDINFO (%lx)\n", AddressFile, Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + InsertTailList( + &Device->AddressNotifyQueue, + REQUEST_LINKAGE(Request) + ); + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->AddressNotifyQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_ADDRESS_NOTIFY); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_LINECHANGE: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("MIPX_LINECHANGE (%lx)\n", Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + InsertTailList( + &Device->LineChangeQueue, + REQUEST_LINKAGE(Request) + ); + + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->LineChangeQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_LINE_CHANGE); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETNETINFO_NR: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // A query on network 0 means that the caller wants + // information about our directly attached net. + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + // + // The tick count is the number of 1/18.21 second ticks + // it takes to deliver a 576-byte packet. Our link speed + // is in 100 bit-per-second units. We calculate it as + // follows (LS is the LinkSpeed): + // + // 576 bytes 8 bits 1 second 1821 ticks + // * ------ * ------------- * ---------- + // 1 byte LS * 100 bits 100 seconds + // + // which becomes 839 / LinkSpeed -- we add LinkSpeed + // to the top to round up. + // + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + +#ifdef _PNP_POWER + if (Device->ForwarderBound) { + // + // [FW] Call the Forwarder's FindRoute if installed + // + + // + // BUGBUG: What about the node number here? + // + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + u.IpxNetnumData->netnum, + NULL, // FindRouteRequest->Node, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (ACTION, (" MIPX_GETNETINFO_NR failed net %lx", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + } else { + // + // Fill in the information + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (Binding = NIC_ID_TO_BINDING(Device, routeEntry.LocalTarget.NicId)) { + u.IpxNetnumData->hopcount = routeEntry.HopCount; + u.IpxNetnumData->netdelay = routeEntry.TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(routeEntry.LocalTarget.NicId - 1); + } + + // RtlMoveMemory (u.IpxNetnumData->router, routeEntry.LocalTarget.MacAddress, 6); + + *((UNALIGNED ULONG *)u.IpxNetnumData->router) = + *((UNALIGNED ULONG *)routeEntry.LocalTarget.MacAddress); + *((UNALIGNED ULONG *)(u.IpxNetnumData->router+4)) = + *((UNALIGNED ULONG *)(routeEntry.LocalTarget.MacAddress+4)); + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + + } else { + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + // + // To maintain the lock order: BindAccessLock > RIP table + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#else + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + + } + + break; + + case MIPX_RERIPNETNUM: + + // + // BUGBUG We dont really support Re-RIP in the case of Forwarder above us + // + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + +#ifdef _PNP_POWER + + if (Device->ForwarderBound) { + + // + // [FW] Call the Forwarder's FindRoute if installed + // + + // + // BUGBUG: What about the node number here? + // + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + u.IpxNetnumData->netnum, + NULL, // FindRouteRequest->Node, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (ACTION, (" MIPX_RERIPNETNUM failed net %lx", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + } else { + // + // Fill in the information + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, routeEntry.LocalTarget.NicId)) { + u.IpxNetnumData->hopcount = routeEntry.HopCount; + u.IpxNetnumData->netdelay = routeEntry.TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(routeEntry.LocalTarget.NicId - 1); + } + + // RtlMoveMemory (u.IpxNetnumData->router, routeEntry.LocalTarget.MacAddress, 6); + + *((UNALIGNED ULONG *)u.IpxNetnumData->router) = + *((UNALIGNED ULONG *)routeEntry.LocalTarget.MacAddress); + *((UNALIGNED ULONG *)(u.IpxNetnumData->router+4)) = + *((UNALIGNED ULONG *)(routeEntry.LocalTarget.MacAddress+4)); + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + } else { + Segment = RipGetSegment(u.IpxNetnumData->netnum); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#else + Segment = RipGetSegment(u.IpxNetnumData->netnum); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId]) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_GETNETINFO: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + +#ifdef _PNP_POWER + + if (Device->ForwarderBound) { + + // + // [FW] Call the Forwarder's FindRoute if installed + // + + // + // BUGBUG: What about the node number here? + // + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + u.IpxNetnumData->netnum, + NULL, // FindRouteRequest->Node, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (ACTION, (" MIPX_GETNETINFO failed net %lx", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + } else { + // + // Fill in the information + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (Binding = NIC_ID_TO_BINDING(Device, routeEntry.LocalTarget.NicId)) { + u.IpxNetnumData->hopcount = routeEntry.HopCount; + u.IpxNetnumData->netdelay = routeEntry.TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(routeEntry.LocalTarget.NicId - 1); + } + + // RtlMoveMemory (u.IpxNetnumData->router, routeEntry.LocalTarget.MacAddress, 6); + + *((UNALIGNED ULONG *)u.IpxNetnumData->router) = + *((UNALIGNED ULONG *)routeEntry.LocalTarget.MacAddress); + *((UNALIGNED ULONG *)(u.IpxNetnumData->router+4)) = + *((UNALIGNED ULONG *)(routeEntry.LocalTarget.MacAddress+4)); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } else { + Segment = RipGetSegment(u.IpxNetnumData->netnum); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#else + Segment = RipGetSegment(u.IpxNetnumData->netnum); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_SENDPTYPE: + case MIPX_NOSENDPTYPE: + + // + // For the moment just use OptionsLength >= 1 to indicate + // that the send options include the packet type. + // + // BUGBUG: Do we need to worry about card num being there? + // + +#if 0 + IPX_DEBUG (ACTION, ("%lx: MIPS_%sSENDPTYPE\n", AddressFile, + NwlinkAction->Option == MIPX_SENDPTYPE ? "" : "NO")); +#endif + break; + + case MIPX_ZEROSOCKET: + + // + // Sends from this address should be from socket 0; + // This is done the simple way by just putting the + // information in the address itself, instead of + // making it per address file (this is OK since + // this call is not exposed through winsock). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ZEROSOCKET\n", AddressFile)); + AddressFile->Address->SendSourceSocket = 0; + AddressFile->Address->LocalAddress.Socket = 0; + break; + + + // + // This next batch are the source routing options. They + // are submitted by the IPXROUTE program. + // + // BUGBUG: Do we expose all binding set members to this? + + case MIPX_SRGETPARMS: + + if (DataLength >= sizeof(SR_GET_PARAMETERS)) { + u.GetSrParameters = (PSR_GET_PARAMETERS)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.GetSrParameters->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.GetSrParameters->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRDEF: + case MIPX_SRBCAST: + case MIPX_SRMULTI: + + if (DataLength >= sizeof(SR_SET_PARAMETER)) { + u.SetSrParameter = (PSR_SET_PARAMETER)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrParameter->BoardNumber+1)) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrParameter->BoardNumber+1]) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRREMOVE: + + if (DataLength >= sizeof(SR_SET_REMOVE)) { + u.SetSrRemove = (PSR_SET_REMOVE)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrRemove->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrRemove->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRCLEAR: + + if (DataLength >= sizeof(SR_SET_CLEAR)) { + u.SetSrClear = (PSR_SET_CLEAR)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrClear->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrClear->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + case MIPX_LOCALTARGET: + + // + // A request for the local target for an IPX address. + // + + if (DataLength < sizeof(ISN_ACTION_GET_LOCAL_TARGET)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)(NwlinkAction->Data); + + if (Device->ForwarderBound) { + + // + // [FW] Call the Forwarder's FindRoute if installed + // + + // + // BUGBUG: What about the node number here? + // + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + (PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress, + NULL, // FindRouteRequest->Node, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (ACTION, (" MIPX_LOCALTARGET failed net %lx", + REORDER_ULONG(u.GetLocalTarget->IpxAddress.NetworkAddress))); + Status = STATUS_BAD_NETWORK_PATH; + } else { + // + // Fill in the information + // + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // BUGBUG What about check for IPX_ROUTER_LOCAL_NET + // + if (Binding = NIC_ID_TO_BINDING(Device, routeEntry.LocalTarget.NicId)) { + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + u.GetLocalTarget->LocalTarget.NicId = Binding->NicId; + + } else { + + u.GetLocalTarget->LocalTarget.NicId = routeEntry.LocalTarget.NicId; + } + + *((UNALIGNED ULONG *)u.GetLocalTarget->LocalTarget.MacAddress) = + *((UNALIGNED ULONG *)routeEntry.LocalTarget.MacAddress); + *((UNALIGNED ULONG *)(u.GetLocalTarget->LocalTarget.MacAddress+4)) = + *((UNALIGNED ULONG *)(routeEntry.LocalTarget.MacAddress+4)); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } else { + Segment = RipGetSegment((PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See if this route is local. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + if ((RouteEntry != NULL) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + // + // This is a local net, to send to it you just use + // the appropriate NIC ID and the real MAC address. + // + + if ((RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) == 0) { + + // + // It's the virtual net, send via the first card. + // + #ifdef _PNP_POWER + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, 1); + #else + u.GetLocalTarget->LocalTarget.NicId = 1; + #endif + + } else { + + #ifdef _PNP_POWER + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); + + } else { + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, RouteEntry->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + #else + Binding = Device->Bindings[RouteEntry->NicId]; + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + u.GetLocalTarget->LocalTarget.NicId = Binding->NicId; + } else { + + u.GetLocalTarget->LocalTarget.NicId = RouteEntry->NicId; + + } + #endif + + } + + RtlCopyMemory( + u.GetLocalTarget->LocalTarget.MacAddress, + u.GetLocalTarget->IpxAddress.NodeAddress, + 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (u.GetLocalTarget->IpxAddress.NetworkAddress, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.GetLocalTarget; + InsertTailList( + &Device->Segments[Segment].WaitingLocalTarget, + REQUEST_LINKAGE(Request)); + + } + + #ifdef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + #endif + } + #ifndef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + #endif + + } + + break; + + case MIPX_NETWORKINFO: + + // + // A request for network information about the immediate + // route to a network. + // + + if (DataLength < sizeof(ISN_ACTION_GET_NETWORK_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetNetworkInfo = (PISN_ACTION_GET_NETWORK_INFO)(NwlinkAction->Data); + + if (u.GetNetworkInfo->Network == 0) { + + // + // This is information about the local card. + // + + u.GetNetworkInfo->LinkSpeed = Device->LinkSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Device->Information.MaxDatagramSize; + + } else { + + if (Device->ForwarderBound) { + + // + // [FW] Call the Forwarder's FindRoute if installed + // + + // + // BUGBUG: What about the node number here? + // + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + (PUCHAR)&u.GetNetworkInfo->Network, + NULL, // FindRouteRequest->Node, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (ACTION, (" MIPX_GETNETINFO_NR failed net %lx", + REORDER_ULONG(u.GetNetworkInfo->Network))); + Status = STATUS_BAD_NETWORK_PATH; + } else { + // + // Fill in the information + // + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, routeEntry.LocalTarget.NicId)) { + // + // Our medium speed is stored in 100 bps, we + // convert to bytes/sec by multiplying by 12 + // (should really be 100/8 = 12.5). + // + + u.GetNetworkInfo->LinkSpeed = Binding->MediumSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Binding->AnnouncedMaxDatagramSize; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } else { + Segment = RipGetSegment((PUCHAR)&u.GetNetworkInfo->Network); + + #ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + #endif + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetNetworkInfo->Network); + + if ((RouteEntry != NULL) && + #ifdef _PNP_POWER + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + #else + (Binding = Device->Bindings[RouteEntry->NicId])) { + #endif + + // + // Our medium speed is stored in 100 bps, we + // convert to bytes/sec by multiplying by 12 + // (should really be 100/8 = 12.5). + // + + u.GetNetworkInfo->LinkSpeed = Binding->MediumSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Binding->AnnouncedMaxDatagramSize; + + } else { + + // + // Fail the call, we don't have a route yet. + // BUGBUG: This requires that a packet has been + // sent to this net already; nwrdr says this is + // OK, they will send their connect request + // before they query. On the server it should + // have RIP running so all nets should be in + // the database. + // + + Status = STATUS_BAD_NETWORK_PATH; + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + #ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + #endif + } + } + + break; + + case MIPX_CONFIG: + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(ISN_ACTION_GET_DETAILS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetDetails = (PISN_ACTION_GET_DETAILS)(NwlinkAction->Data); + + if (u.GetDetails->NicId == 0) { + + // + // This is information about the local card. We also + // tell him the total number of bindings in NicId. + // + + u.GetDetails->NetworkNumber = Device->VirtualNetworkNumber; + u.GetDetails->NicId = (USHORT)MIN (Device->MaxBindings, Device->ValidBindings); + + } else { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, u.GetDetails->NicId); +#else + Binding = Device->Bindings[u.GetDetails->NicId]; +#endif + + if ((Binding != NULL) && + (u.GetDetails->NicId <= MIN (Device->MaxBindings, Device->ValidBindings))) { + + ULONG StringLoc; +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + u.GetDetails->NetworkNumber = Binding->LocalAddress.NetworkAddress; + if (Binding->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) { + u.GetDetails->FrameType = ISN_FRAME_TYPE_ARCNET; + } else { + u.GetDetails->FrameType = Binding->FrameType; + } + u.GetDetails->BindingSet = Binding->BindingSetMember; + if (Binding->Adapter->MacInfo.MediumAsync) { + if (Binding->LineUp) { + u.GetDetails->Type = 2; + } else { + u.GetDetails->Type = 3; + } + } else { + u.GetDetails->Type = 1; + } + + RtlCopyMemory (u.GetDetails->Node, Binding->LocalMacAddress.Address, 6); + + // + // Copy the adapter name, including the final NULL. + // + + StringLoc = (Binding->Adapter->AdapterNameLength / sizeof(WCHAR)) - 2; + while (Binding->Adapter->AdapterName[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory( + u.GetDetails->AdapterName, + &Binding->Adapter->AdapterName[StringLoc+1], + Binding->Adapter->AdapterNameLength - ((StringLoc+1) * sizeof(WCHAR))); + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } else { + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Status = STATUS_INVALID_PARAMETER; + + } + } + + break; + + + // + // Return new nic info to the requestor. Currently, no check for + // who retrieved the info earlier. + // + case MIPX_GETNEWNICINFO: + + DbgPrint("GetNewNicInfo case entered\n"); + IPX_DEBUG (ACTION, ("%lx: MIPX_GETNEWNICINFO (%lx)\n", AddressFile, + Request)); + // + // a request for details on new bindings. + // + Status = GetNewNics(Device, Request, TRUE, NwlinkAction, BufferLength, FALSE); + break; + + // + // In case a LineUp occurs with the IpxwanConfigRequired, this is used + // to indicate to IPX that the config is done and that the LineUp + // can be indicated to the other clients. + // + case MIPX_IPXWAN_CONFIG_DONE: + + DbgPrint("IPXWAN_CONFIG_DONE case entered\n"); + IPX_DEBUG (ACTION, ("MIPX_IPXWAN_CONFIG_DONE (%lx)\n", Request)); + + if (DataLength < sizeof(IPXWAN_CONFIG_DONE)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxwanConfigDone = (PIPXWAN_CONFIG_DONE)(NwlinkAction->Data); + Status = IpxIndicateLineUp( IpxDevice, + u.IpxwanConfigDone->NicId, + u.IpxwanConfigDone->Network, + u.IpxwanConfigDone->LocalNode, + u.IpxwanConfigDone->RemoteNode); + break; + + // + // Used to query the WAN inactivity counter for a given NicId + // + case MIPX_QUERY_WAN_INACTIVITY: { + + USHORT NicId; + + DbgPrint("QUERY_WAN_INACTIVITY case entered\n"); + IPX_DEBUG (ACTION, ("MIPX_QUERY_WAN_INACTIVITY (%lx)\n", Request)); + + if (DataLength < sizeof(IPX_QUERY_WAN_INACTIVITY)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.QueryWanInactivity = (PIPX_QUERY_WAN_INACTIVITY)(NwlinkAction->Data); + + // + // If this is an invalid Nic, then we need to associate a Nic with the ConnectionId that + // was passed in. + // This should happen only once per line up. + // + if (u.QueryWanInactivity->NicId == INVALID_NICID) { + PBINDING Binding; + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + for ( NicId = Device->HighestLanNicId+1;NicId < Index;NicId++ ) { + Binding = NIC_ID_TO_BINDING(Device, NicId); + if (Binding && (Binding->ConnectionId == u.QueryWanInactivity->ConnectionId)) { + CTEAssert(Binding->Adapter->MacInfo.MediumAsync); + if (Binding->LineUp != LINE_CONFIG) { + IPX_DEBUG (WAN, ("Binding is not in config state yet got QUERY_WAN_INACTIVITY %lx %lx", Binding, Request)); + NicId = 0; + } + u.QueryWanInactivity->NicId = NicId; + break; + } + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } + + if (NicId) { + u.QueryWanInactivity->WanInactivityCounter = IpxInternalQueryWanInactivity(NicId); + Status = STATUS_SUCCESS; + } else { + Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + IPX_DEBUG (ACTION, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* IpxTdiAction */ + + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel an Action. + What is done to cancel it is specific to each action. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PREQUEST Request = (PREQUEST)Irp; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN Found; + UINT IOCTLType; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Find the request on the address notify queue. + // + + Found = FALSE; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (p = Device->AddressNotifyQueue.Flink; + p != &Device->AddressNotifyQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_NOTIFYCARDINFO; + break; + } + } + + if (!Found) { + for (p = Device->LineChangeQueue.Flink; + p != &Device->LineChangeQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_LINECHANGE; + break; + } + } + } + + if (!Found) { + for (p = Device->NicNtfQueue.Flink; + p != &Device->NicNtfQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_GETNEWNICINFO; + break; + } + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + if (IOCTLType == MIPX_NOTIFYCARDINFO) { + IPX_DEBUG(ACTION, ("Cancelled action NOTIFYCARDINFO %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } else { + if (IOCTLType == MIPX_LINECHANGE) { + IPX_DEBUG(ACTION, ("Cancelled action LINECHANGE %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + } else { + IPX_DEBUG(ACTION, ("Cancelled action LINECHANGE %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + } + } + + } +#if DBG + else { + IPX_DEBUG(ACTION, ("Cancelled action orphan %lx\n", Request)); + } +#endif + +} /* IpxCancelAction */ + + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ) + +/*++ + +Routine Description: + + This routine aborts any line change IRPs posted by the + control channel with the specified open context. It is + called when a control channel is being shut down. + +Arguments: + + ControlChannelContext - The context assigned to the control + channel when it was opened. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + CTELockHandle LockHandle; + LIST_ENTRY AbortList; + PLIST_ENTRY p; + PREQUEST Request; + KIRQL irql; + + + InitializeListHead (&AbortList); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Device->Lock, &LockHandle); + + p = Device->LineChangeQueue.Flink; + + while (p != &Device->LineChangeQueue) { + LARGE_INTEGER ControlChId; + + Request = LIST_ENTRY_TO_REQUEST(p); + + CCID_FROM_REQUEST(ControlChId, Request); + + p = p->Flink; + + if (ControlChId.QuadPart == ((PLARGE_INTEGER)ControlChannelContext)->QuadPart) { + RemoveEntryList (REQUEST_LINKAGE(Request)); + InsertTailList (&AbortList, REQUEST_LINKAGE(Request)); + } + } + + while (!IsListEmpty (&AbortList)) { + + p = RemoveHeadList (&AbortList); + Request = LIST_ENTRY_TO_REQUEST(p); + + IPX_DEBUG(ACTION, ("Aborting line change %lx\n", Request)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Device->Lock, &LockHandle); + } + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); +} /* IpxAbortLineChanges */ + + +NTSTATUS +GetNewNics( + PDEVICE Device, + IN PREQUEST Request, + BOOLEAN fCheck, + PNWLINK_ACTION NwlinkAction, + UINT BufferLength, + BOOLEAN OldIrp +) +{ + NTSTATUS Status = STATUS_SUCCESS; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + CTELockHandle LockHandle1; + PBINDING Binding; + ULONG NoOfNullNics = 0; + PIPX_NICS pNics; + PIPX_NIC_INFO pNicInfo; + PIPX_NIC_INFO pLastNicInfo; + UINT LengthOfHeader; + ULONG n, i; + KIRQL OldIrql; + + + LengthOfHeader = (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0])); + if (fCheck) + { + if (BufferLength < (LengthOfHeader + FIELD_OFFSET(IPX_NICS, Data[0]) + sizeof(IPX_NIC_INFO))) + { + IPX_DEBUG (ACTION, ("Nwlink action failed, buffer too small for even one NICs info\n")); + return STATUS_BUFFER_TOO_SMALL; + } + } + else + { + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + } + pNics = (PIPX_NICS)(NwlinkAction->Data); + pNicInfo = (PIPX_NIC_INFO)(pNics->Data); + pLastNicInfo = pNicInfo + ((BufferLength - LengthOfHeader - FIELD_OFFSET(IPX_NICS, Data[0]))/sizeof(IPX_NIC_INFO)) - 1; + + DbgPrint("GetNewNicInfo: pNicInfo=(%x), pLastNicInfo=(%x),LengthOfHeader=(%x), BindingCount=(%x)\n", pNicInfo, pLastNicInfo, LengthOfHeader, Device->ValidBindings); + DbgPrint("BufferLength is (%d). Length for storing NICS is (%d)\n", BufferLength, (BufferLength - LengthOfHeader - FIELD_OFFSET(IPX_NICS, Data[0]))); + // + // Optimize since we don't want to go over the array all the time. + // + + CTEGetLock (&Device->Lock, &LockHandle); + + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + for (n=0, i=1; i<=Index; i++) + { + Binding = NIC_ID_TO_BINDING(Device, i); + + if (!Binding) + { + NoOfNullNics++; + continue; + } + // + // If we have already indicated info about this NIC, go on to the + // next nic. + // + if ((Binding->fInfoIndicated && !pNics->fAllNicsDesired) + || (pNicInfo > pLastNicInfo)) + { + continue; + } + + // + // If we have a WAN nic, indicate the line up/down status. Also, + // copy the remote address into the app. field + // + if (Binding->Adapter->MacInfo.MediumAsync) + { + RtlCopyMemory(pNicInfo->RemoteNodeAddress, Binding->WanRemoteNode, HARDWARE_ADDRESS_LENGTH); + if (Binding->LineUp) + { + // pNicInfo->Status = NIC_LINE_UP; + + pNicInfo->Status = NIC_CREATED; + } + else + { + // pNicInfo->Status = NIC_LINE_DOWN; + + pNicInfo->Status = NIC_DELETED; + } + + pNicInfo->InterfaceIndex = Binding->InterfaceIndex; + pNicInfo->MaxPacketSize = + Binding->MaxSendPacketSize - ASYNC_MEDIUM_HDR_LEN; + } + else + { + if (Binding->LocalAddress.NetworkAddress == 0) + { + pNicInfo->Status = NIC_CREATED; + } + else + { + pNicInfo->Status = NIC_CONFIGURED; + } + + // + // RealMaxDatagramSize does not include space for ipx + // header. The forwarder needs to have it included since + // we give the entire packet (mimus the mac header) to + // the forwarder + // + pNicInfo->MaxPacketSize = + Binding->RealMaxDatagramSize + sizeof(IPX_HEADER); + } + pNicInfo->NdisMediumType= Binding->Adapter->MacInfo.RealMediumType; + pNicInfo->LinkSpeed = Binding->MediumSpeed; + pNicInfo->PacketType = Binding->FrameType; + pNicInfo->NetworkAddress= Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(pNicInfo->LocalNodeAddress, Binding->LocalAddress.NodeAddress, HARDWARE_ADDRESS_LENGTH); + pNicInfo->NicId = Binding->NicId; + pNicInfo->ConnectionId = Binding->ConnectionId; + pNicInfo->IpxwanConfigRequired = Binding->IpxwanConfigRequired; + + pNicInfo++; //increment to store next nic info + n++; //indicates the # of nics processed so far. + Binding->fInfoIndicated = TRUE; + DbgPrint("Iteration no = (%d) complete\n", n); + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + CTEFreeLock (&Device->Lock, LockHandle); + + pNics->NoOfNics = n; + pNics->TotalNoOfNics = Device->ValidBindings - NoOfNullNics; + + // + // If no nics. to report, queue the request + // + if (!n) { + + DbgPrint("GetNewNicInfo: Inserting Irp\n"); + CTEGetLock (&Device->Lock, &LockHandle); + + InsertTailList( &Device->NicNtfQueue, REQUEST_LINKAGE(Request) ); + + if (!OldIrp) + { + CTEFreeLock (&Device->Lock, LockHandle); + IoAcquireCancelSpinLock(&OldIrql); + IoSetCancelRoutine (Request, IpxCancelAction); + IoReleaseCancelSpinLock(OldIrql); + CTEGetLock (&Device->Lock, &LockHandle); + } + if (Request->Cancel) { + DbgPrint("GetNewNicInfo:Cancelling Irp\n"); + + (VOID)RemoveTailList (&Device->NicNtfQueue); + CTEFreeLock (&Device->Lock, LockHandle); + IoAcquireCancelSpinLock(&OldIrql); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(OldIrql); + Status = STATUS_CANCELLED; + } else { + if (!OldIrp) + { + IpxReferenceDevice (Device, DREF_NIC_NOTIFY); + } + Status = STATUS_PENDING; + CTEFreeLock (&Device->Lock, LockHandle); + } + } + else + { + DbgPrint("Reporting (%d) nics\n", n); + } + + return(Status); +} + + +VOID +IpxAbortNtfChanges( + IN PVOID ControlChannelContext + ) + +/*++ + +Routine Description: + + This routine aborts any line change IRPs posted by the + control channel with the specified open context. It is + called when a control channel is being shut down. + +Arguments: + + ControlChannelContext - The context assigned to the control + channel when it was opened. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + CTELockHandle LockHandle; + LIST_ENTRY AbortList; + PLIST_ENTRY p; + PREQUEST Request; + KIRQL irql; + + + InitializeListHead (&AbortList); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Device->Lock, &LockHandle); + + p = Device->NicNtfQueue.Flink; + + while (p != &Device->NicNtfQueue) { + LARGE_INTEGER ControlChId; + + Request = LIST_ENTRY_TO_REQUEST(p); + + CCID_FROM_REQUEST(ControlChId, Request); + + DbgPrint("IpxAbortNtfChange: There is at least one IRP in the queue\n"); + p = p->Flink; + + if (ControlChId.QuadPart == ((PLARGE_INTEGER)ControlChannelContext)->QuadPart) { + DbgPrint("IpxAbortNtfChanges: Dequeing an Irp\n"); + RemoveEntryList (REQUEST_LINKAGE(Request)); + InsertTailList (&AbortList, REQUEST_LINKAGE(Request)); + } + } + + while (!IsListEmpty (&AbortList)) { + + p = RemoveHeadList (&AbortList); + Request = LIST_ENTRY_TO_REQUEST(p); + + IPX_DEBUG(ACTION, ("Aborting line change %lx\n", Request)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + DbgPrint("IpxAbortNtfChanges: Cancelling the dequeued Irp\n"); + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + + IpxDereferenceDevice (Device, DREF_NIC_NOTIFY); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Device->Lock, &LockHandle); + } + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); +} /* IpxAbortNtfChanges */ + +NTSTATUS +IpxIndicateLineUp( + IN PDEVICE Device, + IN USHORT NicId, + IN ULONG Network, + IN UCHAR LocalNode[6], + IN UCHAR RemoteNode[6] + ) +/*++ + +Routine Description: + + This routine indicates a line-up to all the concerned clients once + the line is up. + For now, called only if the MIPX_IPXWAN_CONFIG_DONE IOCTL is received. + + +Arguments: + + Device - The device for the operation. + + NicId - The NicId corresponding to the binding that is up. + + Network, LocalNode, RemoteNode - addresses corresponding to this lineup. + +Return Value: + + NTSTATUS - status of operation. + +--*/ +{ + PBINDING Binding = NIC_ID_TO_BINDING(Device, NicId); + IPX_LINE_INFO LineInfo; + USHORT i; + PLIST_ENTRY p; + PREQUEST Request; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + UINT BufferLength; + PIPX_ADDRESS_DATA IpxAddressData; + IPXCP_CONFIGURATION Configuration; + KIRQL irql, OldIrq; + NTSTATUS Status; + NTSTATUS ntStatus; + KIRQL OldIrql; + + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + if (!(Binding && + Binding->Adapter->MacInfo.MediumAsync && + Binding->LineUp == LINE_CONFIG)) { + IPX_DEBUG(WAN, ("Indicate line up on invalid line: %lu\n", NicId)); + return STATUS_INVALID_PARAMETER; + } + + // [BUGBUG] take bindaccesslock here... + // + + // + // If we are here, then this flag was set on a line up. + // We turn it off now so that the adapter dll above us can decide + // to indicate this lineup to the router module instead of the IpxWan module + // + + CTEAssert(Binding->IpxwanConfigRequired); + + Binding->IpxwanConfigRequired = 0; + + Binding->LineUp = LINE_UP; + + // + // Indicate to the upper drivers. + // + + LineInfo.LinkSpeed = Binding->MediumSpeed; + LineInfo.MaximumPacketSize = Binding->MaxSendPacketSize - 14; + LineInfo.MaximumSendSize = Binding->MaxSendPacketSize - 14; + LineInfo.MacOptions = Binding->Adapter->MacInfo.MacOptions; + + // + // Fill-in the addresses into the bindings + // + Binding->LocalAddress.NetworkAddress = Network; + + *(UNALIGNED ULONG *)Binding->LocalAddress.NodeAddress = *(UNALIGNED ULONG *)LocalNode; + *(UNALIGNED ULONG *)(Binding->LocalAddress.NodeAddress+4) = *(UNALIGNED ULONG *)(LocalNode+4); + + *(UNALIGNED ULONG *)Binding->WanRemoteNode = *(UNALIGNED ULONG *)RemoteNode; + *(UNALIGNED ULONG *)(Binding->WanRemoteNode+4) = *(UNALIGNED ULONG *)(RemoteNode+4); + + // + // Fill in the IPXCP_CONFIGURATION structure from the binding. + // + *(UNALIGNED ULONG *)Configuration.Network = Binding->LocalAddress.NetworkAddress; + + *(UNALIGNED ULONG *)Configuration.LocalNode = *(UNALIGNED ULONG *)Binding->LocalAddress.NodeAddress; + *(UNALIGNED USHORT *)(Configuration.LocalNode+4) = *(UNALIGNED USHORT *)(Binding->LocalAddress.NodeAddress+4); + + *(UNALIGNED ULONG *)Configuration.RemoteNode = *(UNALIGNED ULONG *)RemoteNode; + *(UNALIGNED USHORT *)(Configuration.RemoteNode+4) = *(UNALIGNED USHORT *)(RemoteNode+4); + + Configuration.InterfaceIndex = Binding->InterfaceIndex; + Configuration.ConnectionClient = Binding->DialOutAsync; + +#ifdef _PNP_POWER + + // + // We dont give lineups; instead indicate only if the PnP reserved address + // changed to SPX. NB gets all PnP indications with the reserved address case + // marked out. + // + { + IPX_PNP_INFO NBPnPInfo; + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + // + // NB's reserved address changed. + // + NBPnPInfo.NewReservedAddress = TRUE; + + if (!Device->VirtualNetwork) { + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + IpxPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + } + } else { + NBPnPInfo.NewReservedAddress = FALSE; + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + Binding->IsnInformed[IDENTIFIER_NB] = TRUE; + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + NBPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADD_DEVICE (lineup) to NB: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + } + + // + // Indicate to the upper drivers. + // + // + // Give line up to RIP as it is not PnP aware. + // + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + Binding->IsnInformed[IDENTIFIER_RIP] = TRUE; + (*Device->UpperDrivers[IDENTIFIER_RIP].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + &Configuration); + } +#else + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + &Configuration); + } + } +#endif + + // + // Add router entry for this net since it was not done on LineUp. + // Also, update the addresses' pre-constructed local IPX address. + // + { + ULONG CurrentHash; + PADAPTER Adapter = Binding->Adapter; + PADDRESS Address; + + // + // Add a router entry for this net if there is no router. + // We want the number of ticks for a 576-byte frame, + // given the link speed in 100 bps units, so we calculate + // as: + // + // seconds 18.21 ticks 4608 bits + // --------------------- * ----------- * --------- + // link_speed * 100 bits second frame + // + // to get the formula + // + // ticks/frame = 839 / link_speed. + // + // We add link_speed to the numerator also to ensure + // that the value is at least 1. + // + + if ((!Device->UpperDriverBound[IDENTIFIER_RIP]) && + (*(UNALIGNED ULONG *)Configuration.Network != 0)) { + + if (RipInsertLocalNetwork( + *(UNALIGNED ULONG *)Configuration.Network, + Binding->NicId, + Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)) != STATUS_SUCCESS) { + + // + // This means we couldn't allocate memory, or + // the entry already existed. If it already + // exists we can ignore it for the moment. + // + // BUGBUG: Now it will succeed if the network + // exists. + // + + IPX_DEBUG (WAN, ("Line up, could not insert local network\n")); + // [FW] Binding->LineUp = FALSE; + Binding->LineUp = LINE_DOWN; + return STATUS_SUCCESS; + } + } + + // + // Update the device node and all the address + // nodes if we have only one bound, or this is + // binding one. + // + + if (!Device->VirtualNetwork) { + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + Device->SourceAddress.NetworkAddress = *(UNALIGNED ULONG *)(Configuration.Network); + RtlCopyMemory (Device->SourceAddress.NodeAddress, Configuration.LocalNode, 6); + } + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration.Network; + RtlCopyMemory (Address->LocalAddress.NodeAddress, Configuration.LocalNode, 6); + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + } + } + + + + // + // [FW] IpxWan config state will not be entered if only the line params are getting + // updated. + // + // if (!UpdateLineUp) { + + // + // Instead of the check for ConnectionClient, use the DialOutAsync flag + // + if ((Device->SingleNetworkActive) && + /*(LineUp->Configuration.ConnectionClient == 1)*/ + Binding->DialOutAsync) { + + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = TRUE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoAcquireCancelSpinLock( &irql ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock( irql ); + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + // + // BUGBUG:NwRdr assumes that Line-up completions are at DPC + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + IpxCompleteRequest (Request); + KeLowerIrql(OldIrql); + + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + // + // If we have a virtual net, do a broadcast now so + // the router on the other end will know about us. + // + // BUGBUG: Use RipSendResponse, and do it even + // if SingleNetworkActive is FALSE?? + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + } + + // + // Find a queued address notify and complete it. + // If WanGlobalNetworkNumber is TRUE, we only do + // this when the first dialin line comes up. + // + + if ((!Device->WanGlobalNetworkNumber || + (!Device->GlobalNetworkIndicated && !Binding->DialOutAsync)) + && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + if (Device->WanGlobalNetworkNumber) { + Device->GlobalWanNetwork = Binding->LocalAddress.NetworkAddress; + Device->GlobalNetworkIndicated = TRUE; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + if (Device->WanGlobalNetworkNumber) { + IpxAddressData->adapternum = Device->SapNicCount - 1; + } else { + IpxAddressData->adapternum = Binding->NicId - 1; + } + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = TRUE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoAcquireCancelSpinLock( &irql ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock( irql ); + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + + Binding->fInfoIndicated = FALSE; + if ((p = ExInterlockedRemoveHeadList( + &Device->NicNtfQueue, + &Device->Lock)) != NULL) + { + Request = LIST_ENTRY_TO_REQUEST(p); + + DbgPrint("IpxStatus: WAN LINE UP\n"); + Status = GetNewNics(Device, Request, FALSE, NULL, 0, FALSE); + if (Status != STATUS_SUCCESS) + { + DbgPrint("WAN Line up screw up\n"); + } + else + { + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(OldIrq); + + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + IpxDereferenceDevice (Device, DREF_NIC_NOTIFY); + } + } +// } + + return STATUS_SUCCESS; +} diff --git a/private/ntos/tdi/isn/ipx/adapter.c b/private/ntos/tdi/isn/ipx/adapter.c new file mode 100644 index 000000000..4dc3799aa --- /dev/null +++ b/private/ntos/tdi/isn/ipx/adapter.c @@ -0,0 +1,804 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + adapter.c + +Abstract: + + This module contains code which implements the ADAPTER object. + Routines are provided to reference, and dereference transport + adapter objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// These are init only until binding is really dynamic. +// +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateAdapter) +#endif +#endif _PNP_POWER + +// +// [FW] So, later we can change this to pnp-compatible value +// + +// +// ULONG +// ADAPTER_INDEX_TO_FWCONTEXT( +// IN ULONG _adapterindex; +// ); +// + +#define ADAPTER_INDEX_TO_FWCONTEXT(_adapterindex) _adapterindex + + +VOID +IpxRefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Binding->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Binding->ReferenceCount); + +} /* IpxRefBinding */ + + +VOID +IpxDerefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Binding->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyBinding (Binding); + } + +} /* IpxDerefBinding */ + + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Adapter - Pointer to a pointer to a transport device context object. + + AdapterName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PADAPTER Adapter; +#if 0 + UINT i, j; +#endif + + Adapter = (PADAPTER)IpxAllocateMemory (sizeof(ADAPTER) + AdapterName->Length + sizeof(WCHAR), MEMORY_ADAPTER, "Adapter"); + +#ifdef _PNP_POWER + if (Adapter == NULL) { + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create adapter %lx failed\n", AdapterName)); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %lx %lx succeeded\n", Adapter, AdapterName)); +#else + if (Adapter == NULL) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName->Buffer)); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %ws succeeded\n", AdapterName->Buffer)); +#endif + + RtlZeroMemory(Adapter, sizeof(ADAPTER)); + + // + // Copy over the adapter name. + // + + Adapter->AdapterNameLength = AdapterName->Length + sizeof(WCHAR); + Adapter->AdapterName = (PWCHAR)(Adapter+1); + RtlCopyMemory( + Adapter->AdapterName, + AdapterName->Buffer, + AdapterName->Length); + Adapter->AdapterName[AdapterName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + +#if DBG + RtlCopyMemory(Adapter->Signature1, "IAD1", 4); +#endif + + Adapter->Type = IPX_ADAPTER_SIGNATURE; + Adapter->Size = sizeof(ADAPTER); + + CTEInitLock (&Adapter->Lock); + + InitializeListHead (&Adapter->RequestCompletionQueue); + + InitializeListHead (&Adapter->ReceiveBufferPoolList); + + ExInitializeSListHead (&Adapter->ReceiveBufferList); + + Adapter->Device = Device; + Adapter->DeviceLock = &Device->Lock; + IpxReferenceDevice (Device, DREF_ADAPTER); + +#if 0 + Adapter->ReceiveBufferPool.Next = NULL; + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Adapter->Bindings[i] = NULL; + } + Adapter->BindingCount = 0; + + for (i = 0; i < IDENTIFIER_TOTAL; i++) { + for (j = 0; j < SOURCE_ROUTE_HASH_SIZE; j++) { + Adapter->SourceRoutingHeads[i][j] = (PSOURCE_ROUTE)NULL; + } + } +#endif + + // + // BUGBUG: For the moment, we have to do the source + // routing operation on any type where broadcast + // may not be used for discovery -- improve this + // hopefully. + // + + Adapter->SourceRoutingEmpty[IDENTIFIER_RIP] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_IPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_SPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_NB] = TRUE; + +#ifdef _PNP_POWER + // + // Lock here? [BUGBUGZZ] + // + Adapter->ReferenceCount = 1; +#endif + + *AdapterPtr = Adapter; + + return STATUS_SUCCESS; + +} /* IpxCreateAdapter */ + + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Adapter - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ULONG Database, Hash; + PSOURCE_ROUTE Current; + ULONG ReceiveBufferPoolSize; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + PLIST_ENTRY p; + UINT i; + + IPX_DEBUG (ADAPTER, ("Destroy adapter %lx\n", Adapter)); + + // + // Free any receive buffer pools this adapter has. + // + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (Adapter->MaxReceivePacketSize * Device->InitReceiveBuffers); + + while (!IsListEmpty (&Adapter->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Adapter->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, IPX_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + IpxDeinitializeReceiveBuffer (Adapter, ReceiveBuffer, Adapter->MaxReceivePacketSize); + + } + + IPX_DEBUG (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + IpxFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } + + // + // Free all the source routing information for this adapter. + // + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + } + } + } + + IpxDereferenceDevice (Adapter->Device, DREF_ADAPTER); + IpxFreeMemory (Adapter, sizeof(ADAPTER) + Adapter->AdapterNameLength, MEMORY_ADAPTER, "Adapter"); + +} /* IpxDestroyAdapter */ + + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a binding structure. + +Arguments: + + Device - The device. + + ConfigBinding - Information about this binding. If this is + NULL then this is a WAN binding and all the relevant + information will be filled in by the caller. + + NetworkNumberIndex - The index in the frame type array for + ConfigBinding indicating which frame type this binding is for. + Not used if ConfigBinding is not provided. + + AdapterName - Used for error logging. + + BindingPtr - Returns the allocated binding structure. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PBINDING Binding; +#ifdef _PNP_POWER + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBinding; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBinding(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } + +GotBinding: + + Binding = CONTAINING_RECORD (s, BINDING, PoolLinkage); + +#else + Binding = (PBINDING)IpxAllocateMemory (sizeof(BINDING), MEMORY_ADAPTER, "Binding"); + + // + // We can't vsprintf a %ws at DPC level, so we check for + // that. Only WAN bindings will be created then. + // + + if (Binding == NULL) { +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } +#endif + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws succeeded, %lx\n", AdapterName, Binding)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN succeeded\n")); + } +#endif + + RtlZeroMemory(Binding, sizeof(BINDING)); + + // + // Initialize the reference count. + // + + Binding->ReferenceCount = 1; +#if DBG + Binding->RefTypes[BREF_BOUND] = 1; +#endif + +#if DBG + RtlCopyMemory(Binding->Signature1, "IBI1", 4); +#endif + + Binding->Type = IPX_BINDING_SIGNATURE; + Binding->Size = sizeof(BINDING); + + Binding->Device = Device; + Binding->DeviceLock = &Device->Lock; + + if (ConfigBinding != NULL) { + + ULONG Temp = ConfigBinding->NetworkNumber[NetworkNumberIndex]; + Binding->ConfiguredNetworkNumber = REORDER_ULONG (Temp); + + Binding->AutoDetect = ConfigBinding->AutoDetect[NetworkNumberIndex]; + Binding->DefaultAutoDetect = ConfigBinding->DefaultAutoDetect[NetworkNumberIndex]; + + Binding->AllRouteDirected = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_DEF]; + Binding->AllRouteBroadcast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_BC]; + Binding->AllRouteMulticast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_MC]; + + } + + Binding->ReceiveBroadcast = TRUE; +#if 0 + Binding->BindingSetMember = FALSE; + Binding->NextBinding = (PBINDING)NULL; + Binding->DialOutAsync = FALSE; +#endif + + // + // We set Binding->FrameType later, after we can map it based on the + // media type of the adapter we bind to. + // + + *BindingPtr = Binding; + + return STATUS_SUCCESS; + +} /* IpxCreateBinding */ + + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine destroys a binding structure. + +Arguments: + + Binding - Pointer to a transport binding structure. + +Return Value: + + None. + +--*/ + +{ + IPX_DEBUG (ADAPTER, ("Destroy binding %lx\n", Binding)); + +#ifdef _PNP_POWER + + IPX_PUSH_ENTRY_LIST( + &IpxDevice->BindingList, + &Binding->PoolLinkage, + &IpxDevice->SListsLock); +#else + IpxFreeMemory (Binding, sizeof(BINDING), MEMORY_ADAPTER, "Binding"); +#endif + +} /* IpxDestroyBinding */ + + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 bindings to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + UINT BindingNum; + PBINDING Binding; + CTELockHandle LockHandle; + + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + BindingPool = (PIPX_BINDING_POOL)IpxAllocateMemory (BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + if (BindingPool == NULL) { + IPX_DEBUG (PNP, ("Could not allocate binding pool memory\n")); + return; + } + + + IPX_DEBUG (PNP, ("Initializing Binding pool %lx, %d bindings\n", + BindingPool, Device->InitBindings)); + + BindingPool->BindingCount = Device->InitBindings; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BindingNum = 0; BindingNum < BindingPool->BindingCount; BindingNum++) { + + Binding = &BindingPool->Bindings[BindingNum]; + IPX_PUSH_ENTRY_LIST (&Device->BindingList, &Binding->PoolLinkage, &Device->SListsLock); + +#ifdef IPX_TRACK_POOL + Binding->Pool = BindingPool; +#endif + } + + InsertTailList (&Device->BindingPoolList, &BindingPool->Linkage); + + Device->AllocatedBindings += BindingPool->BindingCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBindingPool */ + + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a binding from the device context's pool. + If there are no bindings in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated binding. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedBindings < Device->MaxPoolBindings) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBindingPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBinding */ +#endif + +// +// [FW] +// + +NTSTATUS +IpxOpenAdapter( + IN NIC_HANDLE AdapterIndex1, + IN ULONG FwdAdapterContext, + OUT PNIC_HANDLE IpxAdapterContext + ) + +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to open an adapter + +Arguments: + + AdapterIndex - index of the adapter to open (NICid for now - will change to a struct + with a version number, signature and the NicId + FwdAdapterContext - Forwarder's context + IpxAdapterContext - our context (for now we use the NICid - for pnp will change + this to contain a signature and version #) + +Return Value: + + STATUS_INVALID_HANDLE if the AdapterIndex handle was invalid + STATUS_ADAPTER_ALREADY_OPENED if the Adapter is being opened a second time + STATUS_SUCCESS + +--*/ + +{ + PBINDING Binding; + PDEVICE Device = IpxDevice; + USHORT AdapterIndex = AdapterIndex1.NicId; + +#if DBG + DbgPrint ("IPX: Entered IpxOpenAdapter\n"); +#endif + + // + // Return error if the AdapterIndex is out of range. + // We do indicate the slave bindings to NB/SPX (but not to RIP) + // Hence, the index should be less than HighestExternalNicId (not ValidBindings) + // + + if (AdapterIndex > Device->HighestExternalNicId) { + return STATUS_INVALID_HANDLE; + } + + + // + // Fill up our context to be returned to the Forwarder + // + NIC_HANDLE_FROM_NIC((*IpxAdapterContext), AdapterIndex); + + // + // If AdapterIndex is 0, it is for the virtual net + // BUGBUG: Will the forwarder open this at all? + // + + if (AdapterIndex == 0) { + return STATUS_SUCCESS; + } + + // + // Get the binding pointer + // + + Binding = NIC_ID_TO_BINDING(IpxDevice, AdapterIndex); + + // + // Return error if adapter is being opened a second time (or more times) + // + + if (GET_VALUE(Binding->ReferenceCount) >= 2) { + return STATUS_ADAPTER_ALREADY_OPENED; + } + + // + // Store the Forwarder's Adapter Context in the binding + // + + Binding->FwdAdapterContext = FwdAdapterContext; + + // + // Reference the Binding + // + + IpxReferenceBinding(Binding, BREF_FWDOPEN); + + return STATUS_SUCCESS; + +} + +NTSTATUS +IpxCloseAdapter( + IN NIC_HANDLE IpxAdapterContext + ) + +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to close an adapter + +Arguments: + + IpxAdapterContext - our context (for now we use the NICid - for pnp will change + this to contain a signature and version#) + +Return Value: + + STATUS_ADAPTER_ALREADY_CLOSED - if the adapter is being closed a second time + STATUS_SUCCESS + +--*/ + +{ + + PBINDING Binding; + +#if DBG + DbgPrint ("IPX: Entered IpxCloseAdapter\n"); +#endif + + Binding = NIC_ID_TO_BINDING(IpxDevice, IpxAdapterContext.NicId); + + // + // Either the adapter is around (count = 2) + // or it went away (count = 1). The latter cannot happen now. + // + + if (GET_VALUE(Binding->ReferenceCount) <= 1) { + return STATUS_ADAPTER_ALREADY_CLOSED; + } + + // + // Dereference the Binding so it can be deleted + // + + IpxDereferenceBinding(Binding, BREF_FWDOPEN); + + + // + // Clear the Forwarder's Adapter Context in the binding + // + + Binding->FwdAdapterContext = 0; + + return STATUS_SUCCESS; +} + diff --git a/private/ntos/tdi/isn/ipx/address.c b/private/ntos/tdi/isn/ipx/address.c new file mode 100644 index 000000000..c7e1e0f38 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/address.c @@ -0,0 +1,1882 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* IpxParseTdiAddress */ + + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + IpxPrint0 ("IpxValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* IpxValidateTdiAddress */ + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + None. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * IpxAddress; + + IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + + IpxAddress->TAAddressCount = 1; + IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + IpxAddress->Address[0].Address[0].NetworkAddress = Network; + IpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(IpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + +} /* IpxBuildTdiAddress */ +#endif + + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +{ + return(IpxOpenAddressM(Device, Request, 0)); +} + + + +NTSTATUS +IpxOpenAddressM( + IN PDEVICE Device, + IN PREQUEST Request, + IN ULONG Index + ) +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the IPX transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED *AddressName; + USHORT Socket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // + // If we are a dedicated router, we cannot let addresses + // be opened. + // + + if (Device->DedicatedRouter && (REQUEST_CODE(Request) != MIPX_RT_CREATE)) { + return STATUS_NOT_SUPPORTED; + } + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + IpxPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + // + + for (i=0;iTAAddressCount;i++) { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + Socket = ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + found = TRUE; + } + break; + + } else { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + + } + + } + + if (!found) { + IPX_DEBUG (ADDRESS, ("OpenAddress, request %lx has no IPX Address\n", Request)); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + if (Socket == 0) { + + Socket = IpxAssignSocket (Device); + + if (Socket == 0) { + IPX_DEBUG (ADDRESS, ("OpenAddress, no unique socket found\n")); +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOpenSocketFails); +#endif SNMP + return STATUS_INSUFFICIENT_RESOURCES; + } else { + IPX_DEBUG (ADDRESS, ("OpenAddress, assigned socket %lx\n", REORDER_USHORT(Socket))); + } + + } else { + + IPX_DEBUG (ADDRESS, ("OpenAddress, socket %lx\n", REORDER_USHORT(Socket))); + + } + + // + // get an address file structure to represent this address. + // + + AddressFile = IpxCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return status; + } + + // + // We mark this socket specially. + // + + if (Socket == SAP_SOCKET) { + AddressFile->IsSapSocket = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + + CTEGetLock (&Device->Lock, &LockHandle); + + Address = IpxLookupAddress (Device, Socket); + + if (Address == NULL) { + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // This address doesn't exist. Create it. + // registering it. + // + + Address = IpxCreateAddress ( + Device, + Socket); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + if (REQUEST_CODE(Request) == MIPX_RT_CREATE) { + Address->RtAdd = TRUE; + Address->Index = Index; + } else { + Address->RtAdd = FALSE; + } + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (Device->State == DEVICE_STATE_STOPPING) { + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Request = Request; + AddressFile->Address = Address; + + CTEGetLock (&Address->Lock, &LockHandle); + InsertTailList (&Address->AddressFileDatabase, &AddressFile->Linkage); + CTEFreeLock (&Address->Lock, LockHandle); + + AddressFile->Request = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + IpxDestroyAddressFile (AddressFile); + + } + + } else { + + CTEFreeLock (&Device->Lock, LockHandle); + + IPX_DEBUG (ADDRESS, ("Add to address %lx\n", Address)); + + // + // We never allow shared access to a RT address. So, check that + // we don't have a "RT address create" request and also that the + // address has not only been taken up by a RT Address request. If + // and only if both the above + // + if ((REQUEST_CODE(Request) != MIPX_RT_CREATE) && (!Address->RtAdd)) + { + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + CTEGetLock (&Address->Lock, &LockHandle); + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + AddressFile->Request = NULL; + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + IpxReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + CTEFreeLock (&Address->Lock, LockHandle); + + status = STATUS_SUCCESS; + + } + } + } + else + { + DbgPrint("IpxOpenAddress: ACCESS DENIED - duplicate address\n"); + status = STATUS_ACCESS_DENIED; + ExReleaseResource (&Device->AddressResource); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } + + // + // Remove the reference from IpxLookupAddress. + // + + IpxDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* IpxOpenAddress */ + + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + USHORT InitialSocket, CurrentSocket, AddressSocket; + ULONG CurrentHash; + BOOLEAN Conflict; + PLIST_ENTRY p; + PADDRESS Address; + CTELockHandle LockHandle; + + // + // Loop through all possible sockets, starting at + // Device->CurrentSocket, looking for a suitable one. + // Device->CurrentSocket rotates through the possible + // sockets to improve the chances of finding one + // quickly. + // + + CTEGetLock (&Device->Lock, &LockHandle); + + InitialSocket = Device->CurrentSocket; + Device->CurrentSocket = (USHORT)(Device->CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(Device->CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + Device->CurrentSocket = Device->SocketStart; + } + + CurrentSocket = InitialSocket; + + do { + + // + // Scan all addresses; if we find one with a socket + // that conflicts with this one, we can't use it. + // + // NOTE: Device->Lock is acquired here. + // + + Conflict = FALSE; + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + AddressSocket = REORDER_USHORT(Address->Socket); + + if ((AddressSocket + Device->SocketUniqueness > CurrentSocket) && + (AddressSocket < CurrentSocket + Device->SocketUniqueness)) { + Conflict = TRUE; + break; + } + } + + // + // If we've found a conflict, no need to check the other + // queues. + // + + if (Conflict) { + break; + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // We intentionally free the lock here so that we + // never spend too much time with it held. + // + + if (!Conflict) { + + // + // We went through the address list without + // finding a conflict; use this socket. + // + + return REORDER_USHORT(CurrentSocket); + } + + CurrentSocket = (USHORT)(CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + CurrentSocket = Device->SocketStart; + } + + CTEGetLock (&Device->Lock, &LockHandle); + + } while (CurrentSocket != InitialSocket); + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Could not find one to assign. + // + + return (USHORT)0; + +} /* IpxAssignSocket */ + + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + PIPX_SEND_RESERVED SendReserved; + PIPX_RECEIVE_RESERVED ReceiveReserved; + NDIS_STATUS Status; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + Address = (PADDRESS)IpxAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + IPX_DEBUG (ADDRESS, ("Create address %lx failed\n", REORDER_USHORT(Socket))); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address %lx (%lx)\n", Address, REORDER_USHORT(Socket))); + RtlZeroMemory (Address, sizeof(ADDRESS)); + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->SendPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail1; + } +#endif + + if (IpxInitializeSendPacket (Device, &Address->SendPacket, Address->SendPacketHeader) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail1: +#endif + Address->SendPacketInUse = TRUE; + } else { + SendReserved = SEND_RESERVED(&Address->SendPacket); + SendReserved->Address = Address; + SendReserved->OwnedByAddress = TRUE; + Address->SendPacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + + +#if BACK_FILL + { + PIPX_SEND_RESERVED BackFillReserved; + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->BackFillPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail2; + } +#endif + if (IpxInitializeBackFillPacket (Device, &Address->BackFillPacket, NULL) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail2: +#endif + Address->BackFillPacketInUse = TRUE; + } else { + BackFillReserved = SEND_RESERVED(&Address->BackFillPacket); + BackFillReserved->Address = Address; + Address->BackFillPacketInUse = FALSE; + BackFillReserved->OwnedByAddress = TRUE; +#ifdef IPX_TRACK_POOL + BackFillReserved->Pool = NULL; +#endif + } + } +#endif + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleReceivePacket(Device, &Address->ReceivePacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail3; + } +#endif + if (IpxInitializeReceivePacket (Device, &Address->ReceivePacket) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail3: +#endif + Address->ReceivePacketInUse = TRUE; + } else { + ReceiveReserved = RECEIVE_RESERVED(&Address->ReceivePacket); + ReceiveReserved->Address = Address; + ReceiveReserved->OwnedByAddress = TRUE; + Address->ReceivePacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + ReceiveReserved->Pool = NULL; +#endif + } + + Address->Type = IPX_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + Address->Socket = Socket; + Address->SendSourceSocket = Socket; + + // + // Save our local address for building datagrams quickly. + // + + RtlCopyMemory (&Address->LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + Address->LocalAddress.Socket = Socket; + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + InsertTailList (&Device->AddressDatabases[IPX_HASH_SOCKET(Socket)], &Address->Linkage); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* IpxCreateAddress */ + + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == IPX_ADDRESS_SIGNATURE) ) { + + CTEGetLock (&Address->Lock, &LockHandle); + + if (!Address->Stopping) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->Lock, LockHandle); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + IpxPrint1("IpxVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + IpxPrint1("IpxVerifyAddressFile: AF %lx exception\n", Address); + return GetExceptionCode(); + } + + return status; + +} /* IpxVerifyAddressFile */ + + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by IpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by IpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + IPX_DEBUG (ADDRESS, ("Destroy address %lx (%lx)\n", Address, REORDER_USHORT(Address->Socket))); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Address->Linkage); + CTEFreeLock (&Device->Lock, LockHandle); + + if (!Address->SendPacketInUse) { + IpxDeinitializeSendPacket (Device, &Address->SendPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->SendPacket); +#endif + } + + if (!Address->ReceivePacketInUse) { + IpxDeinitializeReceivePacket (Device, &Address->ReceivePacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleReceivePacket (Device, Address->ReceivePacket); +#endif + } + +#if BACK_FILL + if (!Address->BackFillPacketInUse) { + IpxDeinitializeBackFillPacket (Device, &Address->BackFillPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->BackFillPacket); +#endif + } +#endif + IpxFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + IpxDereferenceDevice (Device, DREF_ADDRESS); + +} /* IpxDestroyAddress */ + + +#if DBG +VOID +IpxRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddress */ + + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + // ++Address->ReferenceCount; + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddressLock */ +#endif + + +VOID +IpxDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddress */ + + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. This routine can + only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddressSync */ + + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + + CTEGetLock (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)IpxAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + IPX_DEBUG (ADDRESS, ("Create address file failed\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = IPX_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + + CTEFreeLock (&Device->Lock, LockHandle); + +#if 0 + AddressFile->SpecialReceiveProcessing = FALSE; + AddressFile->ExtendedAddressing = FALSE; + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->FilterOnPacketType = FALSE; + AddressFile->DefaultPacketType = 0; + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif +#endif + + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + AddressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + + // + // [CH] Added these handlers for chained buffer receives + // + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + AddressFile->ChainedReceiveDatagramHandler = TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + + AddressFile->RegisteredErrorHandler = FALSE; + AddressFile->ErrorHandler = TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + + return AddressFile; + +} /* IpxCreateAddressFile */ + + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by IpxDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + + IPX_DEBUG (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + CTEGetLock (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + CTEGetLock (&Device->Lock, &LockHandle1); + Address->Stopping = TRUE; + if (Device->LastAddress == Address) { + Device->LastAddress = NULL; + } + CTEFreeLock (&Device->Lock, LockHandle1); + + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + IpxFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + IpxCompleteRequest (CloseRequest); + IpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* IpxDestroyAddressFile */ + + +#if DBG +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFile */ + + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + //++AddressFile->ReferenceCount; + (VOID)InterlockedIncrement(&AddressFile->ReferenceCount); + +} /* IpxRefAddressFileLock */ + + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFileSync */ + + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFile */ + + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. This routine + can only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFileSync */ +#endif + + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + ULONG Hash = IPX_HASH_SOCKET (Socket); + + p = Device->AddressDatabases[Hash].Flink; + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->Stopping) { + continue; + } + + if (Address->Socket == Socket) { + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + IpxReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } + + } + + // + // The specified address was not found. + // + + return NULL; + +} /* IpxLookupAddress */ + + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + CTELockHandle LockHandle; + PREQUEST Request; + PADDRESS Address = AddressFile->Address; + PLIST_ENTRY p; + KIRQL irql; + + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Address->Lock, &LockHandle); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + CTEFreeLock (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + return STATUS_SUCCESS; + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!(IsListEmpty(&AddressFile->ReceiveDatagramQueue))) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Address->Lock, &LockHandle); + + } + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + +} /* IpxStopAddressFile */ + + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + // + // If this address file had broadcasts enabled, turn it off. + // + + CTEGetLock (&Device->Lock, &LockHandle); + if (AddressFile->EnableBroadcast) { + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + CTEFreeLock (&Device->Lock, LockHandle); + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* IpxCloseAddressFile */ + + diff --git a/private/ntos/tdi/isn/ipx/config.c b/private/ntos/tdi/isn/ipx/config.c new file mode 100644 index 000000000..f5d8aefbf --- /dev/null +++ b/private/ntos/tdi/isn/ipx/config.c @@ -0,0 +1,1715 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN IPX module. + +Revision History: + + Sanjay Anand (SanjayAn) 19-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxGetConfiguration) +#pragma alloc_text(INIT,IpxFreeConfiguration) + +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxGetConfigValue) +#pragma alloc_text(INIT,IpxGetBindingValue) +#pragma alloc_text(INIT,IpxGetFrameType) +#pragma alloc_text(INIT,IpxWriteDefaultAutoDetectType) +#endif + +#pragma alloc_text(INIT,IpxAddBind) +#pragma alloc_text(INIT,IpxAddExport) +#pragma alloc_text(INIT,IpxReadLinkageInformation) +#endif + + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of IPX's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ + +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Ten = 10; + ULONG Fifteen = 15; + ULONG Fifty = 50; + ULONG DefaultSocketStart = 0x4000; + ULONG DefaultSocketEnd = 0x8000; + ULONG RipSegments = RIP_SEGMENTS; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"DedicatedRouter", &Zero } , + { L"InitDatagrams", &Ten } , + { L"MaxDatagrams", &Fifty } , + { L"RipAgeTime", &Five } , // minutes + { L"RipCount", &Five } , + { L"RipTimeout", &One } , // half-second + { L"RipUsageTime", &Fifteen } , // minutes + { L"SourceRouteUsageTime", &Ten } , // minutes + { L"SocketUniqueness", &Eight } , + { L"SocketStart", &DefaultSocketStart } , + { L"SocketEnd", &DefaultSocketEnd } , + { L"VirtualNetworkNumber", &Zero } , + { L"MaxMemoryUsage", &Zero } , + { L"RipTableSize", &RipSegments } , + { L"VirtualNetworkOptional", &One } , + { L"EthernetPadToEven", &One } , + { L"EthernetExtraPadding", &Zero } , + { L"SingleNetworkActive", &Zero } , + { L"DisableDialoutSap", &Zero } , + { L"DisableDialinNetbios", &One } , + { L"VerifySourceAddress", &One } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = IpxAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + InitializeListHead (&Config->BindingList); + Config->DriverObject = DriverObject; + + // + // Read in the NDIS binding information. + // + // IpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)IpxAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + IpxFreeConfiguration(Config); + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + // + // Determine what name to export and who to bind to. + // + + Status = IpxReadLinkageInformation (Config); + if (Status != STATUS_SUCCESS) { + + // + // It logged an error if it failed. + // + + IpxFreeConfiguration(Config); + return Status; + } + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-14) Call IpxGetConfigValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = IpxGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 15) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxFreeConfiguration(Config); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // For PnP, we need to keep this path around + // +#ifndef _PNP_POWER + IpxFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); +#endif _PNP_POWER + + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* IpxGetConfiguration */ + + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get free any storage that was allocated + by IpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PBINDING_CONFIG Binding; + + while (!IsListEmpty (&Config->BindingList)) { + p = RemoveHeadList (&Config->BindingList); + Binding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + IpxFreeMemory (Binding->AdapterName.Buffer, Binding->AdapterName.MaximumLength, MEMORY_CONFIG, "NameBuffer"); + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + } + + if (Config->DeviceName.Buffer) { + IpxFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + IpxFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* IpxFreeConfiguration */ + + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 904, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Config->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetConfigValue */ + + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the NetConfig\DriverNN + node to set the per-binding values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - The index in Binding->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Binding parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Binding->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetBindingValue */ + + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues. + It is called for each of the entry in the "PktType" and + "NetworkNumber" multi-strings for a given binding. + +Arguments: + + ValueName - The name of the value ("PktType" or "NetworkNumber" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - A pointer to a count of multi-string entries. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + ULONG IntegerValue; + PWCHAR Cur; + PULONG Count = (PULONG)EntryContext; + + if ((ValueType != REG_SZ) || + (*Count >= 4)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IntegerValue = 0; + for (Cur = (PWCHAR)(ValueData); ; Cur++) { + if (*Cur >= L'0' && *Cur <= L'9') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'0'); + } else if (*Cur >= L'A' && *Cur <= L'F') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'A' + 10); + } else if (*Cur >= L'a' && *Cur <= L'f') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'a' + 10); + } else { + break; + } + } + + if (((PWCHAR)ValueName)[0] == L'P') { + + // + // PktType. We map arcnet to 802_3 so the code around + // here can assume there are only four packets type -- + // the frame type is ignored later for arcnet. + // + + if ((IntegerValue > ISN_FRAME_TYPE_ARCNET) && + (IntegerValue != ISN_FRAME_TYPE_AUTO)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("PktType(%d) is %lx\n", *Count, IntegerValue)); + if (IntegerValue == ISN_FRAME_TYPE_ARCNET) { + Binding->FrameType[*Count] = ISN_FRAME_TYPE_802_3; + } else { + Binding->FrameType[*Count] = IntegerValue; + } + + } else { + + // + // NetworkNumber + // + + IPX_DEBUG (CONFIG, ("NetworkNumber(%d) is %d\n", *Count, IntegerValue)); + Binding->NetworkNumber[*Count] = IntegerValue; + + } + + ++(*Count); + + return STATUS_SUCCESS; + +} /* IpxGetFrameType */ + + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. It + also queries the per-binding information and stores it. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PBINDING_CONFIG Binding; + PULONG CurBindNum = ((PULONG)EntryContext); + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + PWSTR ValueDataWstr = (PWSTR)ValueData; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + + Binding = (PBINDING_CONFIG)IpxAllocateMemory (sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + if (Binding == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(BINDING_CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "NameBuffer"); + if (NameBuffer == NULL) { + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Binding->AdapterName.Buffer = NameBuffer; + Binding->AdapterName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Binding->AdapterName.MaximumLength = (USHORT)ValueLength; + + Binding->DriverObject = Config->DriverObject; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + + InsertTailList (&Config->BindingList, &Binding->Linkage); + ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + + StringLoc = (ValueLength / sizeof(WCHAR)) - 2; + while (ValueDataWstr[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &ValueDataWstr[StringLoc+1], ValueLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", ValueData, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if (Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; + +} /* IpxAddBind */ + + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + IPX_DEBUG (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* IpxAddExport */ + + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call IpxAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = IpxAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 901, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + +#ifndef _PNP_POWER +// +// This will be done as and when adapters appear. +// + // + // 1) Change to call IpxAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = IpxAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&Config->BindCount; + QueryTable[1].DefaultType = REG_NONE; + + Config->BindCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + // + // For the moment fail if we find no bindings -- eventually when + // we support dynamic binding we should stick around in this case. + // + + if ((Status != STATUS_SUCCESS) || (Config->BindCount == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 902, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } +#endif + return STATUS_SUCCESS; + +} /* IpxReadLinkageInformation */ + + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ) + +/*++ + +Routine Description: + + This routine is called when we were unable to detect the default + auto-detect type and instead found a different one. We update + the "DefaultAutoDetectType" in the registry. + +Arguments: + + RegistryPath - The name of IPX's node in the registry. + + Adapter - The adapter which we auto-detected on. + + FrameType - The new auto-detected value. + +Return Value: + + None. + +--*/ + +{ + PWSTR FullRegistryPath; + PUCHAR CurRegistryPath; + ULONG FullRegistryPathLength; + ULONG AdapterNameLength; + WCHAR NetConfigName[] = L"\\NetConfig"; + static PWCHAR FrameTypeNames[4] = { L"Ethernet II", L"802.3", L"802.2", L"SNAP" }; + PWCHAR CurAdapterName; + NTSTATUS Status; + + + // + // We need to allocate a buffer which contains the registry path, + // followed by "NetConfig", followed by the adapter name, and + // then NULL-terminated. + // + + CurAdapterName = &Adapter->AdapterName[(Adapter->AdapterNameLength/sizeof(WCHAR))-2]; + while (*CurAdapterName != L'\\') { + --CurAdapterName; + } + CurAdapterName; + AdapterNameLength = Adapter->AdapterNameLength - ((CurAdapterName - Adapter->AdapterName) * sizeof(WCHAR)) - sizeof(WCHAR); + + FullRegistryPathLength = RegistryPath->Length + sizeof(NetConfigName) + AdapterNameLength; + + FullRegistryPath = (PWSTR)IpxAllocateMemory (FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + if (FullRegistryPath == NULL) { + IpxWriteResourceErrorLog( + IpxDevice->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + FullRegistryPathLength, + MEMORY_CONFIG); + return; + } + + CurRegistryPath = (PUCHAR)FullRegistryPath; + RtlCopyMemory (CurRegistryPath, RegistryPath->Buffer, RegistryPath->Length); + CurRegistryPath += RegistryPath->Length; + RtlCopyMemory (CurRegistryPath, NetConfigName, sizeof(NetConfigName) - sizeof(WCHAR)); + CurRegistryPath += (sizeof(NetConfigName) - sizeof(WCHAR)); + RtlCopyMemory (CurRegistryPath, CurAdapterName, AdapterNameLength); + CurRegistryPath += AdapterNameLength; + *(PWCHAR)CurRegistryPath = L'\0'; + + Status = RtlWriteRegistryValue( + RTL_REGISTRY_ABSOLUTE, + FullRegistryPath, + L"DefaultAutoDetectType", + REG_DWORD, + &FrameType, + sizeof(ULONG)); + + IpxFreeMemory (FullRegistryPath, FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + + IpxWriteGeneralErrorLog( + IpxDevice->DeviceObject, + EVENT_IPX_NEW_DEFAULT_TYPE, + 888, + STATUS_SUCCESS, + FrameTypeNames[FrameType], + 0, + NULL); + +} /* IpxWriteDefaultAutoDetectType */ + + +#ifdef _PNP_POWER +// +// Vnet# and VnetOptional +// +#define VIRTUAL_NETWORK_PARAMETERS 2 + +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read the virtual network number + from the registry. This is called on appearance/disappearance of an + adapter from the system. We read the registry, starting at RegistryPath, + to get the value of the VirtualNetworkNumber parameter. If it doesn't + exist, we use the default set in ipxcnfg.h file. + Adapted from IpxGetConfiguration(). + +Arguments: + + Config - Contians the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ + +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[VIRTUAL_NETWORK_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[VIRTUAL_NETWORK_PARAMETERS] = { + { L"VirtualNetworkNumber", &Zero } , + { L"VirtualNetworkOptional", &One } }; + UINT i; + + // + // Read the virtual net number from the parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2) Call IpxGetConfigValue for the virtual net number key + // + + QueryTable[1].QueryRoutine = IpxGetConfigValue; + QueryTable[1].Flags = 0; + QueryTable[1].Name = ParameterValues[0].KeyName; + QueryTable[1].EntryContext = (PVOID)CONFIG_VIRTUAL_NETWORK; + QueryTable[1].DefaultType = REG_DWORD; + QueryTable[1].DefaultData = (PVOID)(ParameterValues[0].DefaultValue); + QueryTable[1].DefaultLength = sizeof(ULONG); + + // + // 2) Call IpxGetConfigValue for the virtual net optional key + // + + QueryTable[2].QueryRoutine = IpxGetConfigValue; + QueryTable[2].Flags = 0; + QueryTable[2].Name = ParameterValues[1].KeyName; + QueryTable[2].EntryContext = (PVOID)CONFIG_VIRTUAL_OPTIONAL; + QueryTable[2].DefaultType = REG_DWORD; + QueryTable[2].DefaultData = (PVOID)(ParameterValues[1].DefaultValue); + QueryTable[2].DefaultLength = sizeof(ULONG); + + // + // 15) Stop + // + + QueryTable[3].QueryRoutine = NULL; + QueryTable[3].Flags = 0; + QueryTable[3].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* IpxPnPGetNetworkNumber */ + + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ) +/*++ + +Routine Description: + + This routine is called by IPX to read the adapter-specific parameters + from the registry on PnP appearance of an adapter in the system. + We read the registry, starting at RegistryPath\NetConfig\DeviceName. + + Adapted from IpxAddBind(). + +Arguments: + + Config - Config structure - supplies the DeviceObject and RegistryPathBuffer. + + DeviceName - name of the adapter that was added. + + Binding - Returns the configuration information per adapter. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + +// InsertTailList (&Config->BindingList, &Binding->Linkage); +// ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + StringLoc = (DeviceName->MaximumLength / sizeof(WCHAR)) - 2; + while (DeviceName->Buffer[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &DeviceName->Buffer[StringLoc+1], DeviceName->MaximumLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", DeviceName->Buffer, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if ((Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO)) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + // + // Fixed this loop which can spill over if the FrameTypeCount is 4 and the SlideCount > 0. + // Here, the FrameTypeCount is 1-based, whereas the indices are 0-based, we need to make + // the index 1-based for this to work. So, instead of (3-Binding->FrameTypeCount), we use + // (4-Binding->FrameTypeCount). This loop copies all the non-auto-detect frametypes down to + // the bottom of the array to make space after the last auto-detect frame-type for filling + // in the frametypes in the preference order. + // +#if 0 + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } +#else + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(4-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(4-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(4-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(4-Binding->FrameTypeCount)]; + } +#endif + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; +} /* IpxPnPGetAdapterParameters */ + +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isn/ipx/config.h b/private/ntos/tdi/isn/ipx/config.h new file mode 100644 index 000000000..ba6e76d83 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/config.h @@ -0,0 +1,132 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN IPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_DEDICATED_ROUTER 0 +#define CONFIG_INIT_DATAGRAMS 1 +#define CONFIG_MAX_DATAGRAMS 2 +#define CONFIG_RIP_AGE_TIME 3 +#define CONFIG_RIP_COUNT 4 +#define CONFIG_RIP_TIMEOUT 5 +#define CONFIG_RIP_USAGE_TIME 6 +#define CONFIG_ROUTE_USAGE_TIME 7 +#define CONFIG_SOCKET_UNIQUENESS 8 +#define CONFIG_SOCKET_START 9 +#define CONFIG_SOCKET_END 10 +#define CONFIG_VIRTUAL_NETWORK 11 +#define CONFIG_MAX_MEMORY_USAGE 12 +#define CONFIG_RIP_TABLE_SIZE 13 +#define CONFIG_VIRTUAL_OPTIONAL 14 +#define CONFIG_ETHERNET_PAD 15 +#define CONFIG_ETHERNET_LENGTH 16 +#define CONFIG_SINGLE_NETWORK 17 +#define CONFIG_DISABLE_DIALOUT_SAP 18 +#define CONFIG_DISABLE_DIALIN_NB 19 +#define CONFIG_VERIFY_SOURCE_ADDRESS 20 + +#define CONFIG_PARAMETERS 21 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + PWSTR RegistryPathBuffer; // path to config info + ULONG BindCount; // entries in BindingList + LIST_ENTRY BindingList; // one per binding + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +// +// These are used to index into the Parameters array in BINDING_CONFIG. +// + +#define BINDING_MAX_PKT_SIZE 0 +#define BINDING_BIND_SAP 1 +#define BINDING_DEFAULT_AUTO_DETECT 2 +#define BINDING_SOURCE_ROUTE 3 +#define BINDING_ALL_ROUTE_DEF 4 +#define BINDING_ALL_ROUTE_BC 5 +#define BINDING_ALL_ROUTE_MC 6 +#define BINDING_ENABLE_FUNC_ADDR 7 +#define BINDING_ENABLE_WAN 8 + +#define BINDING_PARAMETERS 9 + + +// +// One of these is allocated per adapter we are to bind to. +// + +typedef struct _BINDING_CONFIG { + + LIST_ENTRY Linkage; // for chaining on BindingList + NDIS_STRING AdapterName; // NDIS adapter to bind to + ULONG FrameTypeCount; // number of frame types defined (max. 4) + // == number of valid entries in arrays: + ULONG FrameType[ISN_FRAME_TYPE_MAX]; // ISN_FRAME_TYPE_XXX + ULONG NetworkNumber[ISN_FRAME_TYPE_MAX]; // may be 0 + BOOLEAN AutoDetect[ISN_FRAME_TYPE_MAX]; // remove if net number can't be found + BOOLEAN DefaultAutoDetect[ISN_FRAME_TYPE_MAX]; // use this if multiple or none found + ULONG Parameters[BINDING_PARAMETERS]; // index defined above + PDRIVER_OBJECT DriverObject; // used for logging errors + +} BINDING_CONFIG, * PBINDING_CONFIG; + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ); + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ); + +#ifdef _PNP_POWER +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ); + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ); +#endif _PNP_POWER diff --git a/private/ntos/tdi/isn/ipx/device.c b/private/ntos/tdi/isn/ipx/device.c new file mode 100644 index 000000000..f6cb2d302 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/device.c @@ -0,0 +1,612 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateDevice) +#endif + + + +VOID +IpxRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Device->ReferenceCount); + +} /* IpxRefDevice */ + + +VOID +IpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyDevice (Device); + } + +} /* IpxDerefDevice */ + + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + SegmentCount - The number of segments in the RIP router table. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG LocksOffset; + ULONG SegmentsOffset; + ULONG DeviceNameOffset; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) + + (sizeof(CTELock) * SegmentCount) + + (sizeof(ROUTER_SEGMENT) * SegmentCount) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + IPX_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject->DeviceExtension; + + IPX_DEBUG(DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + Device->DeviceObject = deviceObject; + + LocksOffset = sizeof(DEVICE); + SegmentsOffset = LocksOffset + (sizeof(CTELock) * SegmentCount); + DeviceNameOffset = SegmentsOffset + (sizeof(ROUTER_SEGMENT) * SegmentCount); + + // + // Set some internal pointers. + // + + Device->SegmentLocks = (CTELock *)(((PUCHAR)Device) + LocksOffset); + Device->Segments = (PROUTER_SEGMENT)(((PUCHAR)Device) + SegmentsOffset); + Device->SegmentCount = SegmentCount; + + for (i = 0; i < SegmentCount; i++) { + + CTEInitLock (&Device->SegmentLocks[i]); + InitializeListHead (&Device->Segments[i].WaitingForRoute); + InitializeListHead (&Device->Segments[i].FindWaitingForRoute); + InitializeListHead (&Device->Segments[i].WaitingLocalTarget); + InitializeListHead (&Device->Segments[i].WaitingReripNetnum); + InitializeListHead (&Device->Segments[i].Entries); + Device->Segments[i].EnumerateLocation = &Device->Segments[i].Entries; + + } + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "IDC1", 4); + RtlCopyMemory(Device->Signature2, "IDC2", 4); +#endif + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 0; // no sends allowed + Device->Information.MaxConnectionUserData = 0; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTIONLESS_MODE | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_ROUTE_DIRECTED; + Device->Information.MinimumLookaheadData = 128; + Device->Information.NumberOfResources = IPX_TDI_RESOURCES; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + +#if 0 + // + // These will be filled in after all the binding is done. + // + + Device->Information.MaxDatagramSize = 0; + Device->Information.MaximumLookaheadData = 0; + + + Device->SourceRoutingUsed = FALSE; + Device->SourceRoutingTime = 0; + Device->RipPacketCount = 0; + + Device->RipShortTimerActive = FALSE; + Device->RipSendTime = 0; +#endif + + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + +#ifdef _PNP_POWER + // + // Init the resource that guards the binding array/indices + // + CTEInitLock (&Device->BindAccessLock); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingRipPackets); + CTEInitTimer (&Device->RipShortTimer); + CTEInitTimer (&Device->RipLongTimer); + + CTEInitTimer (&Device->SourceRoutingTimer); + + // + // [FW] Initialize the timer used to update inactivity counters + // on WAN lines. + // + CTEInitTimer (&Device->WanInactivityTimer); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock); + CTEInitLock (&Device->Lock); + CTEInitLock (&Device->SListsLock); + + Device->ControlChannelIdentifier.QuadPart = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); +#if BACK_FILL + InitializeListHead (&Device->GlobalBackFillPacketList); +#endif + + InitializeListHead (&Device->AddressNotifyQueue); + InitializeListHead (&Device->LineChangeQueue); + + for (i = 0; i < IPX_ADDRESS_HASH_COUNT; i++) { + InitializeListHead (&Device->AddressDatabases[i]); + } + +#if BACK_FILL + InitializeListHead (&Device->BackFillPoolList); +#endif + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + +#ifdef _PNP_POWER + InitializeListHead (&Device->BindingPoolList); +#endif + + ExInitializeSListHead (&Device->SendPacketList); + ExInitializeSListHead (&Device->ReceivePacketList); +#if BACK_FILL + ExInitializeSListHead (&Device->BackFillPacketList); +#endif + +#ifdef _PNP_POWER + ExInitializeSListHead (&Device->BindingList); +#endif + +#if 0 + Device->MemoryUsage = 0; + Device->SendPacketList.Next = NULL; + Device->ReceivePacketList.Next = NULL; + Device->Bindings = NULL; + Device->BindingCount = 0; +#endif + + KeQuerySystemTime (&Device->IpxStartTime); + + Device->State = DEVICE_STATE_CLOSED; + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + Device->Type = IPX_DEVICE_SIGNATURE; + Device->Size = sizeof (DEVICE); + +#ifdef SNMP + // + // BUGBUGZZ: what are the values for these? + // + IPX_MIB_ENTRY(Device, SysInstance) = 0; + IPX_MIB_ENTRY(Device, SysExistState) = 0; +#endif SNMP + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* IpxCreateDevice */ + + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PIPX_SEND_POOL SendPool; + PIPX_SEND_PACKET SendPacket; + PIPX_RECEIVE_POOL ReceivePool; + PIPX_RECEIVE_PACKET ReceivePacket; + PIPX_ROUTE_ENTRY RouteEntry; + UINT SendPoolSize; + UINT ReceivePoolSize; + UINT i; +#if BACK_FILL + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + PIPX_SEND_PACKET BackFillPacket; +#endif + +#ifdef _PNP_POWER + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + PBINDING Binding; +#endif + + IPX_DEBUG (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the packets out of its pools. + // + +#if _PNP_POWER + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + while (!IsListEmpty (&Device->BindingPoolList)) { + + p = RemoveHeadList (&Device->BindingPoolList); + BindingPool = CONTAINING_RECORD (p, IPX_BINDING_POOL, Linkage); + IPX_DEBUG (PACKET, ("Free binding pool %lx\n", BindingPool)); + IpxFreeMemory (BindingPool, BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + } +#endif + +#ifdef IPX_OWN_PACKETS + +#if BACK_FILL + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < BackFillPool->PacketCount; i++) { + BackFillPacket = &BackFillPool->Packets[i]; + IpxDeinitializeBackFillPacket (Device, BackFillPacket); + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + IpxFreeMemory (BackFillPool, BackFillPoolSize, MEMORY_PACKET, "BackPool"); + } +#endif + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (((IPX_MAXIMUM_MAC + sizeof(IPX_HEADER) + 3) & ~3) * Device->InitDatagrams); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + IpxDeinitializeSendPacket (Device, SendPacket); + + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + IpxFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + IpxDeinitializeReceivePacket (Device, ReceivePacket); + + } + + IPX_DEBUG (PACKET, ("Free receive packet pool %lx\n", ReceivePool)); + IpxFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } +#else + +#if BACK_FILL + + while (s = IPX_POP_ENTRY_LIST(&Device->BackFillPacketList, &Device->Lock)) { + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET BackFillPacket; + + BackFillPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeBackFillPacket (Device, &BackFillPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + NdisFreePacketPool (BackFillPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (BackFillPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + } +#endif + + while (s = IPX_POP_ENTRY_LIST(&Device->SendPacketList, &Device->Lock)){ + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET SendPacket; + PUCHAR Header = Reserved->Header; + + SendPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeSendPacket (Device, &SendPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + NdisFreePacketPool (SendPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (SendPool->Header, PACKET_HEADER_SIZE * Device->InitDatagrams, MEMORY_PACKET, "SendPool"); + + IpxFreeMemory (SendPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + } + + while (s = IPX_POP_ENTRY_LIST(&Device->ReceivePacketList, &Device->Lock)){ + PIPX_RECEIVE_RESERVED Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + IPX_RECEIVE_PACKET ReceivePacket; + + ReceivePacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeReceivePacket (Device, &ReceivePacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED)); + } + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", ReceivePool)); + NdisFreePacketPool (ReceivePool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (ReceivePool, sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + } + +#endif IPX_OWN_PACKETS + // + // Destroy all rip table entries. + // + + for (i = 0; i < Device->SegmentCount; i++) { + + RouteEntry = RipGetFirstRoute(i); + while (RouteEntry != NULL) { + + (VOID)RipDeleteRoute(i, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + RouteEntry = RipGetNextRoute(i); + + } + + } + + IPX_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (IpxMemoryTag[i].BytesAllocated != 0) { + IPX_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, IpxMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice (Device->DeviceObject); + } + +} /* IpxDestroyDevice */ + diff --git a/private/ntos/tdi/isn/ipx/dirs b/private/ntos/tdi/isn/ipx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/ipx/driver.c b/private/ntos/tdi/isn/ipx/driver.c new file mode 100644 index 000000000..5db05660a --- /dev/null +++ b/private/ntos/tdi/isn/ipx/driver.c @@ -0,0 +1,4447 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 18-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include +#include +#include + + +PDEVICE IpxDevice = NULL; +PIPX_PADDING_BUFFER IpxPaddingBuffer = NULL; + +#if DBG + +UCHAR IpxTempDebugBuffer[150]; +ULONG IpxDebug = 0x0; +ULONG IpxMemoryDebug = 0xffffffd3; +UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +PUCHAR IpxDebugMemoryLoc = IpxDebugMemory[0]; +PUCHAR IpxDebugMemoryEnd = IpxDebugMemory[IPX_MEMORY_LOG_SIZE]; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (IpxTempDebugBuffer, 150); + ArgLen = vsprintf(IpxTempDebugBuffer, FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (IpxDebugMemoryLoc, 64); + RtlCopyMemory( IpxDebugMemoryLoc, IpxTempDebugBuffer, ArgLen ); + + IpxDebugMemoryLoc += 64; + if (IpxDebugMemoryLoc >= IpxDebugMemoryEnd) { + IpxDebugMemoryLoc = IpxDebugMemory[0]; + } + } +} + + +DEFINE_LOCK_STRUCTURE(IpxMemoryInterlock); +MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +DEFINE_LOCK_STRUCTURE(IpxGlobalInterlock); + +#endif + +#if DBG + +// +// Use for debug printouts +// + +PUCHAR FrameTypeNames[5] = { "Ethernet II", "802.3", "802.2", "SNAP", "Arcnet" }; +#define OutputFrameType(_Binding) \ + (((_Binding)->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) ? \ + FrameTypeNames[4] : \ + FrameTypeNames[(_Binding)->FrameType]) +#endif + + +#ifdef IPX_PACKET_LOG + +ULONG IpxPacketLogDebug = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_SEND_OTHER; +USHORT IpxPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(IpxPacketLogLock); +IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc = IpxPacketLog; +PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd = &IpxPacketLog[IPX_PACKET_LOG_LENGTH]; + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PIPX_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&IpxPacketLogLock, &LockHandle); + + PacketLog = IpxPacketLogLoc; + + ++IpxPacketLogLoc; + if (IpxPacketLogLoc >= IpxPacketLogEnd) { + IpxPacketLogLoc = IpxPacketLog; + } + *(UNALIGNED ULONG *)IpxPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&IpxPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(IPX_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, Length); + } else { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* IpxLogPacket */ + +#endif // IPX_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +// +// This is now shared with other modules +// +#ifndef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); +#endif _PNP_POWER + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) + +// +// These routines can be called at any time in case of PnP. +// +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxResolveAutoDetect) +#pragma alloc_text(INIT,IpxResolveBindingSets) +#pragma alloc_text(INIT,IpxBindToAdapter) +#endif + +#endif + +UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + +ULONG IpxFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the IPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of IPX's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UINT SuccessfulOpens, ValidBindings; +#ifdef _PNP_POWER + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("NWLNKIPX"); +#else + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("IPX Transport"); +#endif + PDEVICE Device; + PBINDING Binding; + PADAPTER Adapter; + ULONG BindingCount, BindingIndex; + PBINDING * BindingArray; + PLIST_ENTRY p; + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG Temp; + UINT i; + BOOLEAN CountedWan; + + PCONFIG Config = NULL; + PBINDING_CONFIG ConfigBinding; + +#if 0 + DbgPrint ("IPX: FailLoad at %lx\n", &IpxFailLoad); + + if (IpxFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + + // + // This ordering matters because we use it to quickly + // determine if packets are internally generated or not. + // + + CTEAssert (IDENTIFIER_NB < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_SPX < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP_INTERNAL > IDENTIFIER_IPX); + + // + // We assume that this structure is not packet in between + // the fields. + // + + CTEAssert (FIELD_OFFSET (TDI_ADDRESS_IPX, Socket) + sizeof(USHORT) == 12); + + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + + IPX_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&IpxGlobalInterlock); + CTEInitLock (&IpxMemoryInterlock); + for (i = 0; i < MEMORY_MAX; i++) { + IpxMemoryTag[i].Tag = i; + IpxMemoryTag[i].BytesAllocated = 0; + } +#endif +#ifdef IPX_PACKET_LOG + CTEInitLock (&IpxPacketLogLock); +#endif + +#ifdef IPX_OWN_PACKETS + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + IPX_DEBUG (DEVICE, ("IPX loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = IpxGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed, it logged an error. + // + + PANIC (" Failed to initialize transport, IPX initialization failed.\n"); + return status; + + } + +#ifdef _PNP_POWER + // + // Initialize the TDI layer. + // + TdiInitialize(); +#endif + + // + // make ourselves known to the NDIS wrapper. + // + + status = IpxRegisterProtocol ((PNDIS_STRING)&ProtocolName); + + if (!NT_SUCCESS (status)) { + + IpxFreeConfiguration(Config); + PANIC ("IpxInitialize: RegisterProtocol failed!\n"); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return status; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = IpxDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = IpxDispatchDeviceControl; + + DriverObject->DriverUnload = IpxUnload; + + SuccessfulOpens = 0; + + status = IpxCreateDevice( + DriverObject, + &Config->DeviceName, + Config->Parameters[CONFIG_RIP_TABLE_SIZE], + &Device); + + if (!NT_SUCCESS (status)) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return status; + } + + IpxDevice = Device; + + + // + // Save the relevant configuration parameters. + // + + Device->DedicatedRouter = (BOOLEAN)(Config->Parameters[CONFIG_DEDICATED_ROUTER] != 0); + Device->InitDatagrams = Config->Parameters[CONFIG_INIT_DATAGRAMS]; + Device->MaxDatagrams = Config->Parameters[CONFIG_MAX_DATAGRAMS]; + Device->RipAgeTime = Config->Parameters[CONFIG_RIP_AGE_TIME]; + Device->RipCount = Config->Parameters[CONFIG_RIP_COUNT]; + Device->RipTimeout = + ((Config->Parameters[CONFIG_RIP_TIMEOUT] * 500) + (RIP_GRANULARITY/2)) / + RIP_GRANULARITY; + Device->RipUsageTime = Config->Parameters[CONFIG_RIP_USAGE_TIME]; + Device->SourceRouteUsageTime = Config->Parameters[CONFIG_ROUTE_USAGE_TIME]; + Device->SocketUniqueness = Config->Parameters[CONFIG_SOCKET_UNIQUENESS]; + Device->SocketStart = (USHORT)Config->Parameters[CONFIG_SOCKET_START]; + Device->SocketEnd = (USHORT)Config->Parameters[CONFIG_SOCKET_END]; + Device->MemoryLimit = Config->Parameters[CONFIG_MAX_MEMORY_USAGE]; + Device->VerifySourceAddress = (BOOLEAN)(Config->Parameters[CONFIG_VERIFY_SOURCE_ADDRESS] != 0); + + Device->InitReceivePackets = (Device->InitDatagrams + 1) / 2; + Device->InitReceiveBuffers = (Device->InitDatagrams + 1) / 2; + + Device->MaxReceivePackets = 10; // BUGBUG: config this? + Device->MaxReceiveBuffers = 10; + + InitializeListHead(&Device->NicNtfQueue); + InitializeListHead(&Device->NicNtfComplQueue); + +#ifdef _PNP_POWER + Device->InitBindings = 5; // BUGBUG: config this? + + // + // RAS max is 240 (?) + 10 max LAN + // + Device->MaxPoolBindings = 250; // BUGBUG: config this? +#endif + +#ifdef SNMP + IPX_MIB_ENTRY(Device, SysConfigSockets) = (Device->SocketEnd - Device->SocketStart) + / ((Device->SocketUniqueness > 1) ? Device->SocketUniqueness : 1); + ; +#endif SNMP + + // + // Have to reverse this. + // +#ifndef _PNP_POWER +// +// Look at this only when the first adapter appears. +// + Temp = Config->Parameters[CONFIG_VIRTUAL_NETWORK]; + Device->VirtualNetworkNumber = REORDER_ULONG (Temp); +#endif + + Device->VirtualNetworkOptional = (BOOLEAN)(Config->Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + Device->CurrentSocket = Device->SocketStart; + + Device->EthernetPadToEven = (BOOLEAN)(Config->Parameters[CONFIG_ETHERNET_PAD] != 0); + Device->EthernetExtraPadding = (Config->Parameters[CONFIG_ETHERNET_LENGTH] & 0xfffffffe) + 1; + + Device->SingleNetworkActive = (BOOLEAN)(Config->Parameters[CONFIG_SINGLE_NETWORK] != 0); + Device->DisableDialoutSap = (BOOLEAN)(Config->Parameters[CONFIG_DISABLE_DIALOUT_SAP] != 0); + Device->DisableDialinNetbios = (UCHAR)(Config->Parameters[CONFIG_DISABLE_DIALIN_NB]); + +#ifdef _PNP_POWER +// +// Used later to access the registry. +// + Device->RegistryPathBuffer = Config->RegistryPathBuffer; + Device->RegistryPath.Length = RegistryPath->Length; + Device->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + Device->RegistryPath.Buffer = Device->RegistryPathBuffer; +#endif _PNP_POWER + + // + // ActiveNetworkWan will start as FALSE, which is correct. + // + + // + // Allocate our initial packet pool. We do not allocate + // receive and receive buffer pools until we need them, + // because in many cases we never do. + // + +#if BACK_FILL + IpxAllocateBackFillPool (Device); +#endif + + IpxAllocateSendPool (Device); + +#ifdef _PNP_POWER + IpxAllocateBindingPool (Device); +#endif + + // + // Allocate one 1-byte buffer for odd length packets. + // + + IpxPaddingBuffer = IpxAllocatePaddingBuffer(Device); + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + 801, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the loopback structures + // + IpxInitLoopback(); + +// +// All this will be done on appearance of adapters. +// + +#ifndef _PNP_POWER + + // + // Bind to all the configured adapters. + // + + InitializeListHead (&Device->InitialBindingList); + + p = Config->BindingList.Flink; + + while (p != &Config->BindingList) { + + ConfigBinding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + p = p->Flink; + + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. + // + + status = IpxBindToAdapter (Device, ConfigBinding, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (status == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + if (status != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding->AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + ++SuccessfulOpens; + + } + + } + + + IpxFreeConfiguration(Config); + + if (SuccessfulOpens == 0) { + + IpxDereferenceDevice (Device, DREF_CREATE); + + } else { + + IPX_DEFINE_SYNC_CONTEXT (SyncContext); + + // + // Allocate the device binding array and transfer those + // on the list to it. First count up the bindings. + // + + BindingCount = 0; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + Adapter->FirstWanNicId = (USHORT)(BindingCount+1); + Adapter->LastWanNicId = (USHORT)(BindingCount + Adapter->WanNicIdCount); + BindingCount += Adapter->WanNicIdCount; + } else { + ++BindingCount; + } + } + + BindingArray = (PBINDING *)IpxAllocateMemory ((BindingCount+1) * sizeof(BINDING), MEMORY_BINDING, "Binding array"); + + if (BindingArray == NULL) { + + while (!IsListEmpty (&Device->InitialBindingList)) { + p = RemoveHeadList (&Device->InitialBindingList); + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + IpxDestroyBinding (Binding); + } + + IpxDereferenceDevice (Device, DREF_CREATE); + SuccessfulOpens = 0; + goto InitFailed; + } + + RtlZeroMemory (BindingArray, (BindingCount+1) * sizeof(BINDING)); + + // + // Now walk the list transferring bindings to the array. + // + + BindingIndex = 1; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + ) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + p = p->Flink; // we overwrite the linkage in here, so save it. + + BindingArray[BindingIndex] = Binding; + Binding->NicId = (USHORT)BindingIndex; + + if (Binding->ConfiguredNetworkNumber != 0) { + + // + // If the configured network number is non-zero, then + // use it, unless we are unable to insert a rip table + // entry for it (duplicates are OK because they will + // become binding set members -- BUGBUG: What if the + // duplicate is a different media or frame type, then + // it won't get noted as a binding set). + // + + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // These are a union with the InitialLinkage fields. + // + + Binding->NextBinding = NULL; + Binding->CurrentSendBinding = NULL; + + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + CTEAssert (Adapter->FirstWanNicId == BindingIndex); + BindingIndex += Adapter->WanNicIdCount; + } else { + ++BindingIndex; + } + } + + CTEAssert (BindingIndex == BindingCount+1); + + Device->Bindings = BindingArray; + Device->BindingCount = BindingCount; + + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + status = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (status == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + for (i = 1; i <= Device->BindingCount; i++) { + + Binding = Device->Bindings[i]; + + // + // Skip empty WAN slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + status = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((status != STATUS_SUCCESS) && + (status != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + } + + ValidBindings = Device->BindingCount; + + if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, RegistryPath); + + } + + Device->ValidBindings = ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This sets Device->HighestExternalNicId + // and Device->HighestType20NicId. + // + + IpxResolveBindingSets (Device, ValidBindings); + + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // Success; see if there is a virtual network configured. + // + + if (Device->VirtualNetworkNumber != 0) { + + status = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + Device->Bindings[1]->Adapter->NdisBindingHandle, + 1); + + if (status != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (status == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + + // + // This will get set to FALSE if RIP binds. + // + + Device->RipResponder = TRUE; + + } else { + +NoVirtualNetwork: + + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + RtlCopyMemory(&Device->SourceAddress, &Device->Bindings[1]->LocalAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + } + + + // + // Now get SapNicCount -- regular adapters are counted + // as one, but all the WAN lines together only count for one. + // We also calculate FirstLanNicId and FirstWanNicId here. + // + + CountedWan = FALSE; + Device->SapNicCount = 0; + + Device->FirstLanNicId = (USHORT)-1; + Device->FirstWanNicId = (USHORT)-1; + + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + + if (Device->Bindings[i]) { + + if (Device->Bindings[i]->Adapter->MacInfo.MediumAsync) { + + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = i; + } + + if (CountedWan) { + continue; + } else { + CountedWan = TRUE; + } + + } else { + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = i; + } + + } + + } else { + + // + // NULL bindings are WANs and are not the first one, + // so don't count them. + // + + CTEAssert (Device->FirstWanNicId != -1); + CTEAssert (CountedWan); + continue; + } + + ++Device->SapNicCount; + + } + } + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = 1; + } + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = 1; + } + + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = Device->Bindings[1]->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = Device->Bindings[1]->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = Device->Bindings[1]->RealMaxDatagramSize; // smallest binding value + + if (Device->Bindings[1]->LineUp) { + LinkSpeed = Device->Bindings[1]->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = Device->Bindings[1]->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= ValidBindings; i++) { + + Binding = Device->Bindings[i]; + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = Device->Bindings[1]->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + Device->State = DEVICE_STATE_OPEN; + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + } + +InitFailed: + + if (SuccessfulOpens == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + return STATUS_DEVICE_DOES_NOT_EXIST; + + } else { + + return STATUS_SUCCESS; + } + +#else // _PNP_POWER +{ + PBIND_ARRAY_ELEM BindingArray; + PTA_ADDRESS TdiRegistrationAddress; + + // + // Pre-allocate the binding array + // Later, we will allocate the LAN/WAN and SLAVE bindings separately + // [BUGBUGZZ] Read the array size from registry? + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + Device->MaxBindings = MAX_BINDINGS - EXTRA_BINDINGS; + + // + // Allocate the TA_ADDRESS structure - this will be used in all TdiRegisterNetAddress + // notifications. + // + TdiRegistrationAddress = (PTA_ADDRESS)IpxAllocateMemory ( + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + if (TdiRegistrationAddress == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxFreeMemory(BindingArray, sizeof(BindingArray), MEMORY_BINDING, "Binding Array"); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + RtlZeroMemory (BindingArray, MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM)); + RtlZeroMemory (TdiRegistrationAddress, 2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)); + + // + // We keep BindingArray[-1] as a placeholder for demand dial bindings. + // This NicId is returned by the Fwd when a FindRoute is done on a demand + // dial Nic. At the time of the InternalSend, the true Nic is returned. + // We create a placeholder here to avoid special checks in the critical send path. + // + // NOTE: we need to free this demand dial binding as well as ensure that the + // true binding array pointer is freed at Device Destroy time. + // + // + // Increment beyond the first pointer - we will refer to the just incremented + // one as Device->Bindings[-1]. + // + BindingArray += EXTRA_BINDINGS; + + Device->Bindings = BindingArray; + + TdiRegistrationAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + TdiRegistrationAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + + // + // Store the pointer in the Device. + // + Device->TdiRegistrationAddress = TdiRegistrationAddress; + + // + // Device state is loaded, but not opened. It is opened when at least + // one adapter has appeared. + // + Device->State = DEVICE_STATE_LOADED; + + Device->FirstLanNicId = Device->FirstWanNicId = (USHORT)1; // will be changed later + + IpxFreeConfiguration(Config); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + return STATUS_SUCCESS; +} +#endif // _PNP_POWER +} /* DriverEntry */ + + +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, +#ifdef _PNP_POWER + IN CTELockHandle *LockHandle1, +#endif + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is called for auto-detect bindings to + remove any bindings that were not successfully found. + It also updates "DefaultAutoDetectType" in the registry + if needed. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + + RegistryPath - The path to the ipx registry, used if we have + to write a value back. + +Return Value: + + The updated number of bindings. + +--*/ + +{ + PBINDING Binding, TmpBinding; + UINT i, j; + + // + // Get rid of any auto-detect devices which we + // could not find nets for. We also remove any + // devices which are not the first ones + // auto-detected on a particular adapter. + // + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + if (!Binding) { + continue; + } + + // + // If this was auto-detected and was not the default, + // or it was the default, but nothing was detected for + // it *and* something else *was* detected (which means + // we will use that frame type when we get to it), + // we may need to remove this binding. + // + + if (Binding->AutoDetect && + (!Binding->DefaultAutoDetect || + (Binding->DefaultAutoDetect && + (Binding->LocalAddress.NetworkAddress == 0) && + Binding->Adapter->AutoDetectResponse))) { + + if ((Binding->LocalAddress.NetworkAddress == 0) || + (Binding->Adapter->AutoDetectFound)) { + + // + // Remove this binding. + // + + if (Binding->LocalAddress.NetworkAddress == 0) { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) no net found\n", + i, Binding->FrameType)); + } else { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) adapter already auto-detected\n", + i, Binding->FrameType)); + } + + CTEAssert (Binding->NicId == i); + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + // + // Remove any routes through this NIC, and + // adjust any NIC ID's above this one in the + // database down by one. + // + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + Binding->Adapter->Bindings[Binding->FrameType] = NULL; + for (j = i+1; j <= ValidBindings; j++) { +#ifndef _PNP_POWER + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#else + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#endif _PNP_POWER + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, NULL); +#else + Device->Bindings[ValidBindings] = NULL; +#endif + --Binding->Adapter->BindingCount; + --ValidBindings; + + --i; // so we check the binding that was just moved. + + // + // Wait 100 ms before freeing the binding, + // in case an indication is using it. + // + + KeStallExecutionProcessor(100000); + + IpxDestroyBinding (Binding); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detected OK\n", + i, Binding->FrameType)); + +#if DBG + DbgPrint ("IPX: Auto-detected non-default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + + // + // Save it in the registry for the next boot. + // +#ifdef _PNP_POWER +// +// This cannot be done at DPC, so, drop the IRQL +// + IPX_FREE_LOCK1(&Device->BindAccessLock, *LockHandle1); + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); + IPX_GET_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + } + + } else { + + if (Binding->AutoDetect) { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detect default\n", + i, Binding->FrameType)); + +#if DBG + if (Binding->LocalAddress.NetworkAddress != 0) { + DbgPrint ("IPX: Auto-detected default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); + } else { + DbgPrint ("IPX: Using default auto-detect frame type %s\n", + OutputFrameType(Binding)); + } +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) not auto-detected\n", + i, Binding->FrameType)); + } + + } + + } + + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (Binding = Device->Bindings[i]) { +#endif + CTEAssert (Binding->NicId == i); + IPX_DEBUG (AUTO_DETECT, ("Binding %lx, type %d, auto %d\n", + Binding, Binding->FrameType, Binding->AutoDetect)); + } + + } + + return ValidBindings; + +} /* IpxResolveAutoDetect */ + + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ) + +/*++ + +Routine Description: + + This routine is called to determine if we have any + binding sets and rearrange the bindings the way we + like. The order is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding, MasterBinding, TmpBinding; + UINT i, j; + ULONG WanCount, DuplicateCount; + + // + // First loop through and push all the wan bindings + // to the end. + // +#ifdef _PNP_POWER + + WanCount = Device->HighestExternalNicId - Device->HighestLanNicId; + +#else + + WanCount = 0; + + // + // For PnP, we dont do this as the bindings are in order + // at the time of insertion + // + for (i = 1; i <= (ValidBindings-WanCount); ) { + + Binding = Device->Bindings[i]; + + if ((Binding == NULL) || Binding->Adapter->MacInfo.MediumAsync) { + + // + // Put this binding at the end, and slide all the + // others down. If it is a NULL WAN binding then we + // don't have to do some of this. + // + +#if DBG + // + // Any non-NULL bindings should be correct in this + // respect at any point. + // + + if (Binding != NULL) { + CTEAssert (Binding->NicId == i); + } +#endif + + // + // If the Binding is NULL we won't have anything in the + // database at this binding, but we still need to adjust + // any NIC ID's in the database which are above this. + // + + RipAdjustForBindingChange ((USHORT)i, (USHORT)ValidBindings, IpxBindingMoved); + + // + // Slide the bindings above this down. + // + + for (j = i+1; j <= ValidBindings; j++) { + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } + + // + // Put this binding at the end. + // + + Device->Bindings[ValidBindings] = Binding; + if (Binding != NULL) { + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Binding->Adapter->FirstWanNicId == Binding->NicId)) { + Binding->Adapter->FirstWanNicId = (USHORT)ValidBindings; + Binding->Adapter->LastWanNicId += (USHORT)(ValidBindings - Binding->NicId); + } + Binding->NicId = (USHORT)ValidBindings; + } + ++WanCount; + + // + // Keep i the same, to check the new binding at + // this position. + // + + } else { + + i++; + + } + + } +#endif _PNP_POWER + // + // Now go through and find the LAN duplicates and + // create binding sets from them. + // + + DuplicateCount = 0; + + for (i = 1; i <= (ValidBindings-(WanCount+DuplicateCount)); ) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + CTEAssert (Binding != NULL); // because we are only looking at LAN bindings + + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + if (Binding->LocalAddress.NetworkAddress == 0) { + i++; + continue; + } + + // + // See if any previous bindings match the + // frame type, medium type, and number of + // this network (for the moment we match on + // frame type and medium type too so that we + // don't have to worry about different frame + // formats and header offsets within a set). + // + + for (j = 1; j < i; j++) { +#ifdef _PNP_POWER + MasterBinding = NIC_ID_TO_BINDING(Device, j); +#else + MasterBinding = Device->Bindings[j]; +#endif + if ((MasterBinding->LocalAddress.NetworkAddress == Binding->LocalAddress.NetworkAddress) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + break; + } + + } + + if (j == i) { + i++; + continue; + } + + // + // We have a duplicate. First slide it down to the + // end. Note that we change any router entries that + // use our real NicId to use the real NicId of the + // master (there should be no entries in the rip + // database that have the NicId of a binding slave). + // + + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + + for (j = i+1; j <= ValidBindings; j++) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#else + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#endif + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, Binding); +#else + Device->Bindings[ValidBindings] = Binding; +#endif + + Binding->NicId = (USHORT)ValidBindings; + ++DuplicateCount; + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %lx is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Keep i the same, to check the new binding at + // this position. + // + + } +#ifndef _PNP_POWER + Device->HighestExternalNicId = (USHORT)(ValidBindings - DuplicateCount); + Device->HighestType20NicId = (USHORT)(ValidBindings-(WanCount+DuplicateCount)); +#else + Device->HighestLanNicId -= (USHORT)DuplicateCount; + + if (Device->HighestLanNicId == 0) { + CTEAssert(FALSE); + } + + Device->HighestExternalNicId -= (USHORT)DuplicateCount; + Device->HighestType20NicId -= (USHORT)DuplicateCount; + Device->SapNicCount -= (USHORT)DuplicateCount; +#endif _PNP_POWER +} /* IpxResolveBindingSets */ + + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding, +#ifdef _PNP_POWER + IN PADAPTER *AdapterPtr, +#endif + IN ULONG FrameTypeIndex + ) + +/*++ + +Routine Description: + + This routine handles binding the transport to a new + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Device - The IPX device object. + + ConfigBinding - The configuration info for this binding. + + AdapterPtr - pointer to the adapter to bind to in case of PnP. + + FrameTypeIndex - The index into ConfigBinding's array of frame + types for this adapter. The routine is called once for + every valid frame type. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + +#ifndef _PNP_POWER + // + // Adapter came in as a parameter + // + PADAPTER Adapter = NULL; +#else + PADAPTER Adapter = *AdapterPtr; +#endif + + PBINDING Binding, OldBinding; + ULONG FrameType, MappedFrameType; + PLIST_ENTRY p; + + // + // We can't bind more than one adapter unless we have a + // virtual network configured or we are allowed to run + // with a virtual network of 0. + // + + if (Device->BindingCount == 1) { + if ((Device->VirtualNetworkNumber == 0) && + (!Device->VirtualNetworkOptional)) { + + IPX_DEBUG (ADAPTER, ("Cannot bind to more than one adapter\n")); + DbgPrint ("IPX: Disallowing multiple bind ==> VirtualNetwork is 0\n"); + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 666, + STATUS_NOT_SUPPORTED, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + return STATUS_NOT_SUPPORTED; + } + } + + + // + // First allocate the memory for the binding. + // + + status = IpxCreateBinding( + Device, + ConfigBinding, + FrameTypeIndex, + ConfigBinding->AdapterName.Buffer, + &Binding); + + if (status != STATUS_SUCCESS) { + return status; + } + + FrameType = ConfigBinding->FrameType[FrameTypeIndex]; + +// +// In PnP case, we dont need to check for existing adapters since +// we supply a NULL adapter in the parameters if it needs to be created +// +#ifndef _PNP_POWER + + // + // Check if there is already an NDIS binding to this adapter, + // and if so, that there is not already a binding with this + // frame type. + // + + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + OldBinding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + if (RtlEqualMemory( + OldBinding->Adapter->AdapterName, + ConfigBinding->AdapterName.Buffer, + OldBinding->Adapter->AdapterNameLength)) { + + Adapter = OldBinding->Adapter; + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + break; + + } + } +#endif _PNP_POWER + + if (Adapter == NULL) { + + // + // No binding to this adapter exists, so create a + // new one. + // + + status = IpxCreateAdapter( + Device, + &ConfigBinding->AdapterName, + &Adapter); + + if (status != STATUS_SUCCESS) { + IpxDestroyBinding(Binding); + return status; + } + + // + // Save these now (they will be the same for all bindings + // on this adapter). + // + + Adapter->ConfigMaxPacketSize = ConfigBinding->Parameters[BINDING_MAX_PKT_SIZE]; + Adapter->SourceRouting = (BOOLEAN)ConfigBinding->Parameters[BINDING_SOURCE_ROUTE]; + Adapter->EnableFunctionalAddress = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_FUNC_ADDR]; + Adapter->EnableWanRouter = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_WAN]; + + Adapter->BindSap = (USHORT)ConfigBinding->Parameters[BINDING_BIND_SAP]; + Adapter->BindSapNetworkOrder = REORDER_USHORT(Adapter->BindSap); + CTEAssert (Adapter->BindSap == 0x8137); + CTEAssert (Adapter->BindSapNetworkOrder == 0x3781); + + // + // Now fire up NDIS so this adapter talks + // + + status = IpxInitializeNdis( + Adapter, + ConfigBinding); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + IpxDestroyAdapter (Adapter); + IpxDestroyBinding (Binding); + + // + // Returning this status informs the caller to not + // try any more frame types on this adapter. + // + + return STATUS_DEVICE_DOES_NOT_EXIST; + + } + + // + // For 802.5 bindings we need to start the source routing + // timer to time out old entries. + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->SourceRouting)) { + + if (!Device->SourceRoutingUsed) { + + Device->SourceRoutingUsed = TRUE; + IpxReferenceDevice (Device, DREF_SR_TIMER); + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + } + } + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + IPX_DEBUG (ADAPTER, ("Create new bind to adapter %ws, type %d\n", + ConfigBinding->AdapterName.Buffer, + MappedFrameType)); + + IpxAllocateReceiveBufferPool (Adapter); + +#ifdef _PNP_POWER + *AdapterPtr = Adapter; +#endif + } +#ifdef _PNP_POWER + else { + // + // get the mapped frame type + // + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + } +#endif _PNP_POWER + + // + // The local node address starts out the same as the + // MAC address of the adapter (on WAN this will change). + // The local MAC address can also change for WAN. + // + + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Adapter->LocalMacAddress.Address, 6); + RtlCopyMemory (Binding->LocalMacAddress.Address, Adapter->LocalMacAddress.Address, 6); + + + // + // Save the send handler. + // + + Binding->SendFrameHandler = NULL; + Binding->FrameType = MappedFrameType; + + // + // BUGBUG: Put this in InitializeBindingInfo. + // + + switch (Adapter->MacInfo.RealMediumType) { + case NdisMedium802_3: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrame802_3802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_3802_2; break; + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrame802_3EthernetII; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_3Snap; break; + } + break; + case NdisMedium802_5: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_5802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_5Snap; break; + } + break; + case NdisMediumFddi: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameFddi802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrameFddi802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrameFddiSnap; break; + } + break; + case NdisMediumArcnet878_2: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameArcnet878_2; break; + } + break; + case NdisMediumWan: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrameWanEthernetII; break; + } + break; + } + + if (Binding->SendFrameHandler == NULL) { + DbgPrint ("BUGBUG!: SendFrameHandler is NULL\n"); + } + + Adapter->Bindings[MappedFrameType] = Binding; + ++Adapter->BindingCount; + + Binding->Adapter = Adapter; + +#ifndef _PNP_POWER + InsertTailList (&Device->InitialBindingList, &Binding->InitialLinkage); +#endif _PNP_POWER + + // + // NicId and ExternalNicId will be filled in later when the binding + // is assigned a spot in the Device->Bindings array. + // + + // + // Initialize the per-binding MAC information + // + + if ((Adapter->ConfigMaxPacketSize == 0) || + (Adapter->MaxSendPacketSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = Adapter->MaxSendPacketSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + Binding->MediumSpeed = Adapter->MediumSpeed; + if (Adapter->MacInfo.MediumAsync) { + Binding->LineUp = FALSE; + } else { + Binding->LineUp = TRUE; + } + + MacInitializeBindingInfo( + Binding, + Adapter); + + return STATUS_SUCCESS; + +} /* IpxBindToAdapter */ + + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ) + +/*++ + +Routine Description: + + This routine returns TRUE if the specified SourceAddress indicates + the packet was sent by us, and FALSE otherwise. + +Arguments: + + SourceAddress - The source IPX address. + +Return Value: + + TRUE if the address is local. + +--*/ + +{ + PBINDING Binding; + UINT i; + + // + // First see if it is a virtual network address or not. + // + + if (RtlEqualMemory (VirtualNode, SourceAddress->NodeAddress, 6)) { + + // + // This is us if we have a virtual network configured. + // If we don't have a virtual node, we fall through to the + // other check -- an arcnet card configured as node 1 will + // have what we think of as the "virtual node" as its + // real node address. + // + + if ((IpxDevice->VirtualNetwork) && + (IpxDevice->VirtualNetworkNumber == SourceAddress->NetworkAddress)) { + return TRUE; + } + + } + + // + // Check through our list of adapters to see if one of + // them is the source node. + // + { + ULONG Index = MIN (IpxDevice->MaxBindings, IpxDevice->ValidBindings); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + if (((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) && +#else + if (((Binding = IpxDevice->Bindings[i]) != NULL) && +#endif _PNP_POWER + (RtlEqualMemory (Binding->LocalAddress.NodeAddress, SourceAddress->NodeAddress, 6))) { + return TRUE; + } + } + } + + return FALSE; + +} /* IpxIsAddressLocal */ + + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine handles unbinding the transport from an + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Binding - The adapter to unbind. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + PADAPTER Adapter = Binding->Adapter; + + Adapter->Bindings[Binding->FrameType] = NULL; + --Adapter->BindingCount; + + IpxDereferenceBinding (Binding, BREF_BOUND); + + if (Adapter->BindingCount == 0) { + + // + // DereferenceAdapter is a NULL macro for load-only. + // + // BUGBUG: Revisit Post 4.0 + // +#ifdef _PNP_LATER + // + // Take away the creation reference. When the in-use ref is taken off, + // we destroy this adapter. + // + IpxDereferenceAdapter(Adapter); +#else + // + // Free the packet pools, etc. and close the + // adapter. + // + + IpxCloseNdis (Adapter); + + IpxDestroyAdapter (Adapter); +#endif + } + + return STATUS_SUCCESS; + +} /* IpxUnBindFromAdapter */ + + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has IPX open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + + + UNREFERENCED_PARAMETER (DriverObject); + + IpxDevice->State = DEVICE_STATE_STOPPING; + + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + + // + // Walk the list of device contexts. + // + + for (i = 1; i <= IpxDevice->BindingCount; i++) { +#ifdef _PNP_POWER + if ((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) { + INSERT_BINDING(IpxDevice, i, NULL); +#else + if (IpxDevice->Bindings[i] != NULL) { + Binding = IpxDevice->Bindings[i]; + IpxDevice->Bindings[i] = NULL; +#endif _PNP_POWER + + IpxUnBindFromAdapter (Binding); + + } + + } + + // + // Backup the pointer to free the demand dial location. + // + IpxDevice->Bindings -= EXTRA_BINDINGS; + +#ifdef _PNP_POWER + + IpxFreeMemory ( IpxDevice->Bindings, + IpxDevice->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + // + // Deallocate the TdiRegistrationAddress and RegistryPathBuffer. + // + IpxFreeMemory ( IpxDevice->TdiRegistrationAddress, + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + IpxFreeMemory ( IpxDevice->RegistryPathBuffer, + IpxDevice->RegistryPath.Length + sizeof(WCHAR), + MEMORY_CONFIG, + "RegistryPathBuffer"); + +#endif + + KeResetEvent( + &IpxDevice->UnloadEvent + ); + IpxDevice->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + IpxDereferenceDevice (IpxDevice, DREF_CREATE); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Now free the padding buffer. + // + + IpxFreePaddingBuffer (IpxDevice); + + // + // Now do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&IpxDevice->AddressResource); + IoDeleteDevice (IpxDevice->DeviceObject); + + // + // Finally, remove ourselves as an NDIS protocol. + // + + IpxDeregisterProtocol(); + +} /* IpxUnload */ + + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPX device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = IpxDevice; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PREQUEST Request; + UINT i; + ULONG Type; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + +#ifdef _PNP_POWER + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#else + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = IpxOpenAddress (Device, Request); + break; + } + + // + // Router + // + if (strncmp(openType->EaName, ROUTER_INTERFACE, + openType->EaNameLength) == 0) + { + found = TRUE; + } + + if (found) { + Status = OpenRtAddress (Device, Request); + break; + } + // + // Connection? + // + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = STATUS_NOT_SUPPORTED; + break; + } + else + { + Status = STATUS_NONEXISTENT_EA_ENTRY; + + } + + } else { + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // LowPart is in the OPEN_CONTEXT directly. + // HighPart goes into the upper 2 bytes of the OPEN_TYPE. + // + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier.LowPart); + + (ULONG)(REQUEST_OPEN_TYPE(Request)) = (Device->ControlChannelIdentifier.HighPart << 16); + (ULONG)(REQUEST_OPEN_TYPE(Request)) |= IPX_FILE_TYPE_CONTROL; + + ++(Device->ControlChannelIdentifier.QuadPart); + + if (Device->ControlChannelIdentifier.QuadPart > MAX_CCID) { + Device->ControlChannelIdentifier.QuadPart = 1; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch (Type = ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK)) { + default: + if ((Type >= ROUTER_ADDRESS_FILE) && + (Type <= (ROUTER_ADDRESS_FILE + IPX_RT_MAX_ADDRESSES))) + { + CloseRtAddress(Device, Request); + } + else + { + Status = STATUS_INVALID_HANDLE; + break; + } + + // fall through + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile->Address + // which is removed by IpxCloseAddressFile. + // + + Status = IpxVerifyAddressFile(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = IpxCloseAddressFile (Device, Request); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + IPX_DEBUG (DEVICE, ("CCID: (%d, %d)\n", ControlChannelId.HighPart, ControlChannelId.LowPart)); + + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + if (Device->UpperDriverControlChannel[i].QuadPart == + ControlChannelId.QuadPart) { + Status = IpxInternalUnbind (Device, i); + break; + } + } + + break; + } + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch (Type = ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK)) { + default: + + if ((Type >= ROUTER_ADDRESS_FILE) && + (Type <= (ROUTER_ADDRESS_FILE + IPX_RT_MAX_ADDRESSES))) + { + CleanupRtAddress(Device, Request); + } + else + { + Status = STATUS_INVALID_HANDLE; + break; + } + + + // + // fall through + // + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // Check for any line change IRPs submitted by this + // address. + // + + IpxAbortLineChanges ((PVOID)&ControlChannelId); + IpxAbortNtfChanges ((PVOID)&ControlChannelId); + + Status = STATUS_SUCCESS; + break; + } + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* IpxDispatchOpenClose */ + +#define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED ) + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + static NDIS_STRING SpxServiceName = NDIS_STRING_CONST ("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NwlnkSpx"); + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TDI_QUERY_DIRECT_SENDDG_HANDLER: { + + PULONG EntryPoint; + + // + // This is the LanmanServer trying to get the send + // entry point. + // + + IPX_DEBUG (BIND, ("Direct send entry point being returned\n")); + + EntryPoint = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + *EntryPoint = (ULONG)IpxTdiSendDatagram; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + break; + } + + case IOCTL_IPX_INTERNAL_BIND: + + // + // This is a client trying to bind. + // + + CTEAssert ((IOCTL_IPX_INTERNAL_BIND & 0x3) == METHOD_BUFFERED); + CTEAssert (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + +#ifdef _PNP_POWER + + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { +#else + if (Device->State != DEVICE_STATE_OPEN) { +#endif + Status = STATUS_INVALID_DEVICE_STATE; + + } else { + + Status = IpxInternalBind (Device, Irp); + + } + + CTEAssert (Status != STATUS_PENDING); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + case IOCTL_IPX_LOAD_SPX: + + // + // The SPX helper dll is asking us to load SPX. + // + + Status = ZwLoadDriver (&SpxServiceName); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + +#ifdef SNMP + case IOCTL_IPX_MIB_GET: { + + // + // Get the Base MIB entries out of the device. All Host-side + // entries, appearing in the MS and Novell MIBs are returned. + // + PNOVIPXMIB_BASE UserBuffer; + + UserBuffer = (PNOVIPXMIB_BASE)Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Information = sizeof(NOVIPXMIB_BASE); + + RtlCopyMemory( UserBuffer, + &Device->MibBase, + sizeof(NOVIPXMIB_BASE)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + Status = STATUS_SUCCESS; + + break; + } +#endif SNMP + + case MIPX_SEND_DATAGRAM: + MARK_REQUEST_PENDING(Irp); + Status = SendIrpFromRt (Device, Irp); + if (Status == STATUS_PENDING) { + return STATUS_PENDING; + } else { + UNMARK_REQUEST_PENDING(Irp); + REQUEST_STATUS(Irp) = Status; + IpxCompleteRequest (Irp); + IpxFreeRequest (Device, Irp); + return Status; + } + + break; + + case MIPX_RCV_DATAGRAM: + MARK_REQUEST_PENDING(Irp); + Status = RcvIrpFromRt (Device, Irp); + if (Status == STATUS_PENDING) { + return STATUS_PENDING; + } else { + UNMARK_REQUEST_PENDING(Irp); + REQUEST_STATUS(Irp) = Status; + IpxCompleteRequest (Irp); + IpxFreeRequest (Device, Irp); + return Status; + } + + break; + + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = IpxDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + return Status; + +} /* IpxDispatchDeviceControl */ + + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PREQUEST Request; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + if (Device->State == DEVICE_STATE_OPEN) { + + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); +#if DBG + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; +#endif + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (REQUEST_MINOR_FUNCTION(Request)) { + + case TDI_SEND_DATAGRAM: + Status = IpxTdiSendDatagram (DeviceObject, Request); + break; + + case TDI_ACTION: + Status = IpxTdiAction (Device, Request); + break; + + case TDI_QUERY_INFORMATION: + Status = IpxTdiQueryInformation (Device, Request); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = IpxTdiReceiveDatagram (Request); + break; + + case TDI_SET_EVENT_HANDLER: + Status = IpxTdiSetEventHandler (Request); + break; + + case TDI_SET_INFORMATION: + Status = IpxTdiSetInformation (Device, Request); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Return the immediate status code to the caller. + // + + if (Status == STATUS_PENDING) { + + return STATUS_PENDING; + + } else { + + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + return Status; + } + + } else { + + // + // The device was not open. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + +} /* IpxDispatchInternal */ + + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = IpxDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + IpxPrint1 ("IPX: Could not allocate %d: limit\n", BytesNeeded); + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' XPI'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + IpxPrint1("IPX: Could not allocate %d: no pool\n", BytesNeeded); + if (ChargeDevice) { + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; +} /* IpxpAllocateMemory */ + + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* IpxpFreeMemory */ + +#if DBG + + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = IpxpAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &IpxMemoryInterlock); + } + + return Memory; + +} /* IpxpAllocateTaggedMemory */ + + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &IpxMemoryInterlock); + + IpxpFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* IpxpFreeTaggedMemory */ + +#endif + + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry which has + a %3 value that needs to be converted to a string. It is currently + used for EVENT_TRANSPORT_RESOURCE_POOL and EVENT_IPX_INTERNAL_NET_ + INVALID. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - The transport event code. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated -- will be put in the dump data. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet and converted for use as the %3 string. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + PDEVICE Device = IpxDevice; + static WCHAR UniqueErrorBuffer[9] = L"00000000"; + UINT CurrentDigit; + INT i; + + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + i = 8; + do { + CurrentDigit = TempUniqueError & 0xf; + TempUniqueError >>= 4; + i--; + if (CurrentDigit >= 0xa) { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } while (TempUniqueError); + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR)); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer + i, sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR))); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteResourceErrorLog */ + + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceObject - Pointer to the system device object, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR DriverName[9] = L"NwlnkIpx"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceObject->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceObject->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteGeneralErrorLog */ + + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteOidErrorLog */ + + +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + Updates datagram sizes, lookahead sizes, etc. in the Device as a result + of a new binding coming in. + +Arguments: + + Device - The IPX device object. + +Return Value: + + None. + +--*/ +{ + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + IPX_GET_LOCK(&Device->BindAccessLock, &LockHandle); + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->RealMaxDatagramSize; // smallest binding value + + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->LineUp) { + LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= Device->ValidBindings; i++) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, i); + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + IPX_FREE_LOCK(&Device->BindAccessLock, LockHandle); +} + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine is called to update the binding array to + add the new bindings that appeared in this PnP event. + The order of bindings in the array is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + This routine inserts the bindings while maintaining this + order by resolving binding sets. + + The bindings are also inserted into the RIP database. + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + + Updates the SapNicCount, Device->FirstLanNicId and Device->FirstWanNicId + +Arguments: + + Device - The IPX device object. + + Adapter - The adapter added in this PnP event + + ValidBindings - the number of bindings valid for this adapter (if LAN) + +Return Value: + + None. + +--*/ +{ + ULONG i, j; + PBINDING Binding, MasterBinding; + NTSTATUS status; + + // + // Insert in proper place; if WAN, after all the WAN bindings + // If LAN, check for binding sets and insert in proper place + // Also, insert into the Rip Tables. + // + + // + // Go thru' the bindings for this adapter, inserting into the + // binding array in place + // + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + ULONG MappedFrameType; + + // + // Store in the preference order. + // Map the frame types since we could have a case where the user selects a FrameType (say, EthernetII on FDDI) + // which maps to a different FrameType (802.2). Then we would fail to find the binding in the adapter array; + // we could potentialy add a binding twice (if two frame types map to the same Frame, then we would go to the + // mapped one twice). This is taken care of by purging dups from the ConfigBinding->FrameType array when we + // create the bindings off of the Adapter (see call to IpxBindToAdapter). + // + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + ConfigBinding->FrameType[i], + &MappedFrameType); + + Binding = Adapter->Bindings[MappedFrameType]; + + if (!Binding){ + continue; + } + + CTEAssert(Binding->FrameType == MappedFrameType); + + Binding->fInfoIndicated = FALSE; + + if (Adapter->MacInfo.MediumAsync) { + PBINDING DemandDialBinding; + + // + // WAN: Place after the HighestExternalNicId, with space for WanLine # of bindings. + // Update the First/LastWanNicId. + // + Adapter->FirstWanNicId = (USHORT)Device->HighestExternalNicId+1; + Adapter->LastWanNicId = (USHORT)(Device->HighestExternalNicId + Adapter->WanNicIdCount); + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+Adapter->WanNicIdCount >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, Adapter->WanNicIdCount); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // Move Slaves down by WanNicIdCount# of entries + // + for (j = Device->ValidBindings; j > Device->HighestExternalNicId; j--) { + INSERT_BINDING(Device, j+Adapter->WanNicIdCount, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)->NicId += (USHORT)Adapter->WanNicIdCount; + } + } + + // + // Insert the WAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestExternalNicId+1, Binding); + SET_VERSION(Device, Device->HighestExternalNicId+1); + + Binding->NicId = (USHORT)Device->HighestExternalNicId+1; + + // + // Update the indices + // + Device->HighestExternalNicId += (USHORT)Adapter->WanNicIdCount; + Device->ValidBindings += (USHORT)Adapter->WanNicIdCount; + Device->BindingCount += (USHORT)Adapter->WanNicIdCount; + Device->SapNicCount++; + + // + // Since we initialize FirstWanNicId to 1, we need to compare against that. + // In case of no LAN bindings, we are fine since we have only one WAN binding initally + // (all the other WAN lines have place holders). + // + if (Device->FirstWanNicId == (USHORT)1) { + Device->FirstWanNicId = Binding->NicId; + } + + // + // Prime the DemandDial binding too. + // + + // + // First allocate the memory for the binding. + // + status = IpxCreateBinding( + Device, + NULL, + 0, + Adapter->AdapterName, + &DemandDialBinding); + + if (status != STATUS_SUCCESS) { + CTEAssert(FALSE); + } + + // + // Copy over all the values from the first WAN binding created above. + // + RtlCopyMemory(DemandDialBinding, Binding, sizeof(BINDING)); + INSERT_BINDING(Device, (SHORT)DEMAND_DIAL_ADAPTER_CONTEXT, DemandDialBinding); + DemandDialBinding->NicId = (USHORT)DEMAND_DIAL_ADAPTER_CONTEXT; + DemandDialBinding->FwdAdapterContext = INVALID_CONTEXT_VALUE; + IpxReferenceBinding(DemandDialBinding, BREF_FWDOPEN); // so it appears the FWD opened it. + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + + // + // Since WAN can have only one frame type, break + // + break; + + } else { + + Device->BindingCount++; + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+1 >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, 1); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // LAN: Figure out if it is a slave binding only for non-auto-detect bindings. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (j = 1; j < Index; j++) { + MasterBinding = NIC_ID_TO_BINDING_NO_ILOCK(Device, j); + if (MasterBinding && + (MasterBinding->ConfiguredNetworkNumber) && + (MasterBinding->ConfiguredNetworkNumber == Binding->ConfiguredNetworkNumber) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + + CTEAssert(Binding->ConfiguredNetworkNumber); + break; + } + } + } + + if (j < Device->HighestExternalNicId) { + // + // Slave binding + // + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %ws is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Place the binding after the last slave binding + // + INSERT_BINDING(Device, Device->ValidBindings+1, Binding); + SET_VERSION(Device, Device->ValidBindings+1); + + Binding->NicId = (USHORT)Device->ValidBindings+1; + + // + // Update the indices + // + Device->ValidBindings++; + + } else { + + PBINDING WanBinding=NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + + if (WanBinding) { + WanBinding->Adapter->LastWanNicId++; + WanBinding->Adapter->FirstWanNicId++; + } + + // + // Not a binding set slave binding - just add it after the last LAN binding + // + + // + // Move WAN and Slaves down by 1 entry + // + for (j = Device->ValidBindings; j > Device->HighestLanNicId; j--) { + INSERT_BINDING(Device, j+1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)->NicId++; + } + } + + // + // Increment the WAN counters in the adapter. + // + + + // + // Insert the LAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestLanNicId+1, Binding); + SET_VERSION(Device, Device->HighestLanNicId+1); + Binding->NicId = (USHORT)Device->HighestLanNicId+1; + + // + // Update the indices + // + Device->HighestLanNicId++; + Device->HighestExternalNicId++; + Device->ValidBindings++; + Device->HighestType20NicId++; + Device->SapNicCount++; + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = Binding->NicId; + } + + if (!NIC_ID_TO_BINDING(Device, (SHORT)LOOPBACK_NIC_ID)) { + PBINDING LoopbackBinding; + + // + // Prime the Loopback binding too. + // + + // + // First allocate the memory for the binding. + // + status = IpxCreateBinding( + Device, + NULL, + 0, + Adapter->AdapterName, + &LoopbackBinding); + + if (status != STATUS_SUCCESS) { + CTEAssert(FALSE); + } + + // + // Copy over all the values from the first WAN binding created above. + // + RtlCopyMemory(LoopbackBinding, Binding, sizeof(BINDING)); + INSERT_BINDING(Device, (SHORT)LOOPBACK_NIC_ID, LoopbackBinding); + LoopbackBinding->NicId = (USHORT)LOOPBACK_NIC_ID; + LoopbackBinding->FwdAdapterContext = VIRTUAL_NET_FORWARDER_CONTEXT; + IpxReferenceBinding(LoopbackBinding, BREF_FWDOPEN); // so it appears the FWD opened it. + } + } + + } + + // + // Insert this binding in the RIP Tables + // + if (Binding->ConfiguredNetworkNumber != 0) { + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + } +} /* IpxPnPUpdateBindingArray */ + + +VOID +IpxPnPToLoad() +/*++ + +Routine Description: + + This routine takes the driver to LOADED state (from OPEN) when all + PnP adapters have been removed from the machine. + +Arguments: + + None. + +Return Value: + + None. When the function returns, the driver is in LOADED state. + +--*/ + +{ + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + NTSTATUS ntStatus; + + IPX_DEBUG(PNP, ("Going back to loaded state\n")); + + // + // Inform TDI clients about the close of our device object. + // + if ((ntStatus = TdiDeregisterDeviceObject(IpxDevice->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterDeviceObject failed: %lx", ntStatus)); + } + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } +} /* IpxPnPToLoad */ + + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ) +/*++ + +Routine Description: + + This routine reallocates the binding array when the number of bindings go above + Device->MaxBindings. + +Arguments: + + Device - pointer to the device. + Size - the number of new entries required. + +Return Value: + + None. + +--*/ +{ + PBIND_ARRAY_ELEM BindingArray; + PBIND_ARRAY_ELEM OldBindingArray; + ULONG Pad=2; // extra bindings we keep around + ULONG NewSize = Size + Pad + Device->MaxBindings; + + // + // The absolute max WAN bindings. + // + CTEAssert(Size < 2048); + + // + // Re-allocate the new array + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + NewSize * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)Device->DeviceObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + + DbgPrint ("Failed to allocate memory in binding array expansion\n"); + + // + // Unload the driver here? In case of WAN, we can tolerate this failure. What about LAN? [BUGBUGZZ] + // + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory (BindingArray, NewSize * sizeof(BIND_ARRAY_ELEM)); + + // + // Backup the pointer to free the demand dial location. + // + OldBindingArray = Device->Bindings - EXTRA_BINDINGS; + + // + // Copy the old array into the new one. + // + RtlCopyMemory (BindingArray, OldBindingArray, (Device->ValidBindings+1+EXTRA_BINDINGS) * sizeof(BIND_ARRAY_ELEM)); + + // + // Free the old one. + // + IpxFreeMemory ( OldBindingArray, + (Device->MaxBindings+EXTRA_BINDINGS) * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + IPX_DEBUG(PNP, ("Expand bindarr old: %lx, new: %lx, oldsize: %lx\n", + Device->Bindings, BindingArray, Device->MaxBindings)); + + + // + // We keep BindingArray[-1] as a placeholder for demand dial bindings. + // This NicId is returned by the Fwd when a FindRoute is done on a demand + // dial Nic. At the time of the InternalSend, the true Nic is returned. + // We create a placeholder here to avoid special checks in the critical send path. + // + // NOTE: we need to free this demand dial binding as well as ensure that the + // true binding array pointer is freed at Device Destroy time. + // + // + // Increment beyond the first pointer - we will refer to the just incremented + // one as Device->Bindings[-1]. + // + BindingArray += EXTRA_BINDINGS; + + // + // Use interlocked exchange to assign this since we dont take the BindAccessLock anymore. + // + // Device->Bindings = BindingArray; + SET_VALUE(Device->Bindings, BindingArray); + + Device->MaxBindings = (USHORT)NewSize - EXTRA_BINDINGS; + + return STATUS_SUCCESS; +} +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isn/ipx/event.c b/private/ntos/tdi/isn/ipx/event.c new file mode 100644 index 000000000..a64f85d34 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/event.c @@ -0,0 +1,143 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added a new event type - TDI_EVENT_CHAINED_RECEIVE_DATAGRAM +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Request - Pointer to the request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile (AddressFile); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + CTEGetLock (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + + switch (Parameters->EventType) { + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + // + // [SA] New event handler to receive chained buffers + // + case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + } else { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredChainedReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (Parameters->EventHandler == NULL) { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + AddressFile->RegisteredErrorHandler = FALSE; + } else { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)Parameters->EventHandler; + AddressFile->ErrorHandlerContext = Parameters->EventContext; + AddressFile->RegisteredErrorHandler = TRUE; + } + + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + + } /* switch */ + + CTEFreeLock (&AddressFile->Address->Lock, LockHandle); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + REQUEST_INFORMATION(Request) = 0; + + return Status; + +} /* IpxTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isn/ipx/ind.c b/private/ntos/tdi/isn/ipx/ind.c new file mode 100644 index 000000000..c670c3a59 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/ind.c @@ -0,0 +1,4417 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ind.c + +Abstract: + + This module contains code which implements the indication handler + for the IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added IpxReceivePacket which receives buffers that can be owned + 2. Changed IpxReceiveIndication to call a new function IpxReceiveIndicationCommon + which takes an extra parameter to indicate whether this is a chained receive or + not. + 3. Changed IpxProcessDatagram to take the MDL ptr to indicate chained receive, + a client count and the headerbuffersize as params. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is declared here so it will be in the same function +// as IpxReceiveIndication and we can inline it. +// + + +#if defined(_M_IX86) +_inline +#endif +VOID +IpxProcessDatagram( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING Binding, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_DATAGRAM_OPTIONS DatagramOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast, + IN PINT pTdiClientCount, + IN UINT HeaderBufferSize, + IN PMDL pMdl, + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routing handles incoming IPX datagrams. + +Arguments: + + Device - The IPX device. + + Adapter - The adapter the frame was received on. + + Binding - The binding of the adapter it was received on. + + MacReceiveContext - The context to use when calling + NdisTransferData. + + DatagramOptions - Contains the datagram options, which + consists of room for the packet type, padding, and + the local target of the remote the frame was received from. + + LookaheadBuffer - The lookahead data. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The length of the packet, starting at the IPX + header. + + Broadcast - TRUE if the packet was broadcast. + + pTdiClientCount - to return count of the number of TDI clients above us + so NDIS can obtain that many ref counts on the buffer. + + HeaderBufferSize - the size of the MAC header buffer - used to determine + the offsets into the TSDU. + + pMdl - Mdl chain pointer - non-NULL if chained receive + + BindingContext - In case of loopback, this contains IPX_LOOPBACK_COOKIE + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)LookaheadBuffer; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PADDRESS_FILE ReferencedAddressFile; + PREQUEST Request; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_IPX UNALIGNED * DatagramAddress; + ULONG IndicateBytesCopied; + IPX_ADDRESS_EXTENDED_FLAGS SourceAddress; + ULONG SourceAddressLength; + ULONG RequestCount; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PIRP Irp; + UINT ByteOffset, BytesToTransfer; + ULONG BytesTransferred; + BOOLEAN LastAddressFile; + ULONG IndicateOffset; + PNDIS_PACKET ReceivePacket; + PIPX_RECEIVE_RESERVED Reserved; + PLIST_ENTRY p, q; + PSINGLE_LIST_ENTRY s; + USHORT DestinationSocket; + USHORT SourceSocket; + ULONG Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PIPX_DATAGRAM_OPTIONS2 Options2; + BOOLEAN RtProc = FALSE; + + // + // First scan the device's address database, looking for + // the destination socket of this frame. + // + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Address = Device->LastAddress) && + (Address->Socket == DestinationSocket)) { + + // + // Device->LastAddress cannot be stopping, so + // we use it. + // + + IpxReferenceAddressLock (Address, AREF_RECEIVE); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + + Hash = IPX_DEST_SOCKET_HASH (IpxHeader); + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if ((Address->Socket == DestinationSocket) && + (!Address->Stopping)) { + IpxReferenceAddressLock (Address, AREF_RECEIVE); + Device->LastAddress = Address; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If we had found an address we would have jumped + // past here. + // + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInUnknownSockets); +#endif SNMP + + return; + +FoundAddress: + + SourceSocket = *(USHORT UNALIGNED *)&IpxHeader->SourceSocket; + IpxBuildTdiAddress( + &SourceAddress.IpxAddress, + (*(ULONG UNALIGNED *)(IpxHeader->SourceNetwork) == 0) ? + Binding->LocalAddress.NetworkAddress : + *(UNALIGNED ULONG *)(IpxHeader->SourceNetwork), + IpxHeader->SourceNode, + SourceSocket); + + DatagramOptions->PacketType = IpxHeader->PacketType; + + + // + // Now that we have found the address, scan its list of + // address files for clients that want this datagram. + // + // If we have to release the address lock to indicate to + // a client, we reference the current address file. If + // we get an IRP we transfer the reference to that; + // otherwise we store the address file in ReferencedAddressFile + // and deref it the next time we release the lock. + // + + ReferencedAddressFile = NULL; + RequestCount = 0; + + ++Device->TempDatagramsReceived; + Device->TempDatagramBytesReceived += (PacketSize - sizeof(IPX_HEADER)); + + // + // If LastAddressFile is TRUE, it means we did an indication + // to the client on the last address file in the address' + // list, and we did not reacquire the lock when we were + // done. + // + + LastAddressFile = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + // + // If this is RtAdd, skip the entire body + // + if (!Address->RtAdd) + { + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; // next address file + } + + // + // Set these to the common values, then change them. + // + + SourceAddressLength = sizeof(TA_IPX_ADDRESS); + IndicateOffset = sizeof(IPX_HEADER); + + if (AddressFile->SpecialReceiveProcessing) { + + // + // On dial out lines, we don't indicate packets to + // the SAP socket if DisableDialoutSap is set. + // + + // + // [FW] no need to check if the FWD is bound + // + if (!Device->ForwarderBound && + (AddressFile->IsSapSocket) && + (Binding->DialOutAsync) && + (Device->DisableDialoutSap || Device->SingleNetworkActive)) { + + // + // Go to the next address file (although it will + // likely fail this test too). + // + + continue; + + } + + // + // Set this, since generally we want it. + // + + SourceAddress.PacketType = IpxHeader->PacketType; + + // + // See if we fail a packet type filter. + // + + if (AddressFile->FilterOnPacketType) { + if (AddressFile->FilteredType != IpxHeader->PacketType) { + continue; + } + } + + // + // Calculate how long the addresses expected are. + // + + if (AddressFile->ReceiveFlagsAddressing || + AddressFile->ExtendedAddressing) { + + SourceAddress.Flags = 0; + if (Broadcast) { + SourceAddress.Flags = IPX_EXTENDED_FLAG_BROADCAST; + } + if (IpxIsAddressLocal((TDI_ADDRESS_IPX UNALIGNED *) + &SourceAddress.IpxAddress.Address[0].Address[0])) { + SourceAddress.Flags |= IPX_EXTENDED_FLAG_LOCAL; + } + SourceAddressLength = sizeof(IPX_ADDRESS_EXTENDED_FLAGS); + SourceAddress.IpxAddress.Address[0].AddressLength += + (sizeof(IPX_ADDRESS_EXTENDED_FLAGS) - sizeof(TA_IPX_ADDRESS)); + + } + + // + // Determine how much of the packet the client wants. + // + + if (AddressFile->ReceiveIpxHeader) { + IndicateOffset = 0; + } + } + + // + // First scan the address' receive datagram queue + // for datagrams that match. We do a quick check + // to see if the list is empty. + // + + q = AddressFile->ReceiveDatagramQueue.Flink; + if (q != &AddressFile->ReceiveDatagramQueue) { + + do { + + Request = LIST_ENTRY_TO_REQUEST(q); + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReceiveDatagramInformation; + + if ((DatagramInformation != NULL) && + (DatagramInformation->RemoteAddress != NULL) && + (DatagramAddress = IpxParseTdiAddress(DatagramInformation->RemoteAddress)) && + (DatagramAddress->Socket != SourceSocket)) { + + // + // The address that this datagram is looking for is + // not satisfied by this frame. + // + // BUGBUG: Speed this up; worry about node and network? + // + + q = q->Flink; + continue; // next receive datagram on this address file + + } else { + + // + // We found a datagram on the queue. + // + + IPX_DEBUG (RECEIVE, ("Found RDG on %lx\n", AddressFile)); + RemoveEntryList (q); + REQUEST_INFORMATION(Request) = 0; + + goto HandleDatagram; + + } + + } while (q != &AddressFile->ReceiveDatagramQueue); + + } + + // + // If we found a datagram we would have jumped past here, + // so looking for a datagram failed; see if the + // client has a receive datagram handler registered. + // + + // + // Look for the chained receive handler if the MDL is not NULL + // + if (pMdl && AddressFile->RegisteredChainedReceiveDatagramHandler) { + + // + // Chained receive both above and below => we indicate the entire MDL up. + // Offset the LookaheadBuffer by the size of the MAC header. + // + LookaheadBufferOffset += HeaderBufferSize; + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IndicateBytesCopied = 0; + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IPX_DEBUG(RECEIVE, ("ChainedIndicate RecvLen: %d, StartOffset: %d, Tsdu: %lx\n", + PacketSize - IndicateOffset, IndicateOffset, pMdl)); + + // + // Will return SUCCESS if the client did not take ownership of the Tsdu + // PENDING if the client took ownership and will free it later (using TdiFreeReceiveChain). + // DATA_NOT_ACCEPTED if the client did not take ownership and did not copy the data. + // + + // + // Since NDIS needs an array of PNDIS_PACKETs when the TDI client returns this packet, + // we pass the Packet as the ReceiveContext here. The TDI client will pass in the address + // of this context on a ReturnPacket. + // Also, NDIS needs the PacketArray (not to be confused with the array of packetptrs. mentioned + // above) on an NdisTransferData call. These clients dont do this, but other clients like + // NB, SPX, RIP or TDI clients that do not have this new interface, can call NdisTransferData + // so we pass in the PacketArray as a parameter to them. + // + Status = (*AddressFile->ChainedReceiveDatagramHandler)( + AddressFile->ChainedReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, // TdiRcvFlags|Adapter->MacInfo.CopyLookahead, Receive datagram flags + PacketSize - IndicateOffset, // ReceiveLength + IndicateOffset+LookaheadBufferOffset, // StartingOffset + pMdl, // Tsdu - MDL chain + (PNDIS_PACKET)MacReceiveContext); // TransportContext - pointer to the packet + + if (Status != STATUS_DATA_NOT_ACCEPTED) { + + if (Status == STATUS_PENDING) { + // + // We assume here that the client referenced the packet which will + // be removed when the packet is freed. + // Increment the Tdi client count + // + (*pTdiClientCount)++; + } + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + // RequestCount should always be 0 here. + // + + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + + } else { + // + // Since no IRP can be returned here, we continue to the next addressfile + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + } + + } else if (AddressFile->RegisteredReceiveDatagramHandler) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IndicateBytesCopied = 0; + + if (PacketSize > LookaheadBufferSize) { + IPX_DEBUG(RECEIVE, ("Indicate %d/%d to %lx on %lx\n", + LookaheadBufferSize, PacketSize, + AddressFile->ReceiveDatagramHandler, AddressFile)); + } + + Status = (*AddressFile->ReceiveDatagramHandler)( + AddressFile->ReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, + LookaheadBufferSize - IndicateOffset, // indicated + PacketSize - IndicateOffset, // available + &IndicateBytesCopied, // taken + LookaheadBuffer + IndicateOffset, // data + &Irp); + + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + if (RequestCount == 0) { + return; + } + goto BreakWithoutLock; + } + + } else { + + // + // The client returned an IRP. + // + + IPX_DEBUG (RECEIVE, ("Indicate IRP %lx, taken %d\n", Irp, IndicateBytesCopied)); + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ReferencedAddressFile = AddressFile; + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + if (!LastAddressFile) { + IPX_GET_LOCK (&Address->Lock, &LockHandle); + } + + #if DBG + // + // Make sure the IRP file object is right. + // + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext != AddressFile) { + DbgPrint ("IRP %lx does not match AF %lx, H %lx C %lx\n", + Irp, AddressFile, + AddressFile->ReceiveDatagramHandler, + AddressFile->ReceiveDatagramHandlerContext); + DbgBreakPoint(); + } + #endif + // + // Set up the information field so we know + // how much to skip in it. + // + + IpxTransferReferenceAddressFile (AddressFile, AFREF_INDICATION, AFREF_RCV_DGRAM); + REQUEST_INFORMATION(Request) = IndicateBytesCopied; + + // + // Fall out of the if and continue via + // HandleDatagram... + // + + } + + } else { + + // + // No posted datagram, no handler; go to the next + // address file. + // + + continue; // next address file + + } + + HandleDatagram: + + // + // At this point, Request is set to the request + // that will hold for this address file, and + // REQUEST_INFORMATION() is the offset to start + // the transfer at. + // + + // + // First copy over the source address while it is handy. + // + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReturnDatagramInformation; + + if (DatagramInformation != NULL) { + + RtlCopyMemory( + DatagramInformation->RemoteAddress, + &SourceAddress, + (ULONG)DatagramInformation->RemoteAddressLength < SourceAddressLength ? + DatagramInformation->RemoteAddressLength : SourceAddressLength); + RtlCopyMemory( + DatagramInformation->Options, + &DatagramOptions, + (ULONG)DatagramInformation->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS) ? + DatagramInformation->OptionsLength : sizeof(IPX_DATAGRAM_OPTIONS)); + + } + + // + // Now check if this is the first request that will + // take the data, otherwise queue it up. + // + + if (RequestCount == 0) { + + // + // First one; we need to allocate a packet for the transfer. + // + + //if (Address->ReceivePacketInUse) { + if (InterlockedExchangeAdd(&Address->ReceivePacketInUse, 0) != 0) { + // + // Need a packet, check the pool. + // + + s = IpxPopReceivePacket (Device); + + if (s == NULL) { + + // + // None in pool, fail the request. + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + + } + + Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + ReceivePacket = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } else { + + // Address->ReceivePacketInUse = TRUE; + InterlockedIncrement(&Address->ReceivePacketInUse); + + ReceivePacket = PACKET(&Address->ReceivePacket); + Reserved = RECEIVE_RESERVED(&Address->ReceivePacket); + + } + + CTEAssert (IsListEmpty(&Reserved->Requests)); + + Reserved->SingleRequest = Request; + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + + ByteOffset = REQUEST_INFORMATION(Request) + LookaheadBufferOffset + IndicateOffset; + BytesToTransfer = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength; + + if (BytesToTransfer > (PacketSize - IndicateOffset)) { + BytesToTransfer = PacketSize - IndicateOffset; + } + + } else { + + if (RequestCount == 1) { + + // + // There is already one request. We need to + // allocate a buffer. + // + + s = IpxPopReceiveBuffer (Adapter); + + if (s == NULL) { + + // + // No buffers, fail the request. + // + // BUGBUG: Should we fail the transfer for the + // first request too? + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + } + + ReceiveBuffer = CONTAINING_RECORD(s, IPX_RECEIVE_BUFFER, PoolLinkage); + NdisBuffer = ReceiveBuffer->NdisBuffer; + + // + // Convert this to a queued multiple piece request. + // + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Reserved->SingleRequest)); + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = ReceiveBuffer; + + ByteOffset = LookaheadBufferOffset; + BytesToTransfer = PacketSize; + + } + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Request)); + + } + + // + // We are done setting up this address file's transfer, + // proceed to the next one. + // + + ++RequestCount; + + if (LastAddressFile) { + goto BreakWithoutLock; + } + + } + } else { + + //IpxPrint0("IpxProcessDatagram: Rt packet\n"); + if (Address->ReceivePacketInUse) { + // + // Need a packet, check the pool. + // + + s = IpxPopReceivePacket (Device); + + if (s == NULL) { + + goto BreakWithLock; + } + + + Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + ReceivePacket = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } else { + + Address->ReceivePacketInUse = TRUE; + ReceivePacket = PACKET(&Address->ReceivePacket); + Reserved = RECEIVE_RESERVED(&Address->ReceivePacket); + + } + //IpxPrint0("IpxProcessDatagram: Rt packet reserved\n"); + s = IpxPopReceiveBuffer (Adapter); + + if (s == NULL) { + + // + // No buffers, fail the request. + // + // BUGBUG: Should we fail the transfer for the + // first request too? + // + goto BreakWithLock; + } + + ReceiveBuffer = CONTAINING_RECORD(s, IPX_RECEIVE_BUFFER, PoolLinkage); + NdisBuffer = ReceiveBuffer->NdisBuffer; + Reserved->ReceiveBuffer = ReceiveBuffer; + ByteOffset = LookaheadBufferOffset; + BytesToTransfer = PacketSize; + //IpxPrint0("IpxProcessDatagram: Rt packet buffer reserved\n"); + RtProc = TRUE; + Reserved->Index = Address->Index; + + } + +BreakWithLock: + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + +BreakWithoutLock: + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + + // + // We can be transferring directly into a request's buffer, + // transferring into an intermediate buffer, or not + // receiving the packet at all. + // + + if (RequestCount > 0 || RtProc) { + + if (RtProc) { + Reserved->pContext = IpxAllocateMemory(sizeof(IPX_DATAGRAM_OPTIONS2), MEMORY_PACKET, "RT Options"); + if (!Reserved->pContext) { + + ASSERTMSG("Out of resources\n", 1); + goto GetOut; + } else { + //IpxPrint1("IpxProcessDatagram: Nic Id is (%d)\n", DatagramOptions->LocalTarget.NicId); + RtlCopyMemory( + &((PIPX_DATAGRAM_OPTIONS2)(Reserved->pContext))->DgrmOptions, + DatagramOptions, + sizeof(IPX_DATAGRAM_OPTIONS)); + //IpxPrint1("IpxProcessDatagram: Nic Id is (%d)\n", + // ((PIPX_DATAGRAM_OPTIONS2)(Reserved->pContext))->DgrmOptions.LocalTarget.NicId); + } + } else { + Reserved->pContext = NULL; + } + + // + // If this is true, then ReceivePacket, Reserved, + // and NdisBuffer are all set up correctly. + // + + CTEAssert (ReceivePacket); + CTEAssert (Reserved == (PIPX_RECEIVE_RESERVED)(ReceivePacket->ProtocolReserved)); + + + NdisChainBufferAtFront(ReceivePacket, NdisBuffer); + + IPX_DEBUG (RECEIVE, ("Transfer into %lx, offset %d bytes %d\n", + NdisBuffer, ByteOffset, BytesToTransfer)); + + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("Loopback Copy from packet: %lx to packet: %lx\n", ReceivePacket, MacReceiveContext)); + + NdisCopyFromPacketToPacket( + ReceivePacket, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset - loopback packet + &BytesTransferred); // BytesCopied + + NdisStatus = NDIS_STATUS_SUCCESS; + + } else { + NdisTransferData( + &NdisStatus, + Adapter->NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + ReceivePacket, + &BytesTransferred); + } + + if (NdisStatus != NDIS_STATUS_PENDING) { + + IpxTransferDataComplete( + (NDIS_HANDLE)Adapter, + ReceivePacket, + NdisStatus, + BytesTransferred); + } + } +#ifdef SNMP + else { + ++IPX_MIB_ENTRY(Device, SysInUnknownSockets); + } +#endif SNMP + +GetOut: + + IpxDereferenceAddressSync (Address, AREF_RECEIVE); + +} /* IpxProcessDatagram */ + + + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + // + // Call the actual receive indication handler and indicate that this is not a + // chained receive + // + + return IpxReceiveIndicationCommon ( + BindingContext, + ReceiveContext, // ReceiveContext + HeaderBuffer, + HeaderBufferSize, + LookaheadBuffer, + LookaheadBufferSize, + PacketSize, // PacketSize + NULL, // pMdl - non-NULL => chained receive. + NULL // pTdiClientCount - used in chained recv case to keep count of TDI clients + ); + +} + + +NDIS_STATUS +IpxReceiveIndicationCommon( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + + pMdl - pointer to MDL chain if chained, NULL if this came from indication. + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + + IPX_DATAGRAM_OPTIONS DatagramOptions; + PADAPTER Adapter = (PADAPTER)BindingContext; + PBINDING Binding; + PDEVICE Device = IpxDevice; + PUCHAR Header = (PUCHAR)HeaderBuffer; + PUCHAR Lookahead = (PUCHAR)LookaheadBuffer; + ULONG PacketLength; + UINT IpxPacketSize; + ULONG Length802_3; + USHORT Saps; + ULONG DestinationNetwork; + ULONG SourceNetwork; + PUCHAR DestinationNode; + USHORT DestinationSocket; + ULONG IpxHeaderOffset; + PIPX_HEADER IpxHeader; + UINT i; + BOOLEAN IsBroadcast; + BOOLEAN IsLoopback = FALSE; +#if DBG + PUCHAR DestMacAddress; + ULONG ReceiveFlag; +#endif + BOOLEAN fCallProcessDatagram = FALSE; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif _PNP_POWER + + // + // Reject packets that are too short to hold even the + // basic IPX header (this ignores any extra 802.2 etc. + // headers but is good enough because a runt will fail + // the IPX header packet length check). + // + + if (PacketSize < sizeof(IPX_HEADER)) { +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + ++IPX_MIB_ENTRY(Device, SysInHdrErrors); +#endif SNMP + return STATUS_SUCCESS; + } + + // + // If this is a loopback packet, no need to do figure out the + // MAC header. + // + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + +#ifdef _PNP_POWER + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, 1); + + if (!Binding) { + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); + + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, LOOPBACK_NIC_ID); +#else + if ((Binding = IpxDevice->Bindings[1]) == NULL) { + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + DatagramOptions.LocalTarget.NicId = 0; +#endif + + // + // Do this copy later, from the IpxHeader. + // + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->Adapter->MacInfo.MediumType == NdisMedium802_5) { + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + } + + // + // Ipx header starts at the top of the LookAheadBuffer + // + IpxHeaderOffset = 0; + + IPX_DEBUG (LOOPB, ("Loopback packet received: %lx\n", ReceiveContext)); + +#if DBG + DestMacAddress = DatagramOptions.LocalTarget.MacAddress; +#endif + + IsLoopback = TRUE; + goto Loopback; + } + +#ifdef _PNP_POWER + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); +#endif + + // + // The first step is to construct the 8-byte local + // target from the packet. We store it in the 9-byte + // datagram options, leaving one byte at the front + // for use by IpxProcessDatagram when indicating to + // its TDI clients. + // + +#if DBG + Binding = NULL; +#endif + + if (Adapter->MacInfo.MediumType == NdisMedium802_3) { + + // + // Try to figure out what the packet type is. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Header[12] < 0x06) { + + // + // An 802.3 header; check the next bytes. They may + // be E0/E0 (802.2), FFFF (raw 802.3) or A0/A0 (SNAP). + // + + Saps = *(UNALIGNED USHORT *)(Lookahead); + + if (Saps == 0xffff) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 0; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + + } else if (Saps == 0xe0e0) { + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 3; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 8; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + } + + goto NotValid802_3; + + } else { + + // + // It has an ethertype, see if it is ours. + // + + if (*(UNALIGNED USHORT *)(Header+12) == Adapter->BindSapNetworkOrder) { + + if (Adapter->MacInfo.MediumAsync) { + + *((ULONG UNALIGNED *)(&Binding)) = *((ULONG UNALIGNED *)(&Header[2])); + + CTEAssert(Binding != NULL); + + if ((Binding != NULL) && + (Binding->LineUp)) { + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + + // + // Check if this is a type 20 packet and + // we are disabling them on dialin lines -- we do + // this check here to avoid impacting the main + // indication path for LANs. + // + // The 0x02 bit of DisableDialinNetbios controls + // WAN->LAN packets, which we handle here. + // + + // + // [FW] If FWD bound, no need to check since the FWD does the checks + // + if (!Device->ForwarderBound && + (!Binding->DialOutAsync) && + ((Device->DisableDialinNetbios & 0x02) != 0)) { + + IpxHeader = (PIPX_HEADER)Lookahead; // IpxHeaderOffset is 0 + if (IpxHeader->PacketType == 0x14) { +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + return STATUS_SUCCESS; + } + } + + goto Valid802_3; + } + goto NotValid802_3; + + } else if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_ETHERNET_II]) == NULL) { + goto NotValid802_3; + } + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + goto Valid802_3; + + } + } + + goto NotValid802_3; + +Valid802_3: + + if (Length802_3 > PacketSize) { + goto NotValid802_3; + } else if (Length802_3 < PacketSize) { + PacketSize = Length802_3; + if (LookaheadBufferSize > Length802_3) { + LookaheadBufferSize = Length802_3; + } + } + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+6, 6); +#if DBG + DestMacAddress = Header; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMedium802_5) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_5; + } + + IpxHeaderOffset = 3; + goto Valid802_5; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_5; + } + IpxHeaderOffset = 8; + goto Valid802_5; + } + } + + goto NotValid802_5; + +Valid802_5: +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+8, 6); + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + +#if DBG + DestMacAddress = Header+2; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMediumFddi) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 3; + goto ValidFddi; + } + + } else if (Saps == 0xffff) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 0; + goto ValidFddi; + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 8; + goto ValidFddi; + } + } + + goto NotValidFddi; + +ValidFddi: + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+7, 6); + +#if DBG + DestMacAddress = Header+1; +#endif + + + } else { + + // + // NdisMediumArcnet878_2 + // + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + if ((Header[2] == ARCNET_PROTOCOL_ID) && + ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) != NULL)) { + + IpxHeaderOffset = 0; + RtlZeroMemory (DatagramOptions.LocalTarget.MacAddress, 5); + DatagramOptions.LocalTarget.MacAddress[5] = Header[0]; + + } else { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+1, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + } + +#if DBG + DestMacAddress = Header+2; // BUGBUG Need to log less than six bytes +#endif + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + + // + // Make sure this didn't slip through. + // + + CTEAssert (Binding != NULL); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->NicId; +#endif + +Loopback: + + // + // Now that we have validated the header and constructed + // the local target, indicate the packet to the correct + // client. + // + + IpxHeader = (PIPX_HEADER)(Lookahead + IpxHeaderOffset); + + PacketLength = (IpxHeader->PacketLength[0] << 8) | IpxHeader->PacketLength[1]; + + IpxPacketSize = PacketSize - IpxHeaderOffset; + + if (PacketLength > IpxPacketSize) { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, DestMacAddress, DatagramOptions.LocalTarget.MacAddress, (USHORT)PacketSize, IpxHeader, IpxHeader+1); + } +#endif + IPX_DEBUG (BAD_PACKET, ("Packet len %d, IPX len %d\n", + PacketLength, IpxPacketSize)); + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + +#endif SNMP + + return NDIS_STATUS_SUCCESS; + + } else if (PacketLength < IpxPacketSize) { + + IpxPacketSize = PacketLength; + if (LookaheadBufferSize > (PacketLength + IpxHeaderOffset)) { + LookaheadBufferSize = PacketLength + IpxHeaderOffset; + } + + } + + // + // Bug #33595 - (hotfixed in 3.51, checked into 4.0 beta2) + // Customer problem where NT allowed RIP/SAP to reply to an 802.5 functional address in the IPX source node. The source + // MAC address was proper in this case. We need to check for the case where if the packet's source network is the same + // as that of the binding it came on (=> did not come thru a router), then the SourceNodeAddress in the IPX header + // should be equal to the SourceAddress in the MAC header. + // + // This check is controlled through a registry value - VerifySourceAddress. + // In case of Arcnet, this check will not succeed. + // Also, for WAN, the node addresses will not match, so avoid check for those. + + // + // If the source network is 0, we drop it. Auto-detect frames should have matching node (MAC) addresses. + // Loopback packets dont have a valid header, so skip this test for them. + // + // BUGBUG: For loopback pkts, do all the processing above, so we can avoid all these checks for IsLoopback here. + // Also, to prevent the RtlCopyMemory into the localtarget above, try to use the MAC header to indicate the + // correct binding to us so we dont use the first one always. + // + // CAVEAT:: when using the MAC header as a binding pointer, ensure that we use the adapter corresp, to that binding + // to enque all the receive requests. currently we enqueue them onto the first bindings adapter. + // + if (((*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == Binding->LocalAddress.NetworkAddress) || + (*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == 0)) && + (!IPX_NODE_EQUAL (IpxHeader->SourceNode, DatagramOptions.LocalTarget.MacAddress)) && + Device->VerifySourceAddress && + !IsLoopback && + !Adapter->MacInfo.MediumAsync && + (Adapter->MacInfo.MediumType != NdisMediumArcnet878_2)) { + + IPX_DEBUG(BAD_PACKET, ("Local packet: Src MAC %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x ", + DatagramOptions.LocalTarget.MacAddress[0], + DatagramOptions.LocalTarget.MacAddress[1], + DatagramOptions.LocalTarget.MacAddress[2], + DatagramOptions.LocalTarget.MacAddress[3], + DatagramOptions.LocalTarget.MacAddress[4], + DatagramOptions.LocalTarget.MacAddress[5])); + + IPX_DEBUG(BAD_PACKET, ("IPX Src Node %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + +#ifdef IPX_PACKET_LOG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + +#endif SNMP + + return NDIS_STATUS_SUCCESS; + } + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + // + // In order to have consistent local targets, copy over the target from the IpxHeader. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet copied the localtarget: %lx\n", IpxHeader->DestinationNode)); + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + *((UNALIGNED ULONG *)DatagramOptions.LocalTarget.MacAddress) = + *((UNALIGNED ULONG *)IpxHeader->DestinationNode); + + *((UNALIGNED USHORT *)(DatagramOptions.LocalTarget.MacAddress+4)) = + *((UNALIGNED USHORT *)(IpxHeader->DestinationNode+4)); + } + + ++Device->Statistics.PacketsReceived; + + DestinationNode = IpxHeader->DestinationNode; + + if (DestinationSocket != RIP_SOCKET) { + + DestinationNetwork = *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork; + +RecheckPacket: + + if (Device->MultiCardZeroVirtual) { + + if ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + } else { + + if ((DestinationNetwork == Device->SourceAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Device->SourceAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + // + // We need to check for frames that are sent to the + // binding node and net, because if we have a virtual + // net we won't catch them in the check above. This + // will include any Netbios frames, since they don't + // use the virtual net. Doing the check like this will slow + // down netbios indications just a bit on a machine with + // a virtual network, but it saves a jump for other traffic + // vs. adding the check up there (the assumption is if we + // have a virtual net most traffic is NCP). + // + // Note that IsBroadcast is already set, so we don't have + // to do that. + // + + if ((Device->VirtualNetwork) && + ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0))) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + goto DestinationOk; + } else { + if (IsBroadcast && (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + } + } + + // + // If this was a loopback packet that was sent on the second binding (but showed back up on the first one), + // then the networknumbers will not match. Allow the receive on the first binding itself. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet forced on first binding: %lx\n", ReceiveContext)); + goto DestinationOk; + } + + // + // If we did not receive this packet, it might be because + // our network is still 0 and this packet was actually + // sent to the real network number. If so we try to + // update our local address, and if successful we + // re-check the packet. We don't insert if we are + // not done with auto detection, to avoid colliding + // with that. + // + // To avoid problems if we are a router, we only update + // on packets that are broadcast or sent to us. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + (Device->AutoDetectState == AUTO_DETECT_STATE_DONE) && + (DestinationNetwork != 0) && + (IsBroadcast || + IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress))) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + DestinationNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d reconfigured to network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + // + // Jump back and re-process the packet; we know + // we won't loop through here again because the + // binding's network is now non-zero. + // + + goto RecheckPacket; + + } + } + + + // + // The only frames that will not already have jumped to + // DestinationOk are those to or from the SAP socket, + // so we check for those. + // + + if ((*(USHORT UNALIGNED *)&IpxHeader->SourceSocket == SAP_SOCKET) || + (DestinationSocket == SAP_SOCKET)) { + +DestinationOk: + + // + // [FW] For internally destined packets, call the Forwarder's internal + // receive handler to filter the packets. + // + // STEFAN: 3/28/96: + // Dont filter IPXWAN config packets since the FWD does not have this adapter opened. + // + + IPX_DEBUG(RECEIVE, ("DestSocket: %lx\n", DestinationSocket)); + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInDelivers); +#endif SNMP + + if (DestinationSocket != IPXWAN_SOCKET && + Device->ForwarderBound) { + + NDIS_STATUS NdisStatus; + + IPX_DEBUG(RECEIVE, ("Internal packet, sending up to the Forwarder\n")); + + // + // We should pass up the correct Fwd ctx for loopback ptks + // + + // + // Indicate this up only if the adapter the packet came on was opened by the Forwarder + // + if (GET_VALUE(Binding->ReferenceCount) == 2) { + + NdisStatus = (*Device->UpperDrivers[IDENTIFIER_RIP].InternalReceiveHandler) ( + (IsLoopback) ? + VIRTUAL_NET_FORWARDER_CONTEXT : + Binding->FwdAdapterContext, // ForwarderAdapterContext + &DatagramOptions.LocalTarget, // Remote Address + (PUCHAR) IpxHeader, // Lookahead buffer + LookaheadBufferSize - IpxHeaderOffset // Lookahead buffer size + ); + + IPX_DEBUG(RECEIVE, ("Internal packet, Forwarder returned %lx\n", NdisStatus)); + + if (NdisStatus != STATUS_SUCCESS) { + // + // BUGBUG: Log this packet + // + IPX_DEBUG(RECEIVE, ("Internal packet, failed the filter: ipxheader: %lx\n", IpxHeader)); + + // + // The router needs to see Netbios type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast) && + (!fCallProcessDatagram)) { + + goto RipIndication; + } + // else + return NDIS_STATUS_SUCCESS; + } + + } else { + IPX_DEBUG(RECEIVE, ("Internal packet, Forwarder has not opened the adapter yet\n")); + return NDIS_STATUS_SUCCESS; + } + } + + // + // An IPX packet sent to us, or a SAP packet (which + // are not sent to the virtual address but still need + // to be indicated and not forwarded to RIP). + // + + if (DestinationSocket == NB_SOCKET) { +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_NB | IPX_PACKET_LOG_RCV_ALL; +#endif + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_NB].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_NB])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_NB, Adapter, Header, HeaderBufferSize); + } + + // + // We add HeaderBufferSize to the IpxHeaderOffset field since we do an NdisCopyFromPacketToPacket + // in IpxTransferData, which needs offset from the beginning of the packet. + // NdisTransferData adds the offset passed in to the beginning of the IPX packet. + // + if ((*Device->UpperDrivers[IDENTIFIER_NB].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + Binding->FwdAdapterContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize, + pMdl)) { + + CTEAssert(FALSE); + (*pTdiClientCount)++; + } + + Device->ReceiveCompletePending[IDENTIFIER_NB] = TRUE; + } + + // + // The router needs to see Netbios type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast) && + (!fCallProcessDatagram)) { + goto RipIndication; + } + + } else if (IpxHeader->PacketType == SPX_PACKET_TYPE) { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_SPX | IPX_PACKET_LOG_RCV_ALL; +#endif + + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_SPX].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_SPX])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_SPX, Adapter, Header, HeaderBufferSize); + } + + if ((*Device->UpperDrivers[IDENTIFIER_SPX].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + Binding->FwdAdapterContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize, + pMdl)) { + + CTEAssert(FALSE); + (*pTdiClientCount)++; + } + + Device->ReceiveCompletePending[IDENTIFIER_SPX] = TRUE; + } + + } else { + + IPX_DEBUG (RECEIVE, ("Received packet type %d, length %d\n", + Binding->FrameType, + IpxPacketSize)); + IPX_DEBUG (RECEIVE, ("Source %lx %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(USHORT UNALIGNED *)&IpxHeader->SourceSocket, + IpxHeader->SourceNetwork[0], + IpxHeader->SourceNetwork[1], + IpxHeader->SourceNetwork[2], + IpxHeader->SourceNetwork[3], + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + IPX_DEBUG (RECEIVE, ("Destination %d %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + DestinationSocket, + IpxHeader->DestinationNetwork[0], + IpxHeader->DestinationNetwork[1], + IpxHeader->DestinationNetwork[2], + IpxHeader->DestinationNetwork[3], + IpxHeader->DestinationNode[0], + IpxHeader->DestinationNode[1], + IpxHeader->DestinationNode[2], + IpxHeader->DestinationNode[3], + IpxHeader->DestinationNode[4], + IpxHeader->DestinationNode[5])); + +#if DBG + if (IpxHeader->DestinationSocket == IpxPacketLogSocket) { + ReceiveFlag = IPX_PACKET_LOG_RCV_SOCKET | IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } else { + ReceiveFlag = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } +#endif + + // + // Fiddle with this if so in the general case + // the jump is not made (BUGBUG the compiler + // still rearranges it). + // + + if (Adapter->MacInfo.MediumType != NdisMedium802_5) { + +CallProcessDatagram: + // + // [SA] Returns a status now which needs to be returned to NDIS + // Also, MDL is passed in. + // We need to pass in the HeaderBufferSize too.... + // + IpxProcessDatagram( + Device, + Adapter, + Binding, + ReceiveContext, + &DatagramOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, // lookaheadbufferoffset + IpxPacketSize, + IsBroadcast, + pTdiClientCount, + HeaderBufferSize, + pMdl, + BindingContext); + + } else { + if (!IsLoopback) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + goto CallProcessDatagram; + } + + // + // The router needs to see type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast) && + (!fCallProcessDatagram)) { + goto RipIndication; + } + } + + } else { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // We need to let non-type 20 broadcast frames go to RIP to allow for lan-specific + // broadcasts. For logon over IPX, this allows the logon request to get thru the WAN + // line. + // + // if ( !IsBroadcast ) { + +RipIndication:; + + if (Device->ForwarderBound) { + // + // FWD .... + // + + if (DestinationSocket == RIP_SOCKET) { + // + // [FW] Since RIP is now a user app with the same socket #, we inform thru' + // the ProcessDatagram path. And only if the Forwarder is installed. + // + + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + fCallProcessDatagram = TRUE; + goto CallProcessDatagram; + } else { + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_RIP, Adapter, Header, HeaderBufferSize); + } + + // + // We hide binding sets from the router, to avoid + // misordering packets which it routes. + // + + if (!IsLoopback && Binding->BindingSetMember) { + #ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN (Device->MaxBindings, Binding->MasterBinding->NicId)); + #else + DatagramOptions.LocalTarget.NicId = Binding->MasterBinding->NicId; + #endif + } + + if (GET_VALUE(Binding->ReferenceCount) == 2) { + if ((*Device->UpperDrivers[IDENTIFIER_RIP].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + Binding->FwdAdapterContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize, + pMdl)) { + + (*pTdiClientCount)++; + } + + Device->ReceiveCompletePending[IDENTIFIER_RIP] = TRUE; + } else { + IPX_DEBUG(RECEIVE, ("External packet, Forwarder has not opened the adapter yet\n")); + } + } + } else if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + // + // Old RIP... + // + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_RIP, Adapter, Header, HeaderBufferSize); + } + + // + // We hide binding sets from the router, to avoid + // misordering packets which it routes. + // + + if (!IsLoopback && Binding->BindingSetMember) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN (Device->MaxBindings, Binding->MasterBinding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->MasterBinding->NicId; +#endif + } + + + if ((*Device->UpperDrivers[IDENTIFIER_RIP].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + Binding->FwdAdapterContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize, + pMdl)) { + + CTEAssert(FALSE); + (*pTdiClientCount)++; + } + + Device->ReceiveCompletePending[IDENTIFIER_RIP] = TRUE; + } + // } + } + + } else { + + if ((Binding->ReceiveBroadcast) || + (!IPX_NODE_BROADCAST(IpxHeader->DestinationNode))) { + + SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork; + + // + // Sent to the RIP socket; check if this binding needs a + // network number. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + ((SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork) != 0)) { + + switch (Device->AutoDetectState) { + + case AUTO_DETECT_STATE_DONE: + + // + // We are done with auto-detect and running. + // Make sure this packet is useful. If the source + // MAC address and source IPX node are the same then + // it was not routed, and we also check that it is not + // an IPX broadcast (otherwise a misconfigured client + // might confuse us). + // + + if ((RtlEqualMemory( + IpxHeader->SourceNode, + DatagramOptions.LocalTarget.MacAddress, + 6)) && + (*(UNALIGNED ULONG *)(IpxHeader->DestinationNode) != 0xffffffff) && + (*(UNALIGNED USHORT *)(IpxHeader->DestinationNode+4) != 0xffff)) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + *(UNALIGNED LONG *)IpxHeader->SourceNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d is network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + } + } + + break; + + case AUTO_DETECT_STATE_RUNNING: + + // + // We are waiting for rip responses to figure out our + // network number. We count the responses that match + // and do not match our current value; when the non- + // matching number exceeds it we switch (to whatever + // this frame happens to have). Note that on the first + // non-zero response this will be the case and we will + // switch to that network. + // + // After auto-detect is done we call RipInsertLocalNetwork + // for whatever the current network is on each binding. + // + + if (SourceNetwork == Binding->TentativeNetworkAddress) { + + ++Binding->MatchingResponses; + + } else { + + ++Binding->NonMatchingResponses; + + if (Binding->NonMatchingResponses > Binding->MatchingResponses) { + + IPX_DEBUG (AUTO_DETECT, ("Switching to net %lx on %lx (%d - %d)\n", + REORDER_ULONG(SourceNetwork), + Binding, + Binding->NonMatchingResponses, + Binding->MatchingResponses)); + + Binding->TentativeNetworkAddress = SourceNetwork; + Binding->MatchingResponses = 1; + Binding->NonMatchingResponses = 0; + } + + } + + // + // If we are auto-detecting and we have just found + // a default, set this so that RIP stops trying + // to auto-detect on other nets. BUGBUG: Unless we + // are on a server doing multiple detects. + // + + if (Binding->DefaultAutoDetect) { + Adapter->DefaultAutoDetected = TRUE; + } + Adapter->AutoDetectResponse = TRUE; + + break; + + default: + + // + // We are still initializing, or are processing auto-detect + // responses, not the right time to start updating stuff. + // + + break; + + } + + } + + + // + // See if any packets are waiting for a RIP response. + // + + if (Device->RipPacketCount > 0) { + + RIP_PACKET UNALIGNED * RipPacket = (RIP_PACKET UNALIGNED *)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_RESPONSE) && + (RipPacket->NetworkEntry.NetworkNumber != 0xffffffff)) { + + RipProcessResponse( + Device, + &DatagramOptions.LocalTarget, + RipPacket); + } + } + + + // + // See if this is a RIP response for our virtual network + // and we are the only person who could respond to it. + // We also respond to general queries on WAN lines since + // we are the only machine on it. + // + + if (Device->RipResponder) { + + PRIP_PACKET RipPacket = + (PRIP_PACKET)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_REQUEST) && + ((RipPacket->NetworkEntry.NetworkNumber == Device->VirtualNetworkNumber) || + (Adapter->MacInfo.MediumAsync && (RipPacket->NetworkEntry.NetworkNumber == 0xffffffff)))) { + + // + // Update this so our response goes out correctly. + // + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + + RipSendResponse( + Binding, + (TDI_ADDRESS_IPX UNALIGNED *)(IpxHeader->SourceNetwork), + &DatagramOptions.LocalTarget); + } + } + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_RIP | IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // See if the RIP upper driver wants it too. + // + + goto RipIndication; + } + + } + + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + return NDIS_STATUS_SUCCESS; + + // + // These are the failure routines for the various media types. + // They only differ in the debug logging. + // + +NotValid802_3: + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + ++IPX_MIB_ENTRY(Device, SysInHdrErrors); +#endif SNMP + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header, Header+6, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValid802_5: + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + ++IPX_MIB_ENTRY(Device, SysInHdrErrors); +#endif SNMP + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+8, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValidFddi: + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + ++IPX_MIB_ENTRY(Device, SysInHdrErrors); +#endif SNMP + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +NotValidLoopback: + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysInReceives); + ++IPX_MIB_ENTRY(Device, SysInHdrErrors); +#endif SNMP + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+1, Header+7, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + + return NDIS_STATUS_SUCCESS; + +} /* IpxReceiveIndication */ + + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + +Return Value: + + None + +--*/ + +{ + + PADAPTER Adapter = (PADAPTER)BindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PLIST_ENTRY linkage; + CTELockHandle OldIrq; + PDEVICE Device = IpxDevice; + PIRP pIrp; + PLIST_ENTRY pLE; + + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&Adapter->RequestCompletionQueue)) { + + linkage = IPX_REMOVE_HEAD_LIST( + &Adapter->RequestCompletionQueue, + Adapter->DeviceLock); + + if (!IPX_LIST_WAS_EMPTY (&Adapter->RequestCompletionQueue, linkage)) { + + Request = LIST_ENTRY_TO_REQUEST(linkage); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + IPX_DEBUG (RECEIVE, ("Completing RDG on %lx\n", AddressFile)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest(Request); + IpxFreeRequest(Adapter->Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_RCV_DGRAM); + + } else { + + // + // IPX_REMOVE_HEAD_LIST returned nothing, so don't + // bother looping back. + // + + break; + + } + + } + + // + // Unwind this loop for speed. + // + + if (IpxDevice->AnyUpperDriverBound) { + + // PDEVICE Device = IpxDevice; + + if ((Device->UpperDriverBound[0]) && + (Device->ReceiveCompletePending[0])) { + + (*Device->UpperDrivers[0].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[0] = FALSE; + + } + + if ((Device->UpperDriverBound[1]) && + (Device->ReceiveCompletePending[1])) { + + (*Device->UpperDrivers[1].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[1] = FALSE; + + } + + if ((Device->UpperDriverBound[2]) && + (Device->ReceiveCompletePending[2])) { + + (*Device->UpperDrivers[2].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[2] = FALSE; + + } + + } + + CTEGetLock(&Device->Lock, &OldIrq); + if (pRtInfo) + { + CTEFreeLock(&Device->Lock, OldIrq); + IpxReferenceRt(pRtInfo, RT_EXTRACT); + while((pLE = ExInterlockedRemoveHeadList(&pRtInfo->CompletedIrps, + &pRtInfo->Lock)) != NULL) + { + pIrp = LIST_ENTRY_TO_REQUEST(pLE); + CTEAssert(pIrp); + IpxPrint0("IpxReceiveComplete: Completing extracted irp\n"); + NTIoComplete(pIrp, (NTSTATUS)-1,(ULONG)-1); + } + IpxDereferenceRt(pRtInfo, RT_EXTRACT); + + } else { + CTEFreeLock(&Device->Lock, OldIrq); + } + + // + // If there are any Ntf completions, do them. These ntf completions + // are only if we discovered the address of one of our adapters. + // + + while((pLE = ExInterlockedRemoveHeadList(&Device->NicNtfComplQueue, &Device ->Lock)) != NULL) + { + pIrp = LIST_ENTRY_TO_REQUEST(pLE); + CTEAssert(pIrp); + IpxPrint0("IpxReceiveComplete: Completing Nic Ntf irp\n"); + NTIoComplete(pIrp, (NTSTATUS)-1, (ULONG)-1); + IpxDereferenceDevice (Device, DREF_NIC_NOTIFY); + } + +} /* IpxReceiveComplete */ + + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ) + +/*++ + +Routine Description: + + This routine is called when we have decided that we now know + the network number for a binding which we previously thought + was zero. + +Arguments: + + Device - The IPX device. + + Binding - The binding being updated. + + Network - The new network number. + +Return Value: + + The status of the operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS Address; + ULONG CurrentHash; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Only binding set members should have these different, + // and they will not have a network of 0. + // + + Status = RipInsertLocalNetwork( + Network, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if (Status == STATUS_SUCCESS) { + + Binding->LocalAddress.NetworkAddress = Network; + + // + // Update the device address if we have no virtual net + // and there is one binding (!Device->MultiCardZeroVirtual) + // or this is the first binding, which is the one we + // appear to be if a) we have no virtual net defined and + // b) we are bound to multiple cards. + // +#ifdef _PNP_POWER + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + if (!Device->VirtualNetwork) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Network; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Network)); + } + + } + } +#else + if ((!Device->VirtualNetwork) && + ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1))) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own + // headers. When we indicate a line up on NIC ID + // 0 it knows to requery the local address. + // + // BUGBUG: Line up indication to RIP/NB?? + // + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + + IPX_LINE_INFO LineInfo; + LineInfo.LinkSpeed = Device->LinkSpeed; + LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + LineInfo.MacOptions = Device->MacOptions; + + (*Device->UpperDrivers[IDENTIFIER_SPX].LineUpHandler)( + 0, + &LineInfo, + Binding->Adapter->MacInfo.RealMediumType, + NULL); + + } + } +#endif + } else if (Status == STATUS_DUPLICATE_NAME) { + + // + // If it was a duplicate we still set the binding's local + // address to the value so we can detect binding sets. + // + + Binding->LocalAddress.NetworkAddress = Network; + + } + + Binding->fInfoIndicated = FALSE; + if ((p = ExInterlockedRemoveHeadList( + &Device->NicNtfQueue, + &Device->Lock)) != NULL) + { + PREQUEST Request; + + Request = LIST_ENTRY_TO_REQUEST(p); + + DbgPrint("IpxStatus: Got address of binding\n"); + Status = GetNewNics(Device, Request, FALSE, NULL, 0, TRUE); + + // + // If not success, we don't queue back the irp. It has + // already been queued or completed + // + if (Status != STATUS_SUCCESS) + { + DbgPrint("New address Irp screw up. Status = (%lx)\n", + Status); + } + else + { + KIRQL OldIrq; + // + // Status is SUCCESS + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(OldIrq); + REQUEST_STATUS(Request) = Status; + ExInterlockedInsertTailList(&Device->NicNtfComplQueue,REQUEST_LINKAGE(Request), &Device->Lock); + } + + } + + return Status; + +} /* IpxUpdateBindingNetwork */ + + +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + Packet - contains the packet received as well as some mediaspecific info. + +Return Value: + + return of IpxReceiveIndicationCommon(), + +--*/ +{ + UINT HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet); + UINT firstbufferLength, bufferLength; + PNDIS_BUFFER pFirstBuffer; + PUCHAR headerBuffer; + NTSTATUS ntStatus; + INT tdiClientCount = 0; + + // + // Query the number of buffers, the first MDL's descriptor and the packet length + // + NdisGetFirstBufferFromPacket(Packet, // packet + &pFirstBuffer, // first buffer descriptor + &headerBuffer, // ptr to the start of packet + &firstbufferLength,// length of the header+lookahead + &bufferLength); // length of the bytes in the buffers + + // + // ReceiveContext is the packet itself + // + + ntStatus = IpxReceiveIndicationCommon ( + ProtocolBindingContext, + Packet, // ReceiveContext + headerBuffer, + HeaderBufferSize, + headerBuffer + HeaderBufferSize, // LookaheadBuffer + bufferLength - HeaderBufferSize, // LookaheadBufferSize + bufferLength - HeaderBufferSize, // PacketSize - since the whole packet is indicated + pFirstBuffer, // pMdl + &tdiClientCount // tdi client count + ); + + IPX_DEBUG(RECEIVE, ("IpxReceivePacket: Tdi Client Count is: %lx\n", tdiClientCount)); + + return tdiClientCount; +} /* IpxReceivePacket */ + + +#ifdef _PNP_POWER + +#if defined(_M_IX86) +_inline +#endif +BOOLEAN +IpxNewVirtualNetwork( + IN PDEVICE Device, + IN BOOLEAN NewVirtualNetwork + ) +/*++ + +Routine Description: + + If the virtualnetwork number changed, this function records this fact + in the device. + + Called with the BINDACCESSLOCK held. +Arguments: + + Device - Pointer to the Device. + + NewVirtualNetwork - boolean to indicate if the virtual net# changed. + +Return Value: + + BOOLEAN - to indicate whether SPX's reserved address was changed. + +--*/ +{ + NTSTATUS ntStatus; + UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + BOOLEAN ReservedAddrChanged = FALSE; + + if (Device->VirtualNetworkNumber) { + + if (NewVirtualNetwork) { + // + // If a new one appeared. + // + + ntStatus = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + NIC_ID_TO_BINDING(Device, 1)->Adapter->NdisBindingHandle, + 1); + + if (ntStatus != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (ntStatus == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + // + // If the number is non-zero now, a new one appeared + // + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + ReservedAddrChanged = TRUE; + + // + // If RIP is not bound, then this node is a RipResponder + // + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + Device->RipResponder = TRUE; + } + } + + } else { +NoVirtualNetwork: + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (Device->ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + if (NewVirtualNetwork) { + // + // The virtual network number disappeared this time + // + + // + // Remove the prev. net # from the RIP tables here + // + RipAdjustForBindingChange (0, 0, IpxBindingDeleted); + + // + // If we were a RipResponder, we are not anymore + // + if (Device->RipResponder) { + Device->RipResponder = FALSE; + } + } + + // + // Since there is not virtual network number, SPX's reserved address is + // the address of the first binding. This could have changed because of + // several reasons: if there was a WAN binding only earlier and this time + // a LAN binding appeared, or if the first LAN binding disappeared. Instead + // of checking for all these conditions, check if the Device's sourceaddress + // and that of the first mis-match. + // NB uses the address of the first device always and hence does not need + // this mechanism to determine if this is a reserved address change. + // + if (!RtlEqualMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket))) { + + RtlCopyMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + ReservedAddrChanged = TRUE; + } + } + +#ifdef SNMP + *(UNALIGNED ULONG *)(IPX_MIB_ENTRY(Device, SysNetNumber)) = Device->SourceAddress.NetworkAddress; + + *(UNALIGNED ULONG *)(IPX_MIB_ENTRY(Device, SysNode)) = + *(UNALIGNED ULONG *)(Device->SourceAddress.NodeAddress); + *(UNALIGNED USHORT *)(IPX_MIB_ENTRY(Device, SysNode)+4) = + *(UNALIGNED USHORT *)(Device->SourceAddress.NodeAddress+4); +#endif + return ReservedAddrChanged; +} + + +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about a new + adapter in the machine. We are called here only if this adapter + is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING + + BindContext - context to represent this bind indication + + DeviceName - Name of the adapter that appeared (e.g. \Device\Lance1) + + SystemSpecific1/2 - Not used here + +Return Value: + + Status - NDIS_STATUS_SUCCESS + +--*/ +{ + NTSTATUS ntStatus; + PDEVICE Device = IpxDevice; + PADAPTER Adapter = NULL; + CONFIG Config; + UINT i; + ULONG Temp, SuccessfulOpens=0; + PBINDING Binding; + BINDING_CONFIG ConfigBinding; + ULONG ValidBindings; + USHORT AutoDetectReject; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN FirstDevice = FALSE; + BOOLEAN ReservedAddrChanged = FALSE; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + ConfigBinding.AdapterName = *DeviceName; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the virtual network number changed, record this fact. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + Device->VirtualNetworkNumber = Temp; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // For each FrameType and Network Number configured, initialize the + // FrameType array in the CONFIG_BINDING + // + ntStatus = IpxPnPGetAdapterParameters( + &Config, + DeviceName, + &ConfigBinding); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the adapter params: DeviceName: %lx\n", DeviceName->Buffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + IPX_DEBUG(PNP, ("ConfigBinding.FrameTypeCount: %lx\n", ConfigBinding.FrameTypeCount)); + + // + // Reset the auto-detect state to init so that if a receive occurs on this binding + // before we can place this binding in the device's binding array, we know of it. + // + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + // + // Register adapter with NDIS; query the various parameters; get the WAN line count + // if this is a WAN adapter. + // Allocate the bindings corresponding to this adapter + // + for (i = 0; i < ConfigBinding.FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. [BUGBUGZZ] not right now + // Adapter is NULL first time and is allocated then. In subsequent calls, + // it is not NULL and the bindings are hooked to this adapter. + + ntStatus = IpxBindToAdapter (Device, &ConfigBinding, &Adapter, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (ntStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + // + // If the status is STATUS_NOT_SUPPORTED, then this frametype mapped to a previously + // initialized one. In this case, remove this index fron the FrameType array so that + // when we try to update the binding array, we dont have duplicates. + // + if (ntStatus == STATUS_NOT_SUPPORTED) { + ULONG j; + + // + // Remove this frametype from the FrameType array. + // + for (j = i+1; j < ConfigBinding.FrameTypeCount; j++) { + ConfigBinding.FrameType[j-1] = ConfigBinding.FrameType[j]; + } + + --ConfigBinding.FrameTypeCount; + + // + // Decrement so we see the one just moved up. + // + --i; + +#if DBG + for (j = 0; j < ISN_FRAME_TYPE_MAX; j++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + j, ConfigBinding.FrameType[j], ConfigBinding.NetworkNumber[j], ConfigBinding.AutoDetect[j])); + } +#endif + continue; + } + + if (ntStatus != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding.AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + CTEAssert(Adapter); + + ++SuccessfulOpens; + + // + // Even for WAN adapters, the FrameTypeCount is set to 4. We only need to + // allocate one binding for WAN; the others come later. + // + if (Adapter->MacInfo.MediumAsync) { + break; + } + } + + if (SuccessfulOpens == 0) { + goto InitFailed; + } + + // + // Place all the bindings corresponding to this adapter in the binding array + // Also resolve binding sets for non-autodetect bindings. + // + + // + // Obtain lock to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IpxPnPUpdateBindingArray (Device, Adapter, &ConfigBinding); + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // If at least one card appeared here, set our state + // to open + // + // [BUGBUGZZ]: what if all these bindings are eliminated - then + // the state is not open... + // + if (Device->ValidBindings > 0) { + if (Device->State == DEVICE_STATE_LOADED) { + FirstDevice = TRUE; + Device->State = DEVICE_STATE_OPEN; + } + } + + // + // We don't do auto-detect/bindingsets for WAN lines: skip over. + // + if (Adapter->MacInfo.MediumAsync) { + goto jump_wan; + } + + // + // Auto-detect the network number. Update the results for only the + // bindings corresponding to this adapter + // + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + // Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + ntStatus = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (ntStatus == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + // + // Obtain exclusive access to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Note, here we go thru' only the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + Binding = Adapter->Bindings[i]; + + // + // Skip empty binding slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + ntStatus = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((ntStatus != STATUS_SUCCESS) && + (ntStatus != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + // ValidBindings = Device->BindingCount; + + ValidBindings = Device->ValidBindings; + + // [BUGBUGZZ] if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, &LockHandle1, &Device->RegistryPath); + + //} + + // + // Adjust all the indices by the number of AutoDetect bindings thrown away + // + // AutoDetectReject = (USHORT)(Device->BindingCount - ValidBindings); + + AutoDetectReject = (USHORT)(Device->ValidBindings - ValidBindings); + + Device->HighestLanNicId -= AutoDetectReject; + Device->HighestExternalNicId -= AutoDetectReject; + Device->HighestType20NicId -= AutoDetectReject; + Device->SapNicCount -= AutoDetectReject; + + Device->ValidBindings = (USHORT)ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This updates the Device->HighestExternalNicId + // and Device->HighestType20NicId, SapNicCount, HighestLanNicId + // + + // + // Do this only for the auto-detect bindings + // [BUGBUGZZ] check this + // + + //if (Device->AutoDetect) { + IpxResolveBindingSets (Device, Device->HighestExternalNicId); + //} + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +jump_wan: + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); + + // + // Enable this regardless of whether any of our clients enabled b'cast. + // NB always enables it, so we are fine. + // + // Since we dont increment the Broadcast count in the device, we will disable b'casts + // correctly if the count drops to 0. + // + // If the ISN clients appear before the adapters, they increment the BCount, but + // since the ValidBindings is 0, all works. Then, when the adapters appear, we enable + // the broadcasts here. + // + // If the adapters appear before the ISN clients, then the broadcast is enabled on + // the adapters here and the adapter's flag is set to indicate this, which will prevent + // any further calls to NDIS when the ISN clients force an IpxAddBroadcast. + // + Device->EnableBroadcastPending = TRUE; + IpxBroadcastOperation((PVOID)TRUE); + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // This function updates flags like RipResponder, MultiCardZeroVirtual, etc. + // If the VirtualNetwork number changed (NewVirtualNetwork is TRUE), it updates + // the Device structure and the RIP tables accordingly. + // It returns a boolean to indicate if SPX's reserved address changed. + // + ReservedAddrChanged = IpxNewVirtualNetwork(Device, NewVirtualNetwork); + + // + // Update the values once the auto-detect bindings have been thrown away... + // + IpxPnPUpdateDevice(Device); + + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + if (FirstDevice) { + UNICODE_STRING devicename; + + // + // Inform TDI clients about the open of our device object. + // + devicename.MaximumLength = (USHORT)Device->DeviceNameLength; + devicename.Length = (USHORT)Device->DeviceNameLength - sizeof(WCHAR); + devicename.Buffer = Device->DeviceName; + + if ((ntStatus = TdiRegisterDeviceObject( + &devicename, + &Device->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterDeviceObject failed: %lx", ntStatus)); + } + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform NB and TDI of all the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + // + // If a NULL binding or a binding set slave, dont inform NB about it. + // + if (!Binding || (Binding->NicId > Device->HighestExternalNicId)) { +#if DBG + if (Binding) { + IPX_DEBUG(PNP, ("Binding: %lx, Binding set slave\n", Binding)); + } +#endif + continue; + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Lock taken to check the UpperDriverBound flag. + // We already have the BindAccessLock at this point. + // + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // Ensure that we dont do it twice. + // + if (!Binding->IsnInformed[IDENTIFIER_NB]) { + + // + // Also, to ensure that the indications are done in the right order, + // check if the first card has been indicated yet. + // + if ((Binding->NicId != 1) && + !NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_NB]) { + + break; + } + + Binding->IsnInformed[IDENTIFIER_NB] = TRUE; + + if (Binding->NicId == 1) { + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + IpxPnPInfo.FirstORLastDevice = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + } + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB add: %lx\n", Binding)); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // Always true for SPX + // + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + // + // Inform SPX - the network/node address is the Virtual one if it exists + // else the address of the first binding + // + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to SPX add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + + // + // Not the first device - inform if the reserved address changed. + // + if (ReservedAddrChanged) { + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + IPX_DEBUG(PNP, ("Reserved addr changed; SPX not told of first one yet\n")); + } + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("PnP to SPX add (res. addr change): %lx\n", Binding)); + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +InitFailed: + *Status = NDIS_STATUS_SUCCESS; + return; + +} /* IpxBindAdapter */ + + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about the removal + of an existing adapter from the machine. We are called here only if + this adapter is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING. + + ProtocolBindingContext - the adapter that got removed. + + UnbindContext - context to represent this bind indication. + +Return Value: + + Void - return thru' Status above. + +--*/ +{ + NTSTATUS ntStatus; + PADAPTER Adapter=(PADAPTER)ProtocolBindingContext; + CONFIG Config; + PBINDING Binding; + PDEVICE Device=IpxDevice; + ULONG i, Temp; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN NBReservedAddrChanged = FALSE; + BOOLEAN SPXInformed = FALSE; + IPX_PNP_INFO IpxPnPInfo; + PBINDING newMasterBinding; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the VirtualNetwork number changed, record it. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // If the WAN adapter disappeared, we can simply remove all the WAN bindings since + // all of them correspond to this single WAN adapter. Since we tell NB only about + // the first one of these, we need to indicate removal of only one binding to NB. + // + if (Adapter->MacInfo.MediumAsync) { + USHORT wanLineCount = (USHORT)Adapter->WanNicIdCount; + + CTEAssert(wanLineCount == (Device->HighestExternalNicId - Device->HighestLanNicId)); + + // + // If no more bindings remain, tell upper driver of the same. + // We go back to the loaded state. + // + + if ((Device->ValidBindings - wanLineCount) == 0) { + IpxPnPInfo.FirstORLastDevice = TRUE; + Device->State = DEVICE_STATE_LOADED; + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // DeRegister this address with the TDI clients. + // + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Give the PnP indication to indicate the deletion only if it was + // added before. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB delete: %lx\n", Binding)); + } +#if DBG + else { + DbgPrint("WAN adapter id: %lx not indicated to NB\n", Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Inform SPX only if this is the last device. + // + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (IpxPnPInfo.FirstORLastDevice && Binding->IsnInformed[IDENTIFIER_SPX]) { + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Device->HighestLanNicId == 0); + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + } + + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Now remove these WAN bindings from the array. Move all the Slave bindings + // up to where the WAN bindings were. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + for (i = Device->HighestLanNicId+1; i <= Device->HighestExternalNicId; i++) { + // + // Unbind from the adapter - if it is not referenced by any other thread, it will + // be deleted at this point. + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(NIC_ID_TO_BINDING_NO_ILOCK(Device, i)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // Move the slave binding here. + // + INSERT_BINDING(Device, i, NIC_ID_TO_BINDING_NO_ILOCK(Device, i+wanLineCount)); + } + + // + // Free the demand dial binding place holder. + // + IpxUnBindFromAdapter(NIC_ID_TO_BINDING(IpxDevice, DEMAND_DIAL_ADAPTER_CONTEXT)); + + // + // Update the indices + // + Device->HighestExternalNicId -= wanLineCount; + Device->ValidBindings -= wanLineCount; + Device->BindingCount -= wanLineCount; + Device->SapNicCount = Device->HighestType20NicId = Device->HighestLanNicId; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + CTEAssert(Device->HighestLanNicId == Device->HighestExternalNicId); + + } else { + // + // LAN adapter disappeared. + // + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + // + // For each binding corresponding to this adapter, inform NB only + // if the binding addition was indicated. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + if (!Binding) { + continue; + } + + // + // We cannot receive on this binding anymore + // + Adapter->Bindings[i] = NULL; + + // + // If this was a slave binding, dont inform of the deletion. + // Just remove the binding from the binding array and the bindingset list. + // + + if (Binding->NicId > Device->HighestExternalNicId) { + PBINDING MasterBinding, tempBinding; + + CTEAssert(Binding->BindingSetMember); + CTEAssert(Binding->CurrentSendBinding == NULL); + + // + // Traverse the bindingset list and remove this binding from there. + // + tempBinding = MasterBinding = Binding->MasterBinding; + + while (tempBinding->NextBinding != MasterBinding) { + if (tempBinding->NextBinding == Binding) { + tempBinding->NextBinding = tempBinding->NextBinding->NextBinding; + break; + } + tempBinding = tempBinding->NextBinding; + } + + // + // If no more slaves, this is no longer a bindingset. + // + if (MasterBinding->NextBinding == MasterBinding) { + MasterBinding->BindingSetMember = FALSE; + MasterBinding->CurrentSendBinding = NULL; + MasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Slave binding: %lx removed, no master: %lx\n", Binding, MasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", Binding->NicId, MasterBinding->NicId)); + + // + // Null out the Slave binding. + // + INSERT_BINDING(Device, Binding->NicId, NULL); + + --Device->ValidBindings; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + + continue; + } + + // + // If this was the last binding, go back to loaded state and shut down the RIP timers. + // + if (Device->ValidBindings == 1) { + CTEAssert(Device->HighestExternalNicId == 1); + CTEAssert(Device->HighestLanNicId == 1); + CTEAssert(Device->SapNicCount == 1); + CTEAssert(Device->HighestType20NicId == 1); + + Device->State = DEVICE_STATE_LOADED; + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // Free the loopback binding place holder. + // + IpxUnBindFromAdapter(NIC_ID_TO_BINDING(IpxDevice, LOOPBACK_NIC_ID)); + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // If this was a master binding, promote a slave binding to master. + // + if (Binding->BindingSetMember) { + + CTEAssert(Binding->CurrentSendBinding); + CTEAssert(Binding->MasterBinding == Binding); + + // + // Promote the next slave to Master. + // + newMasterBinding = Binding->NextBinding; + INSERT_BINDING(Device, Binding->NicId, newMasterBinding); + newMasterBinding->CurrentSendBinding = newMasterBinding; + newMasterBinding->MasterBinding = newMasterBinding; + + // + // If this is the only binding remaining out of its set, + // it is no longer part of a set. + // + if (newMasterBinding->NextBinding == Binding) { + newMasterBinding->NextBinding = newMasterBinding->CurrentSendBinding = NULL; + newMasterBinding->BindingSetMember = FALSE; + newMasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Master binding: %lx removed, no master: %lx\n", Binding, newMasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (newMasterBinding->NicId, Binding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", newMasterBinding->NicId, Binding->NicId)); + + // + // Register slave's address with the TDI clients. + // + CTEAssert(!newMasterBinding->TdiRegistrationHandle); + + RtlCopyMemory ( Device->TdiRegistrationAddress->Address, + &newMasterBinding->LocalAddress, + sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &newMasterBinding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Null out the slave binding + // + INSERT_BINDING(Device, newMasterBinding->NicId, NULL); + + newMasterBinding->NicId = Binding->NicId; + + IPX_DEBUG(PNP, ("Promoted a master binding: %lx, old master: %lx\n", newMasterBinding, Binding)); + } else { + + ULONG j; + PBINDING WanBinding=NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + + if (WanBinding) { + --WanBinding->Adapter->LastWanNicId; + --WanBinding->Adapter->FirstWanNicId; + } + + // + // Remove the binding from the array + // + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + for (j = Binding->NicId+1; j <= Device->HighestExternalNicId; j++) { + INSERT_BINDING(Device, j-1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + --NIC_ID_TO_BINDING_NO_ILOCK(Device, j)->NicId; + } + + INSERT_BINDING(Device, Device->HighestExternalNicId, NULL); + + --Device->HighestExternalNicId; + --Device->HighestLanNicId; + --Device->HighestType20NicId; + --Device->SapNicCount; + } + + --Device->ValidBindings; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + // + // If this is the first binding, NB's reserved will change. + // When we inform SPX of an address change later, we dont have + // this binding to know if this binding was indicated to SPX earlier. + // So, set SPXInformed, which is used later to determine if an address + // change is to be indicated to SPX later. + // + // Since NB is informed of all adapters, we inform of the reserved address + // change to NB if the new Binding (now at NicId 1) was indicated earlier. + // + if (Binding->NicId == 1) { + NBReservedAddrChanged = TRUE; + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + SPXInformed = TRUE; + } + } + + CTEAssert(Binding->TdiRegistrationHandle); + + // + // DeRegister this address with the TDI clients. + // + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate its deletion to NB. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete LAN device: %lx\n", Binding)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // If this was a Master, indicate the addition of the (promoted) slave + // + if (Binding->BindingSetMember) { + IpxPnPInfo.NetworkAddress = newMasterBinding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, newMasterBinding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, newMasterBinding->NicId); + + // + // In this case, we set the ReservedAddrChanged bit here itself so dont need + // to indicate a separate address changed. + // + IpxPnPInfo.NewReservedAddress = (NBReservedAddrChanged) ? TRUE : FALSE; + NBReservedAddrChanged = FALSE; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: add slave device: NicId: %lx\n", Binding->NicId)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + newMasterBinding->IsnInformed[IDENTIFIER_NB] = TRUE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Last device - inform SPX if it is bound and this device was added earlier. + // + if (IpxPnPInfo.FirstORLastDevice) { + IPX_DEBUG(PNP, ("Last device - inform SPX\n")); + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: last LAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + // + // Unbind from the adapter so it can be deleted + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + + // + // Update the Device and RIP tables if this is not the last device. + // If the reserved address changed, inform NB and SPX of this change. + // + if (!IpxPnPInfo.FirstORLastDevice) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1); + + if (IpxNewVirtualNetwork(Device, NewVirtualNetwork)) { + + IPX_DEBUG(PNP, ("SPX's reserved address changed\n")); + + // + // SPX's reserved address changed + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // If this binding's addition was indicated earlier, indicate change of address. + // + if (SPXInformed) { + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + + IPX_DEBUG(PNP, ("Inform SPX: reserved address changed\n")); + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } else { + + // + // Set the first binding's flag so that when this binding goes away, we remember + // to inform SPX of this device's removal. + // + + IPX_DEBUG(PNP, ("Transfer SPX informed flag to NicId: %lx\n", Binding->NicId)); + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + } + + if (NBReservedAddrChanged) { + // + // NB's reserved address changed. + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate the change of reserved address. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: reserved address changed\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + // + // Re-calculate the values of datagram sizes in the Device. + // + IpxPnPUpdateDevice(Device); + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); +} /* IpxUnbindAdapter */ + + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + ReceivedPacket - The packet received + + MediaSpecificInformation - Used for media such as Irda, wireless, etc. Not used here. + + HeaderBufferSize - Size of the MAC header + +Return Value: + + +--*/ +{ +} /* IpxTranslate */ + +#endif _PNP_POWER + + diff --git a/private/ntos/tdi/isn/ipx/internal.c b/private/ntos/tdi/isn/ipx/internal.c new file mode 100644 index 000000000..a0721679a --- /dev/null +++ b/private/ntos/tdi/isn/ipx/internal.c @@ -0,0 +1,1342 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + internal.c + +Abstract: + + This module contains the code to handle the internal + binding of the upper drivers to IPX. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 25-August-1995 + Bug Fixes - tagged [SA] +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to bind to IPX. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + PIPX_INTERNAL_BIND_INPUT BindInput; + PIPX_INTERNAL_BIND_OUTPUT BindOutput; + PIPX_INTERNAL_BIND_RIP_OUTPUT BindRipOutput; + CTELockHandle LockHandle; + PIPX_NIC_DATA NicData; + PBINDING Binding, LastRealBinding; + PADAPTER Adapter; + ULONG Identifier; + ULONG BindOutputSize; + BOOLEAN BroadcastEnable; + UINT i; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + BOOLEAN fFwdBindAttempt = FALSE; +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(IPX_INTERNAL_BIND_INPUT) - sizeof(ULONG))) { + + IPX_DEBUG (BIND, ("Bind received, bad input length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.InputBufferLength, + sizeof (IPX_INTERNAL_BIND_INPUT))); + return STATUS_INVALID_PARAMETER; + + } + + BindInput = (PIPX_INTERNAL_BIND_INPUT)(Irp->AssociatedIrp.SystemBuffer); + + if (BindInput->Identifier >= UPPER_DRIVER_COUNT) { + IPX_DEBUG (BIND, ("Bind received, bad id %d\n", BindInput->Identifier)); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (BIND, ("Bind received from id %d (%s)\n", + BindInput->Identifier, + IdStrings[BindInput->Identifier])); + +#ifdef _PNP_POWER +// +// RIP gives us version == 1 whereas Forwarder gives us 2 (ISN_VERSION). +// + if (BindInput->Identifier == IDENTIFIER_RIP) { + if (BindInput->Version == ISN_VERSION) { + fFwdBindAttempt = TRUE; + } else { + CTEAssert(!Device->ForwarderBound); + + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } + } else { + if (BindInput->Version != ISN_VERSION) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } + +#else + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } +#endif + + if (BindInput->Identifier != IDENTIFIER_RIP) { + BindOutputSize = sizeof(IPX_INTERNAL_BIND_OUTPUT); + } else { + BindOutputSize = FIELD_OFFSET (IPX_INTERNAL_BIND_RIP_OUTPUT, NicInfoBuffer.NicData[0]) + + (MIN (Device->MaxBindings, Device->HighestExternalNicId) * sizeof(IPX_NIC_DATA)); + } + + Irp->IoStatus.Information = BindOutputSize; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + BindOutputSize) { + + IPX_DEBUG (BIND, ("Bind: bad output length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.OutputBufferLength, + BindOutputSize)); + + // + // Fail this request with BUFFER_TOO_SMALL. Since the + // I/O system may not copy the status block back to + // the user's status block, do that here so that + // he gets IoStatus.Information. + // + + try { + *Irp->UserIosb = Irp->IoStatus; + } except(EXCEPTION_EXECUTE_HANDLER) { + NOTHING; + } + + return STATUS_BUFFER_TOO_SMALL; + } + + // + // We have verified the length, make sure we are not + // already bound. + // + + Identifier = BindInput->Identifier; + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[Identifier]) { + IPX_DEBUG (BIND, ("Bind: already bound\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return STATUS_REQUEST_NOT_ACCEPTED; + } + + { + LARGE_INTEGER ControlChId; + + CCID_FROM_REQUEST(ControlChId, Irp); + + IPX_DEBUG (BIND, ("Control ChId: (%d, %d) for Id: %d\n", ControlChId.HighPart, ControlChId.LowPart, Identifier)); + Device->UpperDriverControlChannel[Identifier].QuadPart = ControlChId.QuadPart; + } + + RtlCopyMemory( + &Device->UpperDrivers[Identifier], + BindInput, + sizeof (IPX_INTERNAL_BIND_INPUT) + ); + + BroadcastEnable = BindInput->BroadcastEnable; + + // + // Now construct the output buffer. + // + + if (Identifier != IDENTIFIER_RIP) { + + BindOutput = (PIPX_INTERNAL_BIND_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindOutput->Version = 1; + + // + // Tell netbios our first binding's net/node instead of the + // virtual one. + // +#ifdef _PNP_POWER +// +// Fill the fields in only if the adapters have already appeared +// Else, set NodeNumber to 0 so NB/SPX know of it. +// + if ((*(UNALIGNED USHORT *)(Device->SourceAddress.NodeAddress+4) != 0) || + (*(UNALIGNED ULONG *)Device->SourceAddress.NodeAddress != 0)) { + + IPX_DEBUG(BIND, ("Device already opened\n")); + CTEAssert(Device->ValidBindings); + + if (Identifier == IDENTIFIER_SPX) { + + // + // For SPX, inform directly. + // + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IpxPnPIsnIndicate((PVOID)Identifier); + + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + } else { + // + // For NB, queue a work item which will go thru' the adapters list and + // inform the upper drivers about each of them. + // + ExInitializeWorkItem( + &Device->PnPIndicationsQueueItem, + IpxPnPIsnIndicate, + (PVOID)Identifier); + ExQueueWorkItem(&Device->PnPIndicationsQueueItem, DelayedWorkQueue); + } + + } else { + IPX_DEBUG(BIND, ("Device not open\n")); + *((UNALIGNED ULONG *)BindOutput->Node) = 0; + *((UNALIGNED USHORT *)(BindOutput->Node+4)) = 0; + RtlZeroMemory(&BindOutput->LineInfo, sizeof(BindOutput->LineInfo)); + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindOutput->IncludedHeaderOffset = MAC_HEADER_SIZE; // (USHORT)Device->IncludedHeaderOffset; + + BindOutput->SendHandler = IpxSendFramePreFwd; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; + +#else + if ((Identifier == IDENTIFIER_NB) && + (Device->VirtualNetwork)) { + RtlCopyMemory(BindOutput->Node, Device->Bindings[1]->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->Bindings[1]->LocalAddress.NetworkAddress; + } else { + + RtlCopyMemory(BindOutput->Node, Device->SourceAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->SourceAddress.NetworkAddress; + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + + BindOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindOutput->LineInfo.LinkSpeed = Device->LinkSpeed; + BindOutput->LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + BindOutput->LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + BindOutput->LineInfo.MacOptions = Device->MacOptions; + + BindOutput->SendHandler = IpxSendFrame; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; +#endif + BindOutput->TransferDataHandler = IpxTransferData; + } else { + // + // Set this so we stop RIPping for our virtual network (if + // we have one). + // + + Device->RipResponder = FALSE; + + // + // See if he wants a single wan network number. + // + + if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IPX_INTERNAL_BIND_INPUT)) || + ((BindInput->RipParameters & IPX_RIP_PARAM_GLOBAL_NETWORK) == 0)) { + + Device->WanGlobalNetworkNumber = FALSE; + Device->SapNicCount = Device->HighestExternalNicId; + + } else { + + Device->WanGlobalNetworkNumber = TRUE; + + } + + BindRipOutput = (PIPX_INTERNAL_BIND_RIP_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindRipOutput->Version = 1; + BindRipOutput->MaximumNicCount = MIN (Device->MaxBindings, Device->HighestExternalNicId) + 1; + + BindRipOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindRipOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindRipOutput->SendHandler = IpxSendFrame; + + if (!fFwdBindAttempt) { + BindRipOutput->SegmentCount = Device->SegmentCount; + BindRipOutput->SegmentLocks = Device->SegmentLocks; + + BindRipOutput->GetSegmentHandler = RipGetSegment; + BindRipOutput->GetRouteHandler = RipGetRoute; + BindRipOutput->AddRouteHandler = RipAddRoute; + BindRipOutput->DeleteRouteHandler = RipDeleteRoute; + BindRipOutput->GetFirstRouteHandler = RipGetFirstRoute; + BindRipOutput->GetNextRouteHandler = RipGetNextRoute; + + // + // [BUGBUGZZ] remove this... + // + BindRipOutput->IncrementWanInactivityHandler = IpxInternalIncrementWanInactivity; + BindRipOutput->QueryWanInactivityHandler = IpxInternalQueryWanInactivity; + } else { + // + // [FW] New routines provided for the Forwarder + // + BindRipOutput->OpenAdapterHandler = IpxOpenAdapter; + BindRipOutput->CloseAdapterHandler = IpxCloseAdapter; + BindRipOutput->InternalSendCompleteHandler = IpxInternalSendComplete; + } + + BindRipOutput->TransferDataHandler = IpxTransferData; + + BindRipOutput->NicInfoBuffer.NicCount = (USHORT)MIN (Device->MaxBindings, Device->HighestExternalNicId); + BindRipOutput->NicInfoBuffer.VirtualNicId = 0; + if (Device->VirtualNetwork || Device->MultiCardZeroVirtual) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = Device->SourceAddress.NetworkAddress; + } else if (Device->DedicatedRouter) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = 0x0; + } + + NicData = &BindRipOutput->NicInfoBuffer.NicData[0]; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + // + // NULL bindings are WAN bindings, so we return the + // information from the last non-NULL binding found, + // which will be the first one on this adapter. + // Otherwise we save this as the last non-NULL one. + // + + if (Binding == NULL) { + Binding = LastRealBinding; + } else { + LastRealBinding = Binding; + } + + Adapter = Binding->Adapter; + NicData->NicId = i; + RtlCopyMemory (NicData->Node, Binding->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)NicData->Network = Binding->LocalAddress.NetworkAddress; + NicData->LineInfo.LinkSpeed = Binding->MediumSpeed; + NicData->LineInfo.MaximumPacketSize = + Binding->MaxLookaheadData + sizeof(IPX_HEADER); + NicData->LineInfo.MaximumSendSize = + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + NicData->LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + NicData->DeviceType = Adapter->MacInfo.RealMediumType; + NicData->EnableWanRouter = Adapter->EnableWanRouter; + + ++NicData; + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + if (BroadcastEnable) { + IpxAddBroadcast (Device); + } + + Device->UpperDriverBound[Identifier] = TRUE; + + Device->ForwarderBound = fFwdBindAttempt; + + Device->AnyUpperDriverBound = TRUE; + CTEFreeLock (&Device->Lock, LockHandle); + + return STATUS_SUCCESS; + +} /* IpxInternalBind */ + + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to unbind from IPX. It does this by closing the + control channel on which the bind ioctl was submitted. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + + IPX_DEBUG (BIND, ("Unbind received from id %d (%s)\n", + Identifier, + IdStrings[Identifier])); + + CTEGetLock (&Device->Lock, &LockHandle); + + if (!Device->UpperDriverBound[Identifier]) { + CTEFreeLock (&Device->Lock, LockHandle); + IPX_DEBUG (BIND, ("No existing binding\n")); + return STATUS_SUCCESS; + } + + // + // [FW] If RIP is unbinding, restart the long timer + // Also, set the RipResponder flag if virutal net configured + + // + // BUGBUG: Deref all bindings that RIP did not close + // + if (Identifier == IDENTIFIER_RIP && + Device->ForwarderBound) { + UINT i; + + Device->ForwarderBound = FALSE; + + // + // [FW] Walk the binding list, to deref all bindings not closed by + // the forwarder before it unbound from us. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + PBINDING Binding = NIC_ID_TO_BINDING(Device, i); + + if (Binding && (Binding->FwdAdapterContext != 0)) { + IpxDereferenceBinding(Binding, BREF_FWDOPEN); + } + } + } + + if (Device->VirtualNetwork) { + Device->RipResponder = TRUE; + } + + // + // Start the timer which updates the RIP database + // periodically. + // + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + } + + Device->UpperDriverBound[Identifier] = FALSE; + Device->AnyUpperDriverBound = (BOOLEAN) + (Device->UpperDriverBound[IDENTIFIER_RIP] || + Device->UpperDriverBound[IDENTIFIER_SPX] || + Device->UpperDriverBound[IDENTIFIER_NB]); + + if (Device->UpperDrivers[Identifier].BroadcastEnable) { + IpxRemoveBroadcast (Device); + } + +#ifdef _PNP_POWER + if (Device->ValidBindings > 0) { + // + // If SPX went away, reset the IsnIndicate flag in the first binding + // + if (Identifier == IDENTIFIER_SPX) { + CTEAssert(NIC_ID_TO_BINDING(Device, 1)); + + if (NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("SPX unbound: IsnInformed turned off\n")); + } + } + + // + // If NB went away, reset all the Binding's flags + // + if (Identifier == IDENTIFIER_SPX) { + PBINDING Binding; + UINT i; + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i < Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + if (Binding && Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("NB unbound: IsnInformed off for NicId: %lx\n", i)); + } + } + } + } +#endif + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // BUGBUG: Ensure that no calls are made to bogus + // handlers. + // + + return STATUS_SUCCESS; + +} /* IpxInternalUnbind */ + + +VOID +IpxInternalFindRoute ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to submit + requests to find a remote network, which is contained in + FindRouteRequest->Network. FindRouteRequest->Identifier must + contain the identifier of the upper driver. + + This request is always asynchronous and is completed by + a call to the FindRouteComplete handler of the upper driver. + + NOTE: As a currently unspecified extension to this call, + we returns the tick and hop counts as two USHORTs in the + PVOID Reserved2 structure of the request. + +Arguments: + + FindRouteRequest - Describes the request and contains + storage for IPX to use while processing it. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + ULONG Segment; + TDI_ADDRESS_IPX TempAddress; + PBINDING Binding, MasterBinding; + NTSTATUS Status; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // [FW] Call the Forwarder's FindRoute if installed + // + + if (Device->ForwarderBound) { + // IPX_ROUTE_ENTRY routeEntry; + + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + FindRouteRequest->Network, + FindRouteRequest->Node, + FindRouteRequest); + + if (Status != STATUS_SUCCESS) { + IPX_DEBUG (RIP, ("RouteHandler returned: %lx\n", Status)); + } else { + +#if DBG + // + // If a demand-dial NIC was returned, we should have a WAN adapter. In PnP we can check this + // by making sure that Device->HighestLanNicId < Device->HighestExternalNicId. + // + if (FindRouteRequest->LocalTarget.NicId == DEMAND_DIAL_ADAPTER_CONTEXT) { + CTEAssert(Device->HighestLanNicId < Device->HighestExternalNicId); + } +#endif + + IPX_DEBUG(RIP, ("FindRoute for %02x-%02x-%02x-%02x-%02x-%02x returned %lx", + FindRouteRequest->LocalTarget.MacAddress[0], + FindRouteRequest->LocalTarget.MacAddress[1], + FindRouteRequest->LocalTarget.MacAddress[2], + FindRouteRequest->LocalTarget.MacAddress[3], + FindRouteRequest->LocalTarget.MacAddress[4], + FindRouteRequest->LocalTarget.MacAddress[5], + Status)); + + } + + } else { + // + // First see if we have a route to this network in our + // table. + // + + TempAddress.NetworkAddress = *(UNALIGNED ULONG *)(FindRouteRequest->Network); + // + // [SA] Bug #15094 Copy over the Node address so it can be used in WAN cases + // + + // RtlZeroMemory (TempAddress.NodeAddress, 6); + + *((UNALIGNED ULONG *)TempAddress.NodeAddress) = *((UNALIGNED ULONG *)FindRouteRequest->Node); + *((UNALIGNED USHORT *)(TempAddress.NodeAddress+4)) = *((UNALIGNED USHORT *)(FindRouteRequest->Node+4)); + + Segment = RipGetSegment(FindRouteRequest->Network); + #ifdef _PNP_POWER + // + // Since we maintain the order of locks as Bind > Device > RIP table + // Get the lock up-front. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + #endif + IPX_BEGIN_SYNC (&SyncContext); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + CTEAssert ((sizeof(USHORT)*2) <= sizeof(PVOID)); + + Status = RipGetLocalTarget( + Segment, + &TempAddress, + FindRouteRequest->Type, + &FindRouteRequest->LocalTarget, + (PUSHORT)&FindRouteRequest->Reserved2); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this find route request for completion when the + // RIP response arrives. + // + + CTEAssert (FindRouteRequest->Type != IPX_FIND_ROUTE_NO_RIP); // should never pend + + InsertTailList( + &Device->Segments[Segment].FindWaitingForRoute, + &FindRouteRequest->Linkage); + + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); + #ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + #endif + } + + if (Status != STATUS_PENDING) { + + if (Status == STATUS_SUCCESS && FindRouteRequest->LocalTarget.NicId) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &FindRouteRequest->LocalTarget.NicHandle); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&FindRouteRequest->LocalTarget, Binding->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[FindRouteRequest->LocalTarget.NicId]; + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FindRouteRequest->LocalTarget.NicId = Binding->NicId; + + } +#endif + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + (BOOLEAN)((Status == STATUS_SUCCESS) ? TRUE : FALSE)); + + } + +} /* IpxInternalFindRoute */ + + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to query + information from us. + +Arguments: + + InternalQueryType - Identifies the type of the query. + + NicId - The ID to query, if needed + + Buffer - Input or output buffer for the query. + + BufferLength - The length of the buffer. + + BufferLengthNeeded - If the buffer is too short, this returns + the length needed. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding; + BOOLEAN BindingNeeded; + ULONG LengthNeeded; + PIPX_LINE_INFO LineInfo; + PUSHORT MaximumNicId; + PULONG ReceiveBufferSpace; + TDI_ADDRESS_IPX UNALIGNED * IpxAddress; + IPX_SOURCE_ROUTING_INFO UNALIGNED * SourceRoutingInfo; + ULONG SourceRoutingLength; + UINT MaxUserData; + PDEVICE Device = IpxDevice; +#ifdef _PNP_POWER + USHORT NicId = NicHandle->NicId; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // First verify the parameters. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_LINE_INFO); + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + BindingNeeded = FALSE; + LengthNeeded = sizeof(USHORT); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + BindingNeeded = FALSE; // for now we don't need it + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(ULONG); + break; + + case IPX_QUERY_IPX_ADDRESS: + + if ((NicId == 0) && + (BufferLength >= sizeof(TDI_ADDRESS_IPX))) { + + RtlCopyMemory (Buffer, &Device->SourceAddress, sizeof(TDI_ADDRESS_IPX)); + return STATUS_SUCCESS; + + } + + BindingNeeded = TRUE; + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_SOURCE_ROUTING_INFO); + break; + +#ifdef _PNP_POWER + // + // These are moved down from NB/SPX to IPX. LengthNeeded is set to 0 + // so we dont return BUFFER_TOO_SMALL here; we assume here that + // Bufferlength is also 0. + // Buffer is actually the IRP here. + // + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + + BindingNeeded = FALSE; + LengthNeeded = 0; + break; +#endif + default: + + return STATUS_NOT_SUPPORTED; + + } + + + if (LengthNeeded > BufferLength) { + if (BufferLengthNeeded != NULL) { + *BufferLengthNeeded = LengthNeeded; + } + return STATUS_BUFFER_TOO_SMALL; + } + + if (BindingNeeded) { + + if (NicId == 0) { + NicId = 1; + } + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding == NULL) || + (!Binding->LineUp)) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + return STATUS_INVALID_PARAMETER; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = IpxDevice->Bindings[NicId]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + return STATUS_INVALID_PARAMETER; + } +#endif + } + + + // + // Now return the data. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + LineInfo = (PIPX_LINE_INFO)Buffer; + LineInfo->LinkSpeed = Binding->MediumSpeed; + LineInfo->MaximumPacketSize = Binding->MaxLookaheadData + sizeof(IPX_HEADER); + LineInfo->MaximumSendSize = Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + LineInfo->MacOptions = Binding->Adapter->MacInfo.MacOptions; + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestExternalNicId); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + IpxAddress = (TDI_ADDRESS_IPX UNALIGNED *)Buffer; + if (!IpxIsAddressLocal(IpxAddress)) { + return STATUS_NO_SUCH_DEVICE; + } + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + ReceiveBufferSpace = (PULONG)Buffer; + *ReceiveBufferSpace = Binding->Adapter->ReceiveBufferSpace; + break; + + case IPX_QUERY_IPX_ADDRESS: + + RtlCopyMemory (Buffer, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + SourceRoutingInfo = (IPX_SOURCE_ROUTING_INFO UNALIGNED *)Buffer; + + MacLookupSourceRouting( + SourceRoutingInfo->Identifier, + Binding, + SourceRoutingInfo->RemoteAddress, + SourceRoutingInfo->SourceRouting, + &SourceRoutingLength); + + // + // Reverse the direction of the source routing since it + // is returned in the outgoing order. + // + + if (SourceRoutingLength > 0) { + SourceRoutingInfo->SourceRouting[0] &= 0x7f; + } + SourceRoutingInfo->SourceRoutingLength = (USHORT)SourceRoutingLength; + + MacReturnMaxDataSize( + &Binding->Adapter->MacInfo, + SourceRoutingInfo->SourceRouting, + SourceRoutingLength, + Binding->MaxSendPacketSize, + &MaxUserData); + + // + // MaxUserData does not include the MAC header but does include + // any extra 802.2 etc. headers, so we adjust for that to get the + // size starting at the IPX header. + // + + SourceRoutingInfo->MaximumSendSize = + MaxUserData - + (Binding->DefHeaderSize - Binding->Adapter->MacInfo.MinHeaderLength); + + break; + + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestType20NicId); + break; + +#ifdef _PNP_POWER + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + // + // Call the TDI query equivalent here. + // + return IpxTdiQueryInformation(Device, (PREQUEST)Buffer); + +#endif + } + +#ifdef _PNP_POWER + // + // If Binding was needed earlier, it was referenced, deref it now. + // + if (BindingNeeded) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + + // + // If we haven't returned failure by now, succeed. + // + + return STATUS_SUCCESS; + +} /* IpxInternalQuery */ + + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER +// RIP not converted yet... +// + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to increment + the inactivity counter on a wan binding. This is done every + minute. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + None. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // + // [BUGBUGZZ] Change to NIC_HANDLE_TO_BINDING later. Not done yet since RIP not changed to + // use NICHANDLE instead of NicId + // + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } +#endif + +} /* IpxInternalIncrementWanInactivity */ + + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to query + the inactivity counter on a wan binding. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + The inactivity counter for this binding. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // Binding = NIC_HANDLE_TO_BINDING(IpxDevice, &NicHandle); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + return Binding->WanInactivityCounter; + + } else { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + CTEAssert (FALSE); + return 0; + + } + +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + return Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + return 0; + + } +#endif + +} /* IpxInternalQueryWanInactivity */ + +#ifdef _PNP_POWER + +VOID +IpxPnPIsnIndicate( + IN PVOID Param +) + +/*++ + +Routine Description: + + This routine goes through the list of adapters and informs (thru' PnP indications) + the ISN drivers bound to IPX about any new adapters that have appeared before the + bind took place. + + This is queued as a work item in the InternalBind routine. + +Arguments: + + Param - the upper driver identifier. + +Return Value: + + None. + +--*/ +{ + ULONG Identifier = (ULONG)Param; + PDEVICE Device=IpxDevice; + ULONG i; + PBINDING Binding; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + // + // Set up the LineInfo struct. + // + + // + // BUGBUG: Do we give Binding-specific information here? + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + switch(Identifier) { + case IDENTIFIER_NB: + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform about all the adapters + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + + if (!Binding) { + continue; + } + + // + // We could have informed the upper driver from IpxBindAdapter + // + if (!Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = TRUE; + + // + // Inform NB - the reserved network/node address is always that of the first + // binding + // + if (i==1) { + IpxPnPInfo.FirstORLastDevice = TRUE; + IpxPnPInfo.NewReservedAddress = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, (USHORT)i); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to NB add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + break; + + case IDENTIFIER_SPX: + // + // For SPX this is called directly, with the IsnInformed flag appropriately set. + // This is done so that the IsnInformed flag cannot be changed under + // us by the BindAdapter routine. + // +#if 0 + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; +#endif + IpxPnPInfo.FirstORLastDevice = TRUE; + // + // Inform of the reserved address only + // + if (Device->VirtualNetwork) { + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + IpxPnPInfo.NetworkAddress = NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IpxPnPInfo.NewReservedAddress = TRUE; + + // IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to SPX add: %lx\n", NIC_ID_TO_BINDING(Device, 1))); +#if 0 + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + + } +} +#endif diff --git a/private/ntos/tdi/isn/ipx/ipx.rc b/private/ntos/tdi/isn/ipx/ipx.rc new file mode 100644 index 000000000..3c403b684 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/ipx.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "ISN IPX Protocol Driver" +#define VER_INTERNALNAME_STR "isnipx.sys" +#define VER_ORIGINALFILENAME_STR "isnipx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/ipx/ipxprocs.h b/private/ntos/tdi/isn/ipx/ipxprocs.h new file mode 100644 index 000000000..0b43029db --- /dev/null +++ b/private/ntos/tdi/isn/ipx/ipxprocs.h @@ -0,0 +1,1675 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxprocs.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + 1. Added new functions - IpxReceivePacket, IpxReceiveIndicationCommon + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if STEFAN_DBG +//#if DBG +#define IpxPrint0(fmt) DbgPrint(fmt) +#define IpxPrint1(fmt,v0) DbgPrint(fmt,v0) +#define IpxPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define IpxPrint0(fmt) +#define IpxPrint1(fmt,v0) +#define IpxPrint2(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define IPX_PACKET_LOG 1 +#endif + +#ifdef IPX_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _IPX_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER IpxHeader; + UCHAR Data[14]; +} IPX_PACKET_LOG_ENTRY, *PIPX_PACKET_LOG_ENTRY; + +#define IPX_PACKET_LOG_LENGTH 128 +extern ULONG IpxPacketLogDebug; +extern USHORT IpxPacketLogSocket; +EXTERNAL_LOCK(IpxPacketLogLock); +extern IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd; + +// +// Bit fields in IpxPacketLogDebug +// + +#define IPX_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to IpxPacketLogSocket +#define IPX_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-IPX) + +#define IPX_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from IpxPacketLogSocket + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (IpxPacketLogDebug & (_Bit)) + +#else // IPX_PACKET_LOG + +#define IpxLogPacket(_MacHeader,_Length,_IpxHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // IPX_PACKET_LOG + +#ifdef _PNP_POWER +// +// In load-only PnP, references are not needed on adapters. This should be changed +// to actually take the reference post 4.0. +// +// BUGBUG: Revisit Post 4.0 - Keep the actual instructions around for ease of activation later. +// +#define IpxReferenceAdapter(_adapter) + // InterlockedIncrement(&(_adapter)->ReferenceCount) + +#define IpxDereferenceAdapter(_adapter) +/* + if (InterlockedDecrement(&(_adapter)->ReferenceCount) == 0) {\ + IpxCloseNdis(_adapter); \ + IpxDestroyAdapter(_adapter);\ + }\ +*/ +#endif + +// +// In load-only PnP case, we dont need the references on bindings. All such references +// have been changed to this macro. +// +#define IpxReferenceBinding1(_Binding, _Type) + +#define IpxDereferenceBinding1(_Binding, _Type) + +#if DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefBinding (_Binding) + +#define IpxDereferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefDevice (_Device) + +#define IpxDereferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefDevice (_Device) + + +#define IpxReferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddress (_Address) + +#define IpxReferenceAddressLock(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressLock (_Address) + +#define IpxDereferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressSync (_Address) + + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFile (_AddressFile) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileLock (_AddressFile) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileSync (_AddressFile) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFile (_AddressFile) + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFileSync (_AddressFile) + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &IpxGlobalInterlock); \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &IpxGlobalInterlock); + +#define IpxReferenceRt(_Rt, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Rt)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefRt (_Rt) + +#define IpxDereferenceRt(_Rt, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Rt)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefRt (_Rt) + +#else // DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + InterlockedIncrement(&(_Binding)->ReferenceCount) + +#define IpxDereferenceBinding(_Binding, _Type) \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define IpxDereferenceDevice(_Device, _Type) \ + IpxDerefDevice (_Device) + +#define IpxReferenceAddress(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxDereferenceAddress(_Address, _Type) \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + IpxDerefAddressSync (_Address) + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG( \ + &(_AddressFile)->ReferenceCount, \ + 1, \ + (_AddressFile)->AddressLock) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + +#define IpxReferenceRt(_Rt, _Type) \ + InterlockedIncrement(&(_Rt)->ReferenceCount) + +#define IpxDereferenceRt(_Rt, _Type) \ + IpxDerefRt (_Rt) + +#endif // DBG + + + +#if DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// This routine compares two node addresses. +// + +#define IPX_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define IPX_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + +// +// This routine does an ordered compare of two node addresses. It +// can handle the first address having the source-routing bit on. +// + +#define IPX_NODE_COMPARE(_A,_B,_R) \ + if ((*(_R) = (*(UNALIGNED SHORT *)(((PUCHAR)(_A))+4) - *(UNALIGNED SHORT *)(((PUCHAR)(_B))+4))) == 0) { \ + *(_R) = ((*(UNALIGNED LONG *)((PUCHAR)(_A)) & 0xffffff7f) - *(UNALIGNED LONG *)((PUCHAR)(_B))); \ + } + + + +// +// Routines in action.c +// + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ); + +VOID +IpxAbortNtfChanges( + IN PVOID ControlChannelContext + ); + +NTSTATUS +IpxIndicateLineUp( + IN PDEVICE Device, + IN USHORT NicId, + IN ULONG Network, + IN UCHAR LocalNode[6], + IN UCHAR RemoteNode[6] + ); + +// +// Routines in adapter.c +// + +VOID +IpxRefBinding( + IN PBINDING Binding + ); + +VOID +IpxDerefBinding( + IN PBINDING Binding + ); + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ); + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ); + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ); + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ); + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ); +#endif + +// +// [FW] New functions added for Forwarder support +// + +NTSTATUS +IpxOpenAdapter( + IN NIC_HANDLE AdapterIndex, + IN ULONG FwdAdapterContext, + OUT PNIC_HANDLE IpxAdapterContext + ); + +NTSTATUS +IpxCloseAdapter( + IN NIC_HANDLE IpxAdapterContext + ); + +// +// Routines in address.c +// + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ); + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ); + +#else + +#define IpxBuildTdiAddress(_AddressBuffer,_Network,_Node,_Socket) { \ + TA_IPX_ADDRESS UNALIGNED * _IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)(_AddressBuffer); \ + _IpxAddress->TAAddressCount = 1; \ + _IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); \ + _IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; \ + _IpxAddress->Address[0].Address[0].NetworkAddress = (_Network); \ + _IpxAddress->Address[0].Address[0].Socket = (_Socket); \ + RtlCopyMemory(_IpxAddress->Address[0].Address[0].NodeAddress, (_Node), 6); \ +} + +#endif + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +IpxOpenAddressM( + IN PDEVICE Device, + IN PREQUEST Request, + IN ULONG Index + ); + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ); + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +IpxRefAddress( + IN PADDRESS Address + ); + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +IpxDerefAddress( + IN PADDRESS Address + ); + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ); + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +#endif + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ); + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in device.c +// + +VOID +IpxRefDevice( + IN PDEVICE Device + ); + +VOID +IpxDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ); + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ); +#endif + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +#ifdef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN CTELockHandle *LockHandle1, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN PADAPTER *AdapterPtr, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxPnPToLoad(); + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ); + +#endif _PNP_POWER + +// +// Routines in event.c +// + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ); + + +// +// Routines in ind.c +// + +// +// [CH] Added these two functions +// +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ); + +NDIS_STATUS +IpxReceiveIndicationCommon( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ); + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ); + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ); + + +// +// Routines in internal.c +// + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ); + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ); + +VOID +IpxInternalFindRoute( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ); + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +#ifdef _PNP_POWER +VOID +IpxPnPIsnIndicate( + IN PVOID Param +); +#endif + +// +// Routines in ndis.c +// + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ); + +VOID +IpxDeregisterProtocol( + VOID + ); + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ); + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ); + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ); + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ); + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ); + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ); + + +#ifdef _PNP_POWER +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ); + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ); + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ); +#endif // _PNP_POWER + +// +// Routines in mac.c +// + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ); + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ); + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ); + +#ifdef _PNP_POWER +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); +#endif + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ); + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR NextRouter[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ); + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ); + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ); + + +// +// Routines in packet.c +// + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); +#endif + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); +#endif + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ); + +#if BACK_FILL +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ); +#endif + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ); + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ); + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + IN PDEVICE Device + ); + +#if BACK_FILL +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + IN PDEVICE Device + ); +#endif + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ); + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ); + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ); + + + +// +// Routines in query.c +// + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in receive.c +// + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ); + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ); + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in rip.c +// + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ); + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ); + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ); + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ); + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ); + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ); + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ); + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ); + +UINT +RipGetSegment( + IN UCHAR Network[4] + ); + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ); + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ); + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ); + +VOID +RipDropRemoteEntries( + VOID + ); + + +// +// Routines in send.c +// + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ); + + +// +// Routines in rt.c +// + + +NTSTATUS +GetNewNics( + PDEVICE, + PREQUEST, + BOOLEAN, + PNWLINK_ACTION, + UINT, + BOOLEAN OldIrp +); + +NTSTATUS +OpenRtAddress( + IN PDEVICE Device, + IN PIRP Request + ); + +NTSTATUS +CloseRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp + ); + +NTSTATUS +CleanupRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp + ); + +NTSTATUS +SendIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ); + +NTSTATUS +RcvIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ); +NTSTATUS +PassDgToRt ( + IN PDEVICE pDevice, + IN PIPX_DATAGRAM_OPTIONS2 pContext, + IN ULONG Index, + IN VOID UNALIGNED *pDgrm, + IN ULONG uNumBytes + ); + +NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN PDEVICE pDevice + ); + + +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength); + +VOID +IpxRefRt( + PRT_INFO pRt); + +VOID +IpxDerefRt( + PRT_INFO pRt); + +VOID +IpxDestroyRt( + PRT_INFO pRt); + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ); +#else +#define IpxConstructHeader(_Header,_PacketLength,_PacketType,_RemoteAddress,_LocalAddress) { \ + PIPX_HEADER _IpxHeader = (PIPX_HEADER)(_Header); \ + _IpxHeader->CheckSum = 0xffff; \ + _IpxHeader->PacketLength[0] = (UCHAR)((_PacketLength) / 256); \ + _IpxHeader->PacketLength[1] = (UCHAR)((_PacketLength) % 256); \ + _IpxHeader->TransportControl = 0; \ + _IpxHeader->PacketType = (_PacketType); \ + RtlCopyMemory(_IpxHeader->DestinationNetwork, (PVOID)(_RemoteAddress), 12); \ + RtlCopyMemory(_IpxHeader->SourceNetwork, (_LocalAddress), 12); \ +} +#endif + +// +// Routines in loopback.c +// + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ); + +VOID +IpxInitLoopback(); + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ); + + +// +// [FW] InternalSendCompletion from Forwarder +// + +// [FW] Added length here +VOID +IpxInternalSendComplete( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN NTSTATUS Status + ); diff --git a/private/ntos/tdi/isn/ipx/ipxtypes.h b/private/ntos/tdi/isn/ipx/ipxtypes.h new file mode 100644 index 000000000..a353a65b4 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/ipxtypes.h @@ -0,0 +1,2144 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxtypes.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#ifdef SNMP +#include +#endif SNMP + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _IPX_SEND_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN SendInProgress; // used in an NdisSend + BOOLEAN OwnedByAddress; // packet is owned by an address + UCHAR DestinationType; // one of DEF, BCAST, MCAST + struct _IPX_PADDING_BUFFER * PaddingBuffer; // if one was allocated + PNDIS_BUFFER PreviousTail; // if padding buffer was appended +#ifdef _PNP_POWER + IPX_LOCAL_TARGET LocalTarget; + USHORT CurrentNicId; // current binding being tried for net 0 sends + ULONG PacketLength; // length that comes into IpxSendFrame initially + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY WaitLinkage; // when on WaitingForRoute/WaitingRipPackets +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + + // + // The next fields are used differently depending on whether + // the packet is being used for a datagram send or a rip request. + // + + union { + struct { + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on + USHORT CurrentNicId; // current binding being tried for net 0 sends + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends + BOOLEAN OutgoingSap; // packet is sent from the SAP socket + } SR_DG; + struct { + ULONG Network; // net we are looking for + USHORT CurrentNicId; // current binding being tried + UCHAR RetryCount; // number of times sent; 0xfe = response, 0xff = down + BOOLEAN RouteFound; // network has been found + USHORT SendTime; // timer expirations when sent. + BOOLEAN NoIdAdvance; // don't advance CurrentNicId this time. + } SR_RIP; + } u; + + PUCHAR Header; // points to the MAC/IPX header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header; +#if BACK_FILL + BOOLEAN BackFill; // 1 if we are using SMB's extended header + PNDIS_BUFFER IpxHeader; // Place holder for our IpxHeader + PNDIS_BUFFER MacHeader; // Place holder for our mac header + PVOID MappedSystemVa; + PVOID ByteOffset; + LONG UserLength; +#endif +} IPX_SEND_RESERVED, *PIPX_SEND_RESERVED; + +// +// Values for the DestinationType field. +// + +#define DESTINATION_DEF 1 +#define DESTINATION_BCAST 2 +#define DESTINATION_MCAST 3 + +// +// Used to indicate to IpxReceiveIndication that this is a loopback packet +// Assumption: Ndis cannot return this as the NdisBindingHandle value since +// that is a pointer (our pointers shd in kernel space, if not in Nonpaged pool). +// +#define IPX_LOOPBACK_COOKIE 0x00460007 + +// +// MIN/MAX macros +// +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#ifdef _PNP_POWER + +// +// In order to avoid a lock to read a value, this is used. +// As long as the final value has made it to _b by the time +// the check is made, this works fine. +// + +#define ASSIGN_LOOP(_a, _b) \ + do { \ + _a = _b; \ + } while ( _a != _b ); + +// +// Gets the value of a Ulong (possibly a pointer) by adding 0 in an interlocked manner. +// This relies on the fact that the return of the ExchangeAdd will be the value prior to +// addition. Since the value added is 0, the final value stays the same. +// +#define GET_VALUE(x) \ + InterlockedExchangeAdd((PULONG)&(x), 0) + +#define SET_VALUE(x,y) \ + InterlockedExchange((PLONG)&(x), (LONG)(y)) + +/* +PBINDING +NIC_ID_TO_BINDING ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// We need to ensure that the binding array pointer is valid hence use the interlocked operation. +// Also, the binding pointer read out of the array should be valid. Since the bindings are never +// freed (IPX maintains a pool of bindings), the pointer thus retrieved will always point to +// memory that belongs to us, which in the worst case could point to a re-claimed binding block. +// +// BUGBUGZZ: we can eliminate the second interlock if we always ensure that the bindings in an array +// dont change i.e. when we move around bindings, do them in a copy and make that the master (thru' +// a single ulong exchange). +// +// A problem that still remains here is that even if we get a valid (IPX owned non-paged) ptr out of +// the array, we still cannot atomically get a ref on the binding +// We might need those locks after all.... (revisit post SUR when the delete is enabled). +// + +// +// NicId cast to SHORT so DemandDial Nic (0xffff) maps to -1. +// +#define NIC_ID_TO_BINDING(_device, _nicid) \ + ((PBINDING)GET_VALUE( ((PBIND_ARRAY_ELEM) GET_VALUE( (_device)->Bindings) )[(SHORT)_nicid].Binding )) + +/* +PBINDING +NIC_ID_TO_BINDING_NO_ILOCK ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// No interlocked operations are used here to get to the binding. This is used in the PnP add/delete +// adapter paths on the assumption that NDIS will serialize the addition/deletion of cards. [JammelH: 5/15/96] +// +#define NIC_ID_TO_BINDING_NO_ILOCK(_device, _nicid) \ + ((_device)->Bindings[_nicid].Binding) + +/* +VOID +INSERT_BINDING( + IN PDEVICE _device, + IN USHORT _nicid, + IN PBINDING _binding + ) +*/ +// +// We dont do a get_value for the first arg of the macro since we are the writer and +// this value cannot change from under us here (NDIS will not give us two PnP Add adapter +// indications simultaneously). +// +#define INSERT_BINDING(_device, _nicid, _binding) \ + SET_VALUE((_device)->Bindings[_nicid].Binding, (_binding)); + +/* +VOID +SET_VERSION( + IN PDEVICE _device, + IN USHORT _nicid + ) +*/ +#define SET_VERSION(_device, _nicid) \ + SET_VALUE((_device)->Bindings[_nicid].Version, ++(_device)->BindingVersionNumber); + +/* +PBINDING +NIC_HANDLE_TO_BINDING ( + IN PDEVICE _device, + IN PNIC_HANDLE _nichandle, + ); +*/ +#ifdef _PNP_LATER +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + (((_nichandle)->Signature == IPX_BINDING_SIGNATURE) && \ + ((_nichandle)->Version == (_device)->Bindings[(_nichandle)->NicId].Version)) ? \ + (_device)->Bindings[(_nichandle)->NicId].Binding : NULL; +#else + +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + NIC_ID_TO_BINDING(_device, (_nichandle)->NicId); +#endif + +/* +VOID +FILL_LOCAL_TARGET( + IN PLOCAL_TARGET _localtarget, + IN USHORT _nicid + ) +*/ + +#define FILL_LOCAL_TARGET(_localtarget, _nicid) \ + NIC_HANDLE_FROM_NIC((_localtarget)->NicHandle, _nicid) + +#define NIC_FROM_LOCAL_TARGET(_localtarget) \ + (_localtarget)->NicHandle.NicId + +#endif _PNP_POWER + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _IPX_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + BOOLEAN OwnedByAddress; // packet is owned by an address +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + PREQUEST SingleRequest; // if transfer is for one only + struct _IPX_RECEIVE_BUFFER * ReceiveBuffer; // if transfer is for multiple requests + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY Requests; // waiting on this transfer + PVOID pContext; + ULONG Index; +} IPX_RECEIVE_RESERVED, *PIPX_RECEIVE_RESERVED; + +// +// The amount of data we need in our standard header, rounded up +// to the next longword bounday. +// +// [BUGBUGZZ] Make this declaration in one place +// +#define PACKET_HEADER_SIZE (MAC_HEADER_SIZE + IPX_HEADER_SIZE + RIP_PACKET_SIZE) + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +// #define IPX_OWN_PACKETS 1 + +#define IpxAllocateSendPacket(_Device,_SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_SendPacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define IpxAllocateReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_ReceivePacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#ifdef IPX_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _IPX_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_SEND_RESERVED)]; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_RECEIVE_RESERVED)]; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define IpxFreeSendPacket(_Device,_Packet) + +#define IpxFreeReceivePacket(_Device,_Packet) + +#else // IPX_OWN_PACKETS + +typedef struct _IPX_SEND_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define IpxAllocateSingleSendPacket(_Device,_SendPacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_SendPacket)->PoolHandle,1,sizeof(IPX_SEND_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, (_SendPacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxAllocateSingleReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_ReceivePacket)->PoolHandle,1,sizeof(IPX_RECEIVE_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, (_ReceivePacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxFreeSingleSendPacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ +} +#define IpxFreeSingleReceivePacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ +} + +#define IpxFreeSendPacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#define IpxFreeReceivePacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#endif // IPX_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PIPX_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PIPX_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + +// +// This is the structure that contains a receive buffer for +// datagrams that are going to multiple recipients. +// + +typedef struct _IPX_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#ifdef IPX_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + PUCHAR Data; // the actual data +} IPX_RECEIVE_BUFFER, *PIPX_RECEIVE_BUFFER; + + +// +// This is the structure that contains a padding buffer for +// padding ethernet frames out to an even number of bytes. +// + +typedef struct _IPX_PADDING_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + UCHAR Data[1]; // the actual pad data +} IPX_PADDING_BUFFER, *PIPX_PADDING_BUFFER; + +#ifdef IPX_OWN_PACKETS + +typedef struct _IPX_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_SEND_PACKET Packets[1]; +} IPX_SEND_POOL, *PIPX_SEND_POOL; + +typedef struct _IPX_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_RECEIVE_PACKET Packets[1]; +} IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +#else + +typedef struct _IPX_PACKET_POOL { + LIST_ENTRY Linkage; + PUCHAR Header; + NDIS_HANDLE PoolHandle; +} IPX_PACKET_POOL, *PIPX_PACKET_POOL; + +typedef IPX_PACKET_POOL IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +typedef IPX_PACKET_POOL IPX_SEND_POOL, *PIPX_SEND_POOL; + +#endif // IPX_OWN_PACKETS + +typedef struct _IPX_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; + IPX_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} IPX_RECEIVE_BUFFER_POOL, *PIPX_RECEIVE_BUFFER_POOL; + +// +// Number of upper drivers we support. +// + +#define UPPER_DRIVER_COUNT 3 + + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_RIP 4 +#define MEMORY_SOURCE_ROUTE 5 +#define MEMORY_BINDING 6 +#define MEMORY_QUERY 7 + +#define MEMORY_MAX 8 + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(IpxMemoryInterlock); +extern MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +#endif + + +// +// This defines the reasons we delete rip entries for a binding. +// + +typedef enum _IPX_BINDING_CHANGE_TYPE { + IpxBindingDeleted, + IpxBindingMoved, + IpxBindingDown +} IPX_BINDING_CHANGE_TYPE, *PIPX_BINDING_CHANGE_TYPE; + + +// +// This structure contains information about a single +// source routing entry. +// + +typedef struct _SOURCE_ROUTE { + + struct _SOURCE_ROUTE * Next; // next in hash list + + UCHAR MacAddress[6]; // remote MAC address + UCHAR TimeSinceUsed; // timer expirations since last used + UCHAR SourceRoutingLength; // length of the data + + UCHAR SourceRouting[1]; // source routing data, stored as received in + +} SOURCE_ROUTE, *PSOURCE_ROUTE; + +#define SOURCE_ROUTE_SIZE(_SourceRoutingLength) \ + (FIELD_OFFSET(SOURCE_ROUTE, SourceRouting[0]) + (_SourceRoutingLength)) + +#define SOURCE_ROUTE_HASH_SIZE 16 + +// +// ULONG +// MacSourceRoutingHash( +// IN PUCHAR MacAddress +// ) +// +// /*++ +// +// Routine Description: +// +// This routine returns a hash value based on the MAC address +// that is pointed to. It will be between 0 and SOURCE_ROUTE_HASH_SIZE. +// +// Arguments: +// +// MacAddress - The MAC address. NOTE: The source-routing bit may +// or may not be on in the first byte, this routine will handle +// that. +// +// Return Value: +// +// The hash value. +// +// --*/ +// + +#define MacSourceRoutingHash(_MacAddress) \ + ((ULONG)((_MacAddress)[5] % SOURCE_ROUTE_HASH_SIZE)) + + + +// +// this structure describes a single NDIS adapter that IPX is +// bound to. +// + +struct _DEVICE; + +typedef struct _ADAPTER { + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IAD1" +#endif + +#ifdef _PNP_POWER + ULONG ReferenceCount; +#endif + + ULONG BindingCount; // number bound to this adapter + + // + // Handle returned by the NDIS wrapper after we bind to it. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The queue of (currently receive only) requests waiting to complete. + // + + LIST_ENTRY RequestCompletionQueue; + + // + // IPX header normal offsets for directed and + // broadcast/multicast frames. + // + + ULONG DefHeaderSizes[ISN_FRAME_TYPE_MAX]; + ULONG BcMcHeaderSizes[ISN_FRAME_TYPE_MAX]; + + // + // List of buffers to be used for transfers. + // + + ULONG AllocatedReceiveBuffers; + LIST_ENTRY ReceiveBufferPoolList; + SLIST_HEADER ReceiveBufferList; + + // + // List of ethernet padding buffers. + // + + ULONG AllocatedPaddingBuffers; + SINGLE_LIST_ENTRY PaddingBufferList; + + struct _BINDING * Bindings[ISN_FRAME_TYPE_MAX]; // the binding for each frame type. + + // + // TRUE if broadcast reception is enabled on this adapter. + // + + BOOLEAN BroadcastEnabled; + + // + // TRUE if we have enabled an auto-detected frame type + // on this adapter -- used to prevent multiple ones. + // + + BOOLEAN AutoDetectFound; + + // + // TRUE if we got a response to at least one of our + // auto-detect frames. + // + + BOOLEAN AutoDetectResponse; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // For WAN adapters, we support multiple bindings per + // adapter, all with the same frame type. For them we + // demultiplex using the local mac address. This stores + // the range of device NIC IDs associated with this + // particular address. + // + + USHORT FirstWanNicId; + USHORT LastWanNicId; + ULONG WanNicIdCount; + + // + // This is based on the configuration. + // + + USHORT BindSap; // usually 0x8137 + USHORT BindSapNetworkOrder; // usually 0x3781 + BOOLEAN SourceRouting; + BOOLEAN EnableFunctionalAddress; + BOOLEAN EnableWanRouter; + ULONG ConfigMaxPacketSize; + + // + // TRUE if the tree is empty, so we can check quickly. + // + + BOOLEAN SourceRoutingEmpty[IDENTIFIER_TOTAL]; + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR AdapterName; + ULONG AdapterNameLength; + + struct _DEVICE * Device; + + CTELock Lock; + CTELock * DeviceLock; + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // The value of Device->SourceRoutingTime the last time + // we checked the list for timeouts (this is so we can + // tell in the timeout code when two bindings point to the + // same adapter). + // + + CHAR LastSourceRoutingTime; + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + NDIS_STATUS OpenErrorStatus; // if Status is NDIS_STATUS_OPEN_FAILED. + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + NDIS_INFORMATION MacInfo; + + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + ULONG ReceiveBufferSpace; // as queried from the card + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + + // + // The source routing tree for each of the identifiers + // + + PSOURCE_ROUTE SourceRoutingHeads[IDENTIFIER_TOTAL][SOURCE_ROUTE_HASH_SIZE]; + +} ADAPTER, * PADAPTER; + +#define ASSERT_ADAPTER(_Adapter) \ + CTEAssert (((_Adapter)->Type == IPX_ADAPTER_SIGNATURE) && ((_Adapter)->Size == sizeof(ADAPTER))) + + +// +// These are the media and frame type specific MAC header +// constructors that we call in the main TDI send path. +// + +typedef NDIS_STATUS +(*IPX_SEND_FRAME_HANDLER) ( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +// +// These are the states a WAN line can be in. +// +typedef enum _WAN_LINE_STATE{ + LINE_DOWN, + LINE_UP, + LINE_CONFIG +} WAN_LINE_STATE, *PWAN_LINE_STATE; + +#define BREF_BOUND 1 +#ifdef _PNP_POWER +#define BREF_DEVICE_ACCESS 2 +#define BREF_ADAPTER_ACCESS 3 +#endif + +// +// [FW] New flag to indicate the KFWD opened an adapter +// +#define BREF_FWDOPEN 4 + +#define BREF_TOTAL 5 + +typedef struct _BINDING { + +#if DBG + ULONG RefTypes[BREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IBI1" +#endif + + ULONG ReferenceCount; + +#ifdef _PNP_POWER + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +#endif + + // + // Adapter this binding is on. + // + + PADAPTER Adapter; + + // + // ID identifying us to the system (will be the index + // in Device->Bindings[]). + // + + USHORT NicId; + + // + // For LANs these will be the same as the adapter's, for WANs + // they change on line up indications. + // + + ULONG MaxSendPacketSize; + ULONG MediumSpeed; // in units of 100 bytes/sec + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // This is used for WAN lines, all sends go to this address + // which is given on line up. + // + + HARDWARE_ADDRESS RemoteMacAddress; + + // + // For WAN lines, holds the remote address indicated to us + // in the IPXCP_CONFIGURATION structure -- this is used to + // select a binding to send to when WanGlobalNetworkNumber + // is TRUE. + // + + UCHAR WanRemoteNode[6]; + + // + // TRUE if this binding was set up to allow auto-detection, + // instead of being configured explicitly in the registry. + // + + BOOLEAN AutoDetect; + + // + // TRUE if this binding was set up for auto-detection AND + // was the default in the registry. + // + + BOOLEAN DefaultAutoDetect; + + // + // During auto-detect when we are processing responses from + // various networks, these keep track of how many responses + // we have received that match the current guess at the + // network number, and how many don't (the current guess + // is stored in TentativeNetworkAddress). + // + + USHORT MatchingResponses; + USHORT NonMatchingResponses; + + // + // During auto-detect, stores the current guess at the + // network number. + // + + ULONG TentativeNetworkAddress; + + // + // TRUE if this binding is part of a binding set. + // + + BOOLEAN BindingSetMember; + + // + // TRUE if this binding should receive broadcasts (this + // rotates through the members of a binding set). + // + + BOOLEAN ReceiveBroadcast; + + // + // TRUE for WAN lines if we are up. + // + // BOOLEAN LineUp; + WAN_LINE_STATE LineUp; + + // + // TRUE if this is a WAN line and is dialout. + // + + BOOLEAN DialOutAsync; + + union { + + // + // Used when a binding is active, if it is a member + // of a binding set. + // + + struct { + + // + // Used to link members of a binding set in a circular list. + // NULL for non-set members. + // + + struct _BINDING * NextBinding; + + // + // If this binding is a master of a binding set, this points + // to the binding to use for the next send. For other members + // of a binding set it is NULL. We use this to determine + // if a binding is a master or not. + // + + struct _BINDING * CurrentSendBinding; + + // + // For binding set members, points to the master binding + // (if this is the master it points to itself). + // + + struct _BINDING * MasterBinding; + + }; + + // + // This is used when we are first binding to adapters, + // and the device's Bindings array is not yet allocated. + // + + LIST_ENTRY InitialLinkage; + + }; + + // + // Used by rip to keep track of unused wan lines. + // + + ULONG WanInactivityCounter; + + // + // Our local address, we don't use the socket but we keep + // it here so we can do quick copies. It contains the + // real network that we are bound to and our node + // address on that net (typically the adapter's MAC + // address but it will change for WANs). + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_FRAME_HANDLER SendFrameHandler; + + struct _DEVICE * Device; + + CTELock * DeviceLock; + + ULONG DefHeaderSize; // IPX header offset for directed frames + ULONG BcMcHeaderSize; // IPX header offset for broadcast/multicast + + ULONG AnnouncedMaxDatagramSize; // what we advertise -- assumes worst-case SR + ULONG RealMaxDatagramSize; // what will really break the card + ULONG MaxLookaheadData; + + // + // Configuration parameters. We overlay all of them except + // FrameType over the worker thread item we use to delay + // deletion -- all the others are not needed once the + // binding is up. Some of the config parameters are stored + // in the adapter, these are the ones that are modified + // per-binding. + // + + ULONG FrameType; + union { + struct { + ULONG ConfiguredNetworkNumber; + BOOLEAN AllRouteDirected; + BOOLEAN AllRouteBroadcast; + BOOLEAN AllRouteMulticast; + }; + WORK_QUEUE_ITEM WanDelayedQueueItem; + }; + + ULONG FwdAdapterContext; // [FW] + + ULONG InterfaceIndex; // [FW] + + ULONG ConnectionId; // [FW] used to match TimeSinceLastActivity IOCtls + + ULONG IpxwanConfigRequired; // [FW] used to indicate to the adapter dll whether the line up is for Router or IpxWan. + + BOOLEAN fInfoIndicated; //Info indicated to user app + +#ifdef _PNP_POWER + // + // Indicates whether this binding was indicated to the ISN driver + // + BOOLEAN IsnInformed[UPPER_DRIVER_COUNT]; + + // + // Keeps the NetAddressRegistrationHandle. + // + HANDLE TdiRegistrationHandle; +#endif +} BINDING, * PBINDING; + + +#ifdef _PNP_POWER +typedef struct _IPX_BINDING_POOL { + LIST_ENTRY Linkage; + UINT BindingCount; + BINDING Bindings[1]; +} IPX_BINDING_POOL, *PIPX_BINDING_POOL; +#endif + +// +// This structure defines the control structure for a single +// router table segment. +// + +typedef struct _ROUTER_SEGMENT { + LIST_ENTRY WaitingForRoute; // packets waiting for a route in this segment + LIST_ENTRY FindWaitingForRoute; // find route requests waiting for a route in this segment + LIST_ENTRY WaitingLocalTarget; // QUERY_IPX_LOCAL_TARGETs waiting for a route in this segment + LIST_ENTRY WaitingReripNetnum; // MIPX_RERIPNETNUMs waiting for a route in this segment + LIST_ENTRY Entries; + PLIST_ENTRY EnumerateLocation; +} ROUTER_SEGMENT, *PROUTER_SEGMENT; + + +// +// Number of buckets in the address hash table. This is +// a multiple of 2 so hashing is quick. +// + +#define IPX_ADDRESS_HASH_COUNT 16 + +// +// Routine to convert a socket to a hash index. We use the +// high bits because it is stored reversed. +// + +#define IPX_HASH_SOCKET(_S) ((((_S) & 0xff00) >> 8) % IPX_ADDRESS_HASH_COUNT) + +// +// This macro gets the socket hash right out of the IPX header. +// + +#define IPX_DEST_SOCKET_HASH(_IpxHeader) (((PUCHAR)&(_IpxHeader)->DestinationSocket)[1] % IPX_ADDRESS_HASH_COUNT) + + +// +// This structure defines the per-device structure for IPX +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_SR_TIMER 4 +#define DREF_RIP_TIMER 5 +#define DREF_LONG_TIMER 6 +#define DREF_RIP_PACKET 7 +#define DREF_ADDRESS_NOTIFY 8 +#define DREF_LINE_CHANGE 9 +#define DREF_NIC_NOTIFY 10 + +#define DREF_TOTAL 12 + +#ifdef _PNP_POWER +// +// Pre-allocated binding array size +// +#define MAX_BINDINGS 50 +#endif _PNP_POWER + +#ifdef _PNP_POWER +// +// Our new binding array is composed of the following binding +// array element +// +typedef struct _BIND_ARRAY_ELEM { + PBINDING Binding; + ULONG Version; +} BIND_ARRAY_ELEM, *PBIND_ARRAY_ELEM; + +#endif _PNP_POWER + +typedef struct _DEVICE { + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + CTELock Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + + // + // These are temporary versions of these counters, during + // timer expiration we update the real ones. + // + + ULONG TempDatagramBytesSent; + ULONG TempDatagramsSent; + ULONG TempDatagramBytesReceived; + ULONG TempDatagramsReceived; + + // + // Configuration parameters. + // + + BOOLEAN EthernetPadToEven; + BOOLEAN SingleNetworkActive; + BOOLEAN DisableDialoutSap; + + // + // TRUE if we have multiple cards but a virtual network of 0. + // + + BOOLEAN MultiCardZeroVirtual; + + CTELock Lock; + + // + // Lock to access the sequenced lists in the device. + // + CTELock SListsLock; + + LONG ReferenceCount; // activity count/this provider. + +#ifdef _PNP_POWER + + // + // Lock used to control the access to a binding (either from the + // binding array in the device or from the binding array in the + // adapter. + // + CTELock BindAccessLock; + + // + // Registry Path for use when PnP adapters appear. + // + PWSTR RegistryPathBuffer; + + UNICODE_STRING RegistryPath; + + // + // Binding array has the Version number too + // + PBIND_ARRAY_ELEM Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + + // + // Monotonically increasing version number kept in bindings. + // Hopefully this will not wrap around... + // + ULONG BindingVersionNumber; +#else + // + // During init we hold all bindings in a queue, but after we + // know the approximate number we allocate an array. + // + + union { + LIST_ENTRY InitialBindingList; // only used during init. + struct { + PBINDING * Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + }; + }; +#endif _PNP_POWER + + + // + // ValidBindings is the number of bindings in the array which may + // be valid (they are lan bindings or down wan binding placeholders). + // It will be less than BindingCount by the number of auto-detect + // bindings that are thrown away. HighestExternalNicId is ValidBindings + // minus any binding set slaves which are moved to the end of the + // array. SapNicCount is like HighestExternalNicId except that + // if WanGlobalNetworkNumber is TRUE it will count all WAN bindings + // as one. HighestExternalType20NicId is like HighestExternalNicId + // except it stops when all the remaining bindings are down wan + // lines, or dialin wan lines if DisableDialinNetbios bit 1 is on. + // + + USHORT ValidBindings; + USHORT HighestExternalNicId; + USHORT SapNicCount; + USHORT HighestType20NicId; +#ifdef _PNP_POWER + // + // Keeps track of the last LAN binding's position in the binding array + // + USHORT HighestLanNicId; + + // + // This keeps track of the current size of the binding array + // + USHORT MaxBindings; +#endif _PNP_POWER + + // + // [FW] To keep track of the number of WAN lines currently UP + // + USHORT UpWanLineCount; + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + +#if BACK_FILL + LIST_ENTRY GlobalBackFillPacketList; +#endif + + // + // Action requests from SAP waiting for an adapter status to change. + // + + LIST_ENTRY AddressNotifyQueue; + + // + // Action requests from nwrdr waiting for the WAN line + // to go up/down. + // + + LIST_ENTRY LineChangeQueue; + + // + // Action requests from forwarder waiting for the NIC change notification + // + LIST_ENTRY NicNtfQueue; + LIST_ENTRY NicNtfComplQueue; + + // + // All packet pools are chained on these lists. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + + +#if BACK_FILL + LIST_ENTRY BackFillPoolList; + SLIST_HEADER BackFillPacketList; +#endif + +#ifdef _PNP_POWER + LIST_ENTRY BindingPoolList; + SLIST_HEADER BindingList; +#endif + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + PIPX_PADDING_BUFFER PaddingBuffer; + + UCHAR State; + + UCHAR FrameTypeDefault; + + // + // This holds state if SingleNetworkActive is TRUE. If + // it is TRUE then WAN nets are active; if it is FALSE + // then LAN nets are active. + // + + BOOLEAN ActiveNetworkWan; + + // + // TRUE if we have a virtual network. + // + + BOOLEAN VirtualNetwork; + + // + // If we are set up for SingleNetworkActive, we may have + // to start our broadcast of net 0 frames somewhere other + // than NIC ID 1, so that we don't send to the wrong type. + // + + USHORT FirstLanNicId; + USHORT FirstWanNicId; + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many of various resources have been allocated. + // + + ULONG AllocatedDatagrams; + ULONG AllocatedReceivePackets; + ULONG AllocatedPaddingBuffers; + + // + // Other configuration parameters. + // + + ULONG InitDatagrams; + ULONG MaxDatagrams; + ULONG RipAgeTime; + ULONG RipCount; + ULONG RipTimeout; + ULONG RipUsageTime; + ULONG SourceRouteUsageTime; + USHORT SocketStart; + USHORT SocketEnd; + ULONG SocketUniqueness; + ULONG VirtualNetworkNumber; + ULONG EthernetExtraPadding; + BOOLEAN DedicatedRouter; + BOOLEAN VirtualNetworkOptional; + UCHAR DisableDialinNetbios; + + // + // These are currently not read from the registry. + // + + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG MaxReceivePackets; + ULONG MaxReceiveBuffers; + +#ifdef _PNP_POWER + ULONG MaxPoolBindings; + ULONG AllocatedBindings; + ULONG InitBindings; +#endif + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + LARGE_INTEGER ControlChannelIdentifier; + + // + // This registry parameter controls whether IPX checks (and discards) + // packets with mismatched Source addresses in the receive path. + // + BOOLEAN VerifySourceAddress; + + // + // Where the current socket allocation is. + // + USHORT CurrentSocket; + + // + // Number of segments in the RIP database. + // + + ULONG SegmentCount; + + // + // Points to an array of locks for the RIP database (these + // are stored outside of the ROUTER_SEGMENT so the array + // can be exposed to the RIP upper driver as one piece). + // + + CTELock *SegmentLocks; + + // + // Points to an array of ROUTER_SEGMENT fields for + // various RIP control fields. + // + + ROUTER_SEGMENT *Segments; + + // + // Queue of RIP packets waiting to be sent. + // + + LIST_ENTRY WaitingRipPackets; + ULONG RipPacketCount; + + // + // Timer that keeps RIP requests RIP_GRANULARITY ms apart. + // + + BOOLEAN RipShortTimerActive; + USHORT RipSendTime; + CTETimer RipShortTimer; + + // + // Timer that runs to age out unused rip entries (if the + // router is not bound) and re-rip every so often for + // active entries. + // + + CTETimer RipLongTimer; + + // + // This controls the source routing timeout code. + // + + BOOLEAN SourceRoutingUsed; // TRUE if any 802.5 bindings exist. + CHAR SourceRoutingTime; // incremented each time timer fires. + CTETimer SourceRoutingTimer; // runs every minute. + + // + // [FW] Kicks in every minute if at least one WAN line is up. Increments + // the WAN incativity counter on all UP WAN bindings. + // + CTETimer WanInactivityTimer; + + // + // These are the merging of the binding values. + // + + ULONG LinkSpeed; + ULONG MacOptions; + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // A pre-allocated header containing our node and network, + // plus an unused socket (so the structure is a known size + // for easy copying). + // + + TDI_ADDRESS_IPX SourceAddress; + + // + // The following field is an array of list heads of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabases[IPX_ADDRESS_HASH_COUNT]; // list of defined transport addresses. + + // + // Holds the last address we looked up. + // + + PVOID LastAddress; + + NDIS_HANDLE NdisBufferPoolHandle; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + + // + // Information.MaxDatagramSize is the minimum size we can + // send to all bindings assuming worst-case source routing; + // this is the value that won't break any network drivers. + // + + ULONG RealMaxDatagramSize; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // Indicates whether each upper driver is bound + // (Netbios = 0, SPX = 1, RIP = 2). + // + + BOOLEAN ForwarderBound; + + BOOLEAN UpperDriverBound[UPPER_DRIVER_COUNT]; + + // + // TRUE if any driver is bound. + // + + BOOLEAN AnyUpperDriverBound; + + // + // Whether a receive complete should be indicated to + // this upper driver. + // + + BOOLEAN ReceiveCompletePending[UPPER_DRIVER_COUNT]; + + // + // Control channel identifier for each of the upper + // drivers' bindings. + // + + LARGE_INTEGER UpperDriverControlChannel[UPPER_DRIVER_COUNT]; + + // + // Entry points and other information for each of the + // upper drivers. + // + + IPX_INTERNAL_BIND_INPUT UpperDrivers[UPPER_DRIVER_COUNT]; + + // + // How many upper drivers want broadcast enabled. + // + + ULONG EnableBroadcastCount; + + // + // Indicates if an enable broadcast operation is in + // progress. + // + + BOOLEAN EnableBroadcastPending; + + // + // Indicates if a disable broadcast operation is in + // progress. + // + + BOOLEAN DisableBroadcastPending; + + // + // Indicates if the current operation should be + // reversed when it is finished. + // + + BOOLEAN ReverseBroadcastOperation; + + // + // TRUE if RIP wants a single network number for all WANs + // + + BOOLEAN WanGlobalNetworkNumber; + + // + // If WanGlobalNetworkNumber is TRUE, then this holds the + // actual value of the network number, once we know it. + // + + ULONG GlobalWanNetwork; + + // + // Set to TRUE if WanGlobalNetworkNumber is TRUE and we + // have already completed a queued notify from SAP. In + // this case GlobalWanNetwork will be set correctly. + // + + BOOLEAN GlobalNetworkIndicated; + + // + // TRUE if we need to act as a RIP announcer/responder + // for our virtual net. + // + + BOOLEAN RipResponder; + + // + // TRUE if we have already logged an error because someone + // sent a SAP response but we have multiple cards with no + // virtual network. + // + + BOOLEAN SapWarningLogged; + + // + // Used to queue up a worker thread to perform + // broadcast operations. + // + + WORK_QUEUE_ITEM BroadcastOperationQueueItem; + +#ifdef _PNP_POWER + // + // Used to queue up a worker thread to perform + // PnP indications to upper drivers. + // + + WORK_QUEUE_ITEM PnPIndicationsQueueItem; +#endif + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + + // + // Counters for most of the statistics that IPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + + // + // This is TRUE if we have any adapters where we are + // auto-detecting the frame type. + // + + BOOLEAN AutoDetect; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // Our state during auto-detect. After we are done this + // will stay at AutoDetectDone; + // + + UCHAR AutoDetectState; + + // + // If we are auto-detecting, this event is used to stall + // our initialization code while we do auto-detection -- + // this is so we have a constant view of the world once + // we return from DriverEntry. + // + + KEVENT AutoDetectEvent; + + // + // Counters for "active" time. + // + + LARGE_INTEGER IpxStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // Points back to the system device object. + // + + PDEVICE_OBJECT DeviceObject; + +#ifdef _PNP_POWER + // + // Used to store the Tdi registration handle for deviceobject notifications. + // + HANDLE TdiRegistrationHandle; + + // + // Used to store the TA_ADDRESS which is indicated up to Tdi clients as adapters appear. + // + PTA_ADDRESS TdiRegistrationAddress; +#endif + +#ifdef SNMP + NOVIPXMIB_BASE MibBase; +#endif SNMP + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; + ULONG DeviceNameLength; + +} DEVICE, * PDEVICE; + + +extern PDEVICE IpxDevice; +extern PIPX_PADDING_BUFFER IpxPaddingBuffer; +#if DBG +EXTERNAL_LOCK(IpxGlobalInterlock); +#endif + +#ifdef SNMP +#define IPX_MIB_ENTRY(Device, Variable) ((Device)->MibBase.Variable) +#endif SNMP + +// +// device state definitions +// + +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 + +#ifdef _PNP_POWER + +// +// New state which comes between CLOSED and OPEN. At this state, +// there are no adapters in the system and so no network activity +// is possible. +// +#define DEVICE_STATE_LOADED 0x03 +#endif _PNP_POWER + +// +// This is the state of our auto-detect if we do it. +// + +#define AUTO_DETECT_STATE_INIT 0x00 // still initializing the device +#define AUTO_DETECT_STATE_RUNNING 0x01 // sent ffffffff query, waiting for responses +#define AUTO_DETECT_STATE_PROCESSING 0x02 // processing the responses +#define AUTO_DETECT_STATE_DONE 0x03 // detection is done, IPX is active + + + +#define IPX_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + CTELock * AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST Request; // the request used for open or close + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + // + // + // TRUE if ExtendedAddressing, ReceiveIpxHeader, + // FilterOnPacketType, or ReceiveFlagAddressing is TRUE. + // + + BOOLEAN SpecialReceiveProcessing; + + // + // The remote address of a send datagram includes the + // packet type. and on a receive datagram includes + // the packet type AND a flags byte indicating information + // about the frame (was it broadcast, was it sent from + // this machine). + // + + BOOLEAN ExtendedAddressing; + + // + // TRUE if the address on a receive datagram includes + // the packet type and a flags byte (like ExtendedAddressing), + // but on send the address is normal (no packet type). + // + + BOOLEAN ReceiveFlagsAddressing; + + // + // Is the IPX header received with the data. + // + + BOOLEAN ReceiveIpxHeader; + + // + // The packet type to use if it is unspecified in the send. + // + + UCHAR DefaultPacketType; + + // + // TRUE if packet type filtering is enabled. + // + + BOOLEAN FilterOnPacketType; + + // + // The packet type to filter on. + // + + UCHAR FilteredType; + + // + // Does this address file want broadcast packets. + // + + BOOLEAN EnableBroadcast; + + // + // This is set to TRUE if this is the SAP socket -- we + // put this under SpecialReceiveProcessing to avoid + // hitting the main path. + // + + BOOLEAN IsSapSocket; + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + // + // [CH] Added the chained receive handlers. + // + + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredChainedReceiveDatagramHandler; + BOOLEAN RegisteredErrorHandler; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + PTDI_IND_CHAINED_RECEIVE_DATAGRAM ChainedReceiveDatagramHandler; + PVOID ChainedReceiveDatagramHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + +/* ULONGs to allow for Interlocked operations. + + BOOLEAN SendPacketInUse; // put these after so header is aligned. + + BOOLEAN ReceivePacketInUse; +#if BACK_FILL + BOOLEAN BackFillPacketInUse; +#endif +*/ + + ULONG SendPacketInUse; // put these after so header is aligned. + + ULONG ReceivePacketInUse; +#if BACK_FILL + ULONG BackFillPacketInUse; +#endif + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + CTELock Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + USHORT Socket; // the socket this address corresponds to. + USHORT SendSourceSocket; // used for sends; may be == Socket or 0 + + // + // The following fields are used to maintain state about this address. + // + + BOOLEAN Stopping; + ULONG Flags; // attributes of the address. + struct _DEVICE *Device; // device context to which we are attached. + CTELock * DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // Holds our source address, used for construcing datagrams + // quickly. + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_PACKET SendPacket; + IPX_RECEIVE_PACKET ReceivePacket; + +#if BACK_FILL + IPX_SEND_PACKET BackFillPacket; +#endif + + + UCHAR SendPacketHeader[IPX_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying IpxDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + + ULONG Index; + BOOLEAN RtAdd; + +} ADDRESS, *PADDRESS; + +#define ADDRESS_FLAGS_STOPPING 0x00000001 + +// +// In order to increase the range of ControlChannelIds, we have a large integer to represent +// monotonically increasing ControlChannelIdentifiers. This large integer is packed into the +// 6 Bytes as follows: +// +// REQUEST_OPEN_CONTEXT(_Request) - 4 bytes +// Upper 2 bytes of REQUEST_OPEN_TYPE(_Request) - 2 bytes +// +// IPX_CC_MASK is used to mask out the upper 2 bytes of the OPEN_TYPE. +// MAX_CCID is 2^48. +// +#define IPX_CC_MASK 0x0000ffff + +#define MAX_CCID 0xffffffffffff + +#define CCID_FROM_REQUEST(_ccid, _Request) \ + (_ccid).LowPart = (ULONG)(REQUEST_OPEN_CONTEXT(_Request)); \ + (_ccid).HighPart = ((ULONG)(REQUEST_OPEN_TYPE(_Request)) >> 16); + +//#define USER_BUFFER_OFFSET FIELD_OFFSET(RTRCV_BUFFER, DgrmLength) +#define USER_BUFFER_OFFSET FIELD_OFFSET(RTRCV_BUFFER, Options) +// +// This structure keeps track of the WINS recv Irp and any datagram +// queued to go up to WINS (name service datagrams) +// +#define REFRT_TOTAL 8 + +#define RT_CREATE 0 +#define RT_CLEANUP 1 +#define RT_CLOSE 2 +#define RT_SEND 3 +#define RT_RCV 4 +#define RT_IRPIN 5 +#define RT_BUFF 6 +#define RT_EXTRACT 7 + + +#define RT_EMPTY 0 +#define RT_OPEN 1 +#define RT_CLOSING 2 +#define RT_CLOSED 3 + + +#define RT_IRP_MAX 1000 +#define RT_BUFF_MAX 1000 + +// +// Max. memory allocated for queueing buffers to be received by the RT +// manager +// +#define RT_MAX_BUFF_MEM 65000 //bytes + +// +// Get Index corresponding to the address object opened by RT. BTW We +// can not have more than one Address file (client) for a RT address. +// +#define RT_ADDRESS_INDEX(_pIrp) (((ULONG)REQUEST_OPEN_TYPE(_pIrp)) - ROUTER_ADDRESS_FILE) + +typedef struct _RT_IRP { + PADDRESS_FILE AddressFile; + LIST_ENTRY RcvIrpList; + ULONG NoOfRcvIrps; + LIST_ENTRY RcvList; // linked list of Datagrams Q'd to rcv + ULONG NoOfRcvBuffs; // linked list of Datagrams Q'd to rcv + BOOLEAN State; + } RT_IRP, *PRT_IRP; + +typedef struct +{ +#if DBG + ULONG RefTypes[REFRT_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature[4]; // contains "IBI1" +#endif + LIST_ENTRY CompletedIrps; // linked list of Datagrams Q'd to rcv + LIST_ENTRY HolderIrpsList; // Holds Irps + CTELock Lock; + ULONG ReferenceCount; + ULONG RcvMemoryAllocated; // bytes buffered so far + ULONG RcvMemoryMax; // max # of bytes to buffer on Rcv + PDEVICE pDevice; // the devicecontext used by wins + UCHAR NoOfAdds; + RT_IRP AddFl[IPX_RT_MAX_ADDRESSES]; +} RT_INFO, *PRT_INFO; + +// +// RT Rcv Buffer structure +// +typedef struct +{ + LIST_ENTRY Linkage; + ULONG TotalAllocSize; + ULONG UserBufferLengthToPass; + IPX_DATAGRAM_OPTIONS2 Options; + +} RTRCV_BUFFER, *PRTRCV_BUFFER; + +#define OFFSET_OPTIONS_IN_RCVBUFF FIELD_OFFSET(RTRCV_BUFFER, Options) +#define OFFSET_PKT_IN_RCVBUFF (FIELD_OFFSET(RTRCV_BUFFER, Options) + FIELD_OFFSET(IPX_DATAGRAM_OPTIONS2, Data)) +#define OFFSET_PKT_IN_OPTIONS FIELD_OFFSET(IPX_DATAGRAM_OPTIONS2, Data) + +extern PRT_INFO pRtInfo; + +// +// We keep the demand-dial binding at the beginning of the binding array; this keeps +// track of the number of extra bindings that we have. +// Currently 1 (for demand-dial), we could also keep other bindings like the loopback +// binding, etc. +// +#define DEMAND_DIAL_NIC_ID DEMAND_DIAL_ADAPTER_CONTEXT +#define LOOPBACK_NIC_ID VIRTUAL_NET_ADAPTER_CONTEXT + +#define EXTRA_BINDINGS 2 diff --git a/private/ntos/tdi/isn/ipx/isnipx.h b/private/ntos/tdi/isn/ipx/isnipx.h new file mode 100644 index 000000000..ae48d1449 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/isnipx.h @@ -0,0 +1,554 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnipx.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#ifndef _ISNIPX_ +#define _ISNIPX_ + +#define MAC_HEADER_SIZE ((IPX_MAXIMUM_MAC + 3) & ~3) +#define RIP_PACKET_SIZE ((sizeof(RIP_PACKET) + 3) & ~3) +#define IPX_HEADER_SIZE ((sizeof(IPX_HEADER) + 3) & ~3) + +// +// Frame type definitions +// + +#define ISN_FRAME_TYPE_ETHERNET_II 0 +#define ISN_FRAME_TYPE_802_3 1 +#define ISN_FRAME_TYPE_802_2 2 +#define ISN_FRAME_TYPE_SNAP 3 +#define ISN_FRAME_TYPE_ARCNET 4 // we ignore this +#define ISN_FRAME_TYPE_MAX 4 // of the four standard ones + +#define ISN_FRAME_TYPE_AUTO 0xff + + +// +// This defines the size of the maximum MAC header required +// (token-ring: MAC 14 bytes, RI 18 bytes, LLC 3 bytes, SNAP 5 bytes). +// + +#define IPX_MAXIMUM_MAC 40 + +// +// This is an internal identifier used for RIP query packets. +// + +#define IDENTIFIER_RIP_INTERNAL 4 + +// +// This is an internal identifier used for RIP response packets. +// + +#define IDENTIFIER_RIP_RESPONSE 5 + + +// +// This is the total number of "real" identifiers. +// + +#define IDENTIFIER_TOTAL 4 + + +// +// Some definitions (in the correct on-the-wire order). +// + +#define RIP_PACKET_TYPE 0x01 +#define RIP_SOCKET 0x5304 +#define RIP_REQUEST 0x0100 +#define RIP_RESPONSE 0x0200 +#define RIP_DOWN 0x8200 // use high bit to indicate it + +#define SAP_PACKET_TYPE 0x04 +#define SAP_SOCKET 0x5204 + +#define SPX_PACKET_TYPE 0x05 + +#define NB_SOCKET 0x5504 + + +#include + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of a RIP network entry. +// + +typedef struct _RIP_NETWORK_ENTRY { + ULONG NetworkNumber; + USHORT HopCount; + USHORT TickCount; +} RIP_NETWORK_ENTRY, *PRIP_NETWORK_ENTRY; + +// +// Definition of a single entry rip packet. +// + +typedef struct _RIP_PACKET { + USHORT Operation; + RIP_NETWORK_ENTRY NetworkEntry; +} RIP_PACKET, *PRIP_PACKET; + +#include + + +#define IPX_DEVICE_SIGNATURE 0x1401 +#define IPX_ADAPTER_SIGNATURE 0x1402 +#define IPX_BINDING_SIGNATURE 0x1403 +#define IPX_ADDRESS_SIGNATURE 0x1404 +#define IPX_ADDRESSFILE_SIGNATURE 0x1405 +#define IPX_RT_SIGNATURE 0x1406 + +#define IPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// Defined granularity of RIP timeouts in milliseconds +// + +#define RIP_GRANULARITY 55 + + +// +// The default number of segments in the RIP table. +// + +#define RIP_SEGMENTS 7 + + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#if DBG + +extern ULONG IpxDebug; +extern ULONG IpxMemoryDebug; + +#define IPX_MEMORY_LOG_SIZE 128 +extern UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +extern PUCHAR IpxDebugMemoryLoc; +extern PUCHAR IpxDebugMemoryEnd; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define IPX_DEBUG(_Flag, _Print) { \ + if (IpxDebug & (IPX_DEBUG_ ## _Flag)) { \ + DbgPrint ("IPX: "); \ + DbgPrint _Print; \ + } \ + if (IpxMemoryDebug & (IPX_DEBUG_ ## _Flag)) { \ + IpxDebugMemoryLog _Print; \ + } \ +} + +#else + +#define IPX_DEBUG(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + + +// +// PREQUEST +// IpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define IpxAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// IpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define IpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +#define OPEN_REQUEST_EA_LENGTH(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Parameters.DeviceIoControl.InputBufferLength)) + +#define OPEN_REQUEST_RCV_LEN(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Parameters.DeviceIoControl.OutputBufferLength)) + +#define REQUEST_SPECIAL_RECV(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Parameters.DeviceIoControl.IoControlCode) == MIPX_RCV_DATAGRAM) + +#define REQUEST_SPECIAL_SEND(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Parameters.DeviceIoControl.IoControlCode) == MIPX_SEND_DATAGRAM) + + +#define REQUEST_CODE(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->Parameters.DeviceIoControl.IoControlCode) + +// +// The following value does not clash with TDI_TRANSPORT_ADDRESS_FILE value of +// 0x1 +// +#define ROUTER_ADDRESS_FILE 0x4 + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// VOID +// REQUEST_OPEN_CONTEXT_AND_PARAMS( +// IN PREQUEST Request +// OUT PVOID * OpenContext, +// OUT PTDI_REQUEST_KERNEL * Parameters +// ); +// +// Simultaneously returns the open context and the parameters +// for a request (this is an optimization since the send +// datagram code needs them both). +// + +#define REQUEST_OPEN_CONTEXT_AND_PARAMS(_Request,_OpenContext,_Parameters) { \ + PIO_STACK_LOCATION _IrpSp = IoGetCurrentIrpStackLocation(_Request); \ + *(_OpenContext) = _IrpSp->FileObject->FsContext; \ + *(_Parameters) = (PTDI_REQUEST_KERNEL)(&_IrpSp->Parameters); \ +} + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// IpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define IpxCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + + +#define IPX_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define IPX_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define IPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +#define IPX_DEFINE_SYNC_CONTEXT(_SyncContext) +#define IPX_BEGIN_SYNC(_SyncContext) +#define IPX_END_SYNC(_SyncContext) + +#define IPX_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; +#define IPX_DEFINE_LOCK_HANDLE_PARAM(_LockHandle) CTELockHandle _LockHandle; + +#define IPX_GET_LOCK(_Lock, _LockHandle) \ + CTEGetLock(_Lock, _LockHandle) + +#define IPX_FREE_LOCK(_Lock, _LockHandle) \ + CTEFreeLock(_Lock, _LockHandle) + +#define IPX_GET_LOCK1(_Lock, _LockHandle) + +#define IPX_FREE_LOCK1(_Lock, _LockHandle) + +#define IPX_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, _Lock) +#define IPX_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define IPX_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, _Lock) +#define IPX_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, _Lock) + +#define IPX_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntrySList(_Queue, _Lock) +#define IPX_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntrySList(_Queue, _Entry, _Lock) + +// +// This macro adds a ULONG to a LARGE_INTEGER. +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define IPX_DEBUG_DEVICE 0x00000001 +#define IPX_DEBUG_ADAPTER 0x00000002 +#define IPX_DEBUG_ADDRESS 0x00000004 +#define IPX_DEBUG_SEND 0x00000008 +#define IPX_DEBUG_NDIS 0x00000010 +#define IPX_DEBUG_RECEIVE 0x00000020 +#define IPX_DEBUG_CONFIG 0x00000040 +#define IPX_DEBUG_PACKET 0x00000080 +#define IPX_DEBUG_RIP 0x00000100 +#define IPX_DEBUG_BIND 0x00000200 +#define IPX_DEBUG_ACTION 0x00000400 +#define IPX_DEBUG_BAD_PACKET 0x00000800 +#define IPX_DEBUG_SOURCE_ROUTE 0x00001000 +#define IPX_DEBUG_WAN 0x00002000 +#define IPX_DEBUG_AUTO_DETECT 0x00004000 + +#ifdef _PNP_POWER +#define IPX_DEBUG_PNP 0x00008000 +#endif + +#define IPX_DEBUG_LOOPB 0x00010000 + +#endif diff --git a/private/ntos/tdi/isn/ipx/loopback.c b/private/ntos/tdi/isn/ipx/loopback.c new file mode 100644 index 000000000..be44bae5b --- /dev/null +++ b/private/ntos/tdi/isn/ipx/loopback.c @@ -0,0 +1,280 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + loopback.c + +Abstract: + + This module contains the routines to implement loopback + +Author: + + Sanjay Anand (SanjayAn) 2/6/96 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Global lock to control access to the Loopback queue +// +DEFINE_LOCK_STRUCTURE(LoopLock) + +// +// Head and tail of the Loopback queue +// +PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL; +PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL; + +CTEEvent LoopXmitEvent; +BOOLEAN LoopXmitRtnRunning = 0; + +// +// MaximumPacket sized buffer to hold the lookahead data. +// +// ZZBUGBUG: In PnP this value can change +// +// PUCHAR LookaheadBuffer=NULL; +#define LOOP_LOOKAHEAD_SIZE 128 + sizeof(IPX_HEADER) + 8 + 34 + + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ) +/*++ + +Routine Description: + + Does the actual loopback. + +Arguments: + + Event - Pointer to event structure. + + Context - Pointer to ZZ + +Return Value: + + None. + +--*/ +{ + PNDIS_PACKET Packet; // Pointer to packet being transmitted + PNDIS_BUFFER Buffer; // Current NDIS buffer being processed. + ULONG TotalLength; // Total length of send. + ULONG LookaheadLength; // Bytes in lookahead. + ULONG Copied; // Bytes copied so far. + PUCHAR CopyPtr; // Pointer to buffer being copied into. + PUCHAR SrcPtr; // Pointer to buffer being copied from. + ULONG SrcLength; // Length of src buffer. + BOOLEAN Rcvd = FALSE; + PIPX_SEND_RESERVED Reserved; + ULONG MacSize; + PNDIS_PACKET *PacketPtr; + UCHAR LookaheadBuffer[LOOP_LOOKAHEAD_SIZE]; + + IPX_DEFINE_LOCK_HANDLE(Handle) + + KIRQL OldIrql; + + CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + // + // Raise IRQL so we can acquire locks at DPC level in the receive code. + // Also to be able to ReceiveIndicate at DPC + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + IPX_GET_LOCK(&LoopLock, &Handle); + + if (LoopXmitRtnRunning) { + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } + + LoopXmitRtnRunning = 1; + + for (;;) { + + // + // Get the next packet from the list. + // + Packet = LoopXmitHead; + + if (Packet != (PNDIS_PACKET)NULL) { + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + LoopXmitHead = (PNDIS_PACKET)(Reserved->PaddingBuffer); + IPX_FREE_LOCK(&LoopLock, Handle); + } else { // Nothing left to do. + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + break; + } + + // + // We use the PaddingBuffer section as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_DEBUG(LOOPB, ("Packet: %lx\n", Packet)); + + NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength); + + NdisQueryBuffer(Buffer, NULL, &MacSize); + + IPX_DEBUG(LOOPB, ("Buffer: %lx Totalpktlen: %lx MacSize: %lx\n", Buffer, TotalLength, MacSize)); + + LookaheadLength = MIN(LOOP_LOOKAHEAD_SIZE, TotalLength); + Copied = 0; + CopyPtr = LookaheadBuffer; + while (Copied < LookaheadLength) { + ULONG ThisCopy; // Bytes to copy this time. + +#ifdef DBG + if (!Buffer) { + DbgBreakPoint(); + IPX_GET_LOCK(&LoopLock, &Handle); + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } +#endif + + NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength); + ThisCopy = MIN(SrcLength, LookaheadLength - Copied); + CTEMemCopy(CopyPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + CopyPtr += ThisCopy; + NdisGetNextBuffer(Buffer, &Buffer); + } + + Rcvd = TRUE; + +#ifdef BACK_FILL + // + // For Backfill packets, the MAC header is not yet set up; for others, it is the size + // of the first MDL (17). + // + if ((Reserved->Identifier == IDENTIFIER_IPX) && + (Reserved->BackFill)) { + MacSize = 0; + } +#endif + IpxReceiveIndication( (NDIS_HANDLE)IPX_LOOPBACK_COOKIE, // BindingContext + Packet, // ReceiveContext + (MacSize) ? LookaheadBuffer : NULL, // HeaderBuffer + MacSize, // HeaderBufferSize + LookaheadBuffer+MacSize, // LookAheadBuffer + LookaheadLength-MacSize, // LookAheadBufferSize + TotalLength-MacSize); // PacketSize + + IpxSendComplete(Context, Packet, NDIS_STATUS_SUCCESS); + + // + // Give other threads a chance to run. + // + KeLowerIrql(OldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + IPX_GET_LOCK(&LoopLock, &Handle); + } + + if (Rcvd) { + IpxReceiveComplete(Context); + } + + KeLowerIrql(OldIrql); +} + + +VOID +IpxInitLoopback() +/*++ + +Routine Description: + + Initializes various loopback structures. + +Arguments: + +Return Value: + + None. + +--*/ +{ + CTEInitLock(&LoopLock); + CTEInitEvent(&LoopXmitEvent, IpxDoLoopback); + return; +} + + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Enqueues a packet to the loopbackQ + +Arguments: + + Packet - The packet to be enqueued. + + Context - Pointer to the adapter corresp to the first binding. + +Return Value: + + None. + +--*/ +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // We use the PaddingBuffer as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_GET_LOCK(&LoopLock, &LockHandle); + + // + // LoopbackQ is empty + // + if (LoopXmitHead == (PNDIS_PACKET)NULL) { + LoopXmitHead = Packet; + } else { + Reserved = (PIPX_SEND_RESERVED)(LoopXmitTail->ProtocolReserved); + (PNDIS_PACKET)(Reserved->PaddingBuffer) = Packet; + } + LoopXmitTail = Packet; + + IPX_DEBUG(LOOPB, ("Enqued packet: %lx, Reserved: %lx\n", Packet, Reserved)); + + // + // If this routine is not already running, schedule it as a work item. + // + if (!LoopXmitRtnRunning) { + CTEScheduleEvent(&LoopXmitEvent, Context); + } + + IPX_FREE_LOCK(&LoopLock, LockHandle); +} diff --git a/private/ntos/tdi/isn/ipx/mac.c b/private/ntos/tdi/isn/ipx/mac.c new file mode 100644 index 000000000..a8f2e3b5d --- /dev/null +++ b/private/ntos/tdi/isn/ipx/mac.c @@ -0,0 +1,3940 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the IPX transport. + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define TR_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit +#define TR_DEFAULT_LENGTH 0x70 // default for outgoing +#define TR_MAX_SIZE_MASK 0x70 + +#define TR_PREAMBLE_AC 0x10 +#define TR_PREAMBLE_FC 0x40 + +#define FDDI_HEADER_BYTE 0x57 + + +static UCHAR AllRouteSourceRouting[2] = { 0x82, TR_DEFAULT_LENGTH }; +static UCHAR SingleRouteSourceRouting[2] = { 0xc2, TR_DEFAULT_LENGTH }; + +#define ROUTE_EQUAL(_A,_B) { \ + (*(UNALIGNED USHORT *)(_A) == *(UNALIGNED USHORT *)(_B)) \ +} + +// +// For back-fillable packets, chains the back-fill space as a MAC header +// to the packet and sets the header pointer. +// + +// +// BUGBUG: We dont need to test for IDENTIFIER_IPX since it will always be +// true for the mediumframe specific send handlers. +// +#define BACK_FILL_HEADER(_header, _reserved, _headerlength, _packet) \ + if ((_reserved)->Identifier == IDENTIFIER_IPX) { \ + if((_reserved)->BackFill) { \ + CTEAssert ((_reserved)->HeaderBuffer); \ + CTEAssert ((_reserved)->HeaderBuffer->MdlFlags & MDL_NETWORK_HEADER); \ + _header = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->MappedSystemVa = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->ByteOffset -= _headerlength; \ + NdisChainBufferAtFront(_packet,(PNDIS_BUFFER)(_reserved)->HeaderBuffer); \ + } \ + } + +// +// In case of back-fillable packets, the adjusted length should include +// the prev. bytecount of the headerbuffer. +// +#define BACK_FILL_ADJUST_BUFFER_LENGTH(_reserved, _headerlength) \ + if((_reserved)->BackFill){ \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength+(_reserved)->HeaderBuffer->ByteCount); \ + IPX_DEBUG(SEND,("mac user mdl %x\n", (_reserved)->HeaderBuffer)); \ + } else { \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength); \ + } + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + +#ifndef _PNP_POWER + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,MacInitializeMacInfo) +#endif + +#endif + + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ) + +/*++ + +Routine Description: + + Fills in the binding info based on the adapter's MacInfo + and the frame type of the binding. + +Arguments: + + Binding - The newly created binding. + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + ULONG MaxUserData; + + Binding->DefHeaderSize = Adapter->DefHeaderSizes[Binding->FrameType]; + Binding->BcMcHeaderSize = Adapter->BcMcHeaderSizes[Binding->FrameType]; + + MacReturnMaxDataSize( + &Adapter->MacInfo, + NULL, + 0, + Binding->MaxSendPacketSize, + &MaxUserData); + + Binding->MaxLookaheadData = + Adapter->MaxReceivePacketSize - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->AnnouncedMaxDatagramSize = + MaxUserData - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->RealMaxDatagramSize = + Binding->MaxSendPacketSize - + Adapter->MacInfo.MaxHeaderLength - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + +} /* MacInitializeBindingInfo */ + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + case NdisMedium802_5: + MacInfo->SourceRouting = TRUE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x80; + MacInfo->MaxHeaderLength = 32; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_5; + break; + case NdisMediumFddi: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 13; + MacInfo->MinHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + break; + case NdisMediumArcnet878_2: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x00; + MacInfo->MaxHeaderLength = 3; + MacInfo->MinHeaderLength = 3; + MacInfo->MediumType = NdisMediumArcnet878_2; + break; + case NdisMediumWan: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = TRUE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + default: + CTEAssert(FALSE); + } + MacInfo->RealMediumType = MacType; + +} /* MacInitializeMacInfo */ + + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ) + +/*++ + +Routine Description: + + Maps the specified frame type to a value which is + valid for the medium. + +Arguments: + + MacType - The MAC type we wish to map for. + + FrameType - The frame type in question. + + MappedFrameType - Returns the mapped frame type. + +Return Value: + +--*/ + +{ + switch (MacType) { + + // + // Ethernet accepts all values, the default is 802.2. + // + + case NdisMedium802_3: + if (FrameType >= ISN_FRAME_TYPE_MAX) { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } else { + *MappedFrameType = FrameType; + } + break; + + // + // Token-ring supports SNAP and 802.2 only. + // + + case NdisMedium802_5: + if (FrameType == ISN_FRAME_TYPE_SNAP) { + *MappedFrameType = ISN_FRAME_TYPE_SNAP; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // FDDI supports SNAP, 802.2, and 802.3 only. + // + + case NdisMediumFddi: + if ((FrameType == ISN_FRAME_TYPE_SNAP) || (FrameType == ISN_FRAME_TYPE_802_3)) { + *MappedFrameType = FrameType; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // On arcnet there is only one frame type, use 802.3 + // (it doesn't matter what we use). + // + + case NdisMediumArcnet878_2: + *MappedFrameType = ISN_FRAME_TYPE_802_3; + break; + + // + // WAN uses ethernet II because it includes the ethertype. + // + + case NdisMediumWan: + *MappedFrameType = ISN_FRAME_TYPE_ETHERNET_II; + break; + + default: + CTEAssert(FALSE); + } + +} /* MacMapFrameType */ + +// +// BUGBUG -- use symbols instead of hardcoded values for mac header lengths +// --pradeepb +// + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all headers + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & TR_MAX_SIZE_MASK) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + +#if 0 + if (DeviceMaxFrameSize < 608) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 576; + } +#endif + // + // bug # 6192. There is no point in assuming the worst. It only + // leads to lower throughput. Packets can get dropped by an + // an intermediate router for both cases (this one and the one + // above where 576 is chosen). In the above case, they will + // get dropped if two ethernet machines are communicating via + // a token ring. In this case, they will if two token ring + // machines with a frame size > max ethernet frame size are + // going over an ethernet. To fix the packet drop case, one + // should adjust the MaxPktSize Parameter of the card. + // + *MaxFrameSize = DeviceMaxFrameSize - 32; + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + case NdisMediumArcnet878_2: + + // + // For Arcnet, we always have a 3-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 3; + break; + + } + +} /* MacReturnMaxDataSize */ + +#if 0 + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: THIS FUNCTION IS REAL HACKY AND NEEDS TO BE WORKED AT. WE CAN + Improve the instruction count here - pradeepb + +--*/ + +{ + PNDIS_BUFFER SecondBuffer = NULL; + PUCHAR SecondBufferData; + UINT SecondBufferLength; + USHORT SourceSocket; + PNDIS_BUFFER ThirdBuffer = NULL; + PUCHAR ThirdBufferData; + UINT ThirdBufferLength; + + UNREFERENCED_PARAMETER (PacketLength); + + // + // First get the source socket. + // + +#if 0 + // + // Only time IncludedHeaderLength is less than the offset of + // SourceSocket in IPX header is when it 0 (from rip) + // + if (IncludedHeaderLength <= FIELD_OFFSET (IPX_HEADER, SourceSocket)) { +#endif + + // + // Get the second buffer in the packet (the ipx header is always in + // the second buffer - pradeepb. + // + // In this case + // there must be a second buffer or the packet is too + // short, so we don't check for NULL. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (SecondBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + SourceSocket = *(UNALIGNED USHORT *) + (&SecondBufferData[FIELD_OFFSET(IPX_HEADER, SourceSocket) - IncludedHeaderLength]); + +#if 0 + } +else { + + SourceSocket = IpxHeader->SourceSocket; + } +#endif + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + +#if 0 + // + // We assume the connection control flag and data stream type + // are in the same buffer. + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + } +#endif + + ConnectionControlFlag = *(SecondBufferData + (sizeof(IPX_HEADER) - IncludedHeaderLength)); + DataStreamType = *(SecondBufferData + (sizeof(IPX_HEADER) + 1 - IncludedHeaderLength)); + + +#if 0 + } else { + + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + } + +#endif + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // This would be from the rip driver, if IncludedHeaderLength is + // 0. -- pradeepb + // + // At this point, we assume that total data length is in + // the same buffer as the others. + // + // + // real hacky way of doing things. One should be using + // FIELD_OFFSET(NB_SESSION, TotalDataLength) instead of 8. + // -- pradeepb + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + TotalDataLength = *(USHORT UNALIGNED *)(SecondBufferData + (sizeof(IPX_HEADER) + 8 - IncludedHeaderLength)); + } else { + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + } + + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. + // + + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // if from rip + // + if (IncludedHeaderLength <= sizeof(IPX_HEADER) + 1) { + + + // + // Get the second buffer + // +#if 0 + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + ThirdBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&ThirdBufferData, &ThirdBufferLength); + + } +#endif + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; + + } else { + + // + // will we ever come here - pradeepb? + // + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; +#if 0 + KeepAliveSignature = ((PUCHAR)(IpxHeader+1))[1]; +#endif + + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + + } + + } + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ +#endif + + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: Improve the instruction count here - pradeepb + +--*/ + +{ + USHORT SourceSocket; + PNDIS_BUFFER DataBuffer = NULL; + PUCHAR DataBufferData; + UINT DataBufferLength; + + + // + // First get the source socket. + // + SourceSocket = IpxHeader->SourceSocket; + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + + // + // ConnectionControlFlag and DataStreamType will always follow + // IpxHeader + // + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // TotalDataLength is in the same buffer. + // + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + + // + // No need to update the WAN activity counter + // + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. It can be from rip or from + // NCP on this machine + // + // NOTE: We cannot come here for an SMB packet - [IsaacHe - 12/15]. + // + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // Get the client data buffer + // + NdisQueryPacket(Packet, NULL, NULL, &DataBuffer, NULL); + + // + // If the included header length is 0, it is from rip + // + if (IncludedHeaderLength == 0) { + + // + // Get the second buffer in the packet. The second buffer + // contains the IPX header + other stuff + // + DataBuffer = NDIS_BUFFER_LINKAGE(DataBuffer); + } else { + // + // Get the third buffer in the packet. + // + DataBuffer = NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(DataBuffer)); + } + + NdisQueryBuffer (DataBuffer, (PVOID *)&DataBufferData, &DataBufferLength); + CTEAssert(DataBufferData); + + if (IncludedHeaderLength == 0) { + KeepAliveSignature = DataBufferData[sizeof(IPX_HEADER) + 1]; + } else { + KeepAliveSignature = DataBufferData[1]; + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + } + } + + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ + +#if DBG +ULONG IpxPadCount = 0; +#endif + +#ifdef _PNP_POWER + +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called by NB/SPX to send a frame. + +Arguments: + + LocalTarget - The local target of the send - NB will have the LocalTarget in the Send_Reserved part + of the packet; SPX will not now, but will later. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + Return of IpxSendFrame + + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + PNDIS_BUFFER HeaderBuffer; + PUCHAR IpxHeader; + UINT TempHeaderBufferLength; + PDEVICE Device = IpxDevice; + PIPX_HEADER TempHeader; + NTSTATUS ret; + BOOLEAN fIterate=FALSE; + + // + // Figure out the IpxHeader - it is always at the top of the second MDL. + // + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer (NDIS_BUFFER_LINKAGE(HeaderBuffer), &IpxHeader, &TempHeaderBufferLength); + + // + // Set this now, will change later + // + Reserved->CurrentNicId = 0; + + // + // Copy the LocalTarget into the send reserved area of the packet. + // + Reserved->LocalTarget = *LocalTarget; + + // + // If the NicId in the handle is ITERATIVE_NIC_ID, then this could be a send + // over all NICs in the case of NB/SPX. + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)ITERATIVE_NIC_ID) { + CTEAssert(Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX); + // + // Start with the first NIC + // + IPX_DEBUG(SEND, ("Iteration over NICs started, reserved: %lx\n", Reserved)); + Reserved->CurrentNicId = 1; + Reserved->Net0SendSucceeded = FALSE; + FILL_LOCAL_TARGET(&Reserved->LocalTarget, 1); + Reserved->PacketLength = PacketLength; + fIterate = TRUE; + } + + // + // If the Forwarder is installed, send the packet out for filtering + // + if (Device->ForwarderBound) { + ULONG FwdAdapterContext = INVALID_CONTEXT_VALUE; + PBINDING Binding; + + // + // Figure out the FwdAdapterContext; if the NicId is 0 + // then no NicId is specified (since we never return a + // NicId of 0 in a FindRoute). + // + + // + // We need to fix the following problems with respect to type 20 iterative bcasts : + // 1. IPX will not bcast on a down WAN line (thus the Fwd cannot bring up a demand-dial line). + // 2. IPX bcasts on every Nic (since it is not any wiser about selecting relevant Nics). + // 3. If the first bcast fails, the whole send fails. + // + // All the above (except 3.) occur because the Fwd knows more about the Nics than IPX does; hence + // we let the Fwd decide which lines he wants to send a bcast on. Thus, for Type20 pkts, we pass + // up the invalid Fwd context so the Fwd decides the next Nic to send on. + // + if (!((((PIPX_HEADER)IpxHeader)->PacketType == 0x14) && fIterate) && + Reserved->LocalTarget.NicId && + (Binding = NIC_ID_TO_BINDING(Device, Reserved->LocalTarget.NicId)) && + (GET_VALUE(Binding->ReferenceCount) == 2)) { + // + // If proper NicId specified, and the adapter has been opened by + // the forwarder, set the FwdAdapterContext. + // + FwdAdapterContext = Binding->FwdAdapterContext; + } +#if DBG + else { + if (((PIPX_HEADER)IpxHeader)->PacketType == 0x14) { + IPX_DEBUG(SEND, ("SendComplete: IpxHeader has Type20: %lx\n", IpxHeader)); + } + } +#endif + + // + // Call the InternalSend to filter the packet and get to know + // the correct adapter context + // + ret = (*Device->UpperDrivers[IDENTIFIER_RIP].InternalSendHandler)( + &Reserved->LocalTarget, + FwdAdapterContext, + Packet, + IpxHeader, + IpxHeader+sizeof(IPX_HEADER), // the data starts after the IPX Header. + PacketLength, + fIterate); + + if (ret == STATUS_SUCCESS) { + // + // The adapter could have gone away and we have indicated to the Forwarder + // but the Forwarder has not yet closed the adapter. + // [ZZ] adapters do not go away now. + // + // BUGBUG: what if the binding is NULL here? Can we trust the Forwarder to + // give us a non-NULL binding? + // + + if (GET_VALUE(NIC_ID_TO_BINDING(Device, Reserved->LocalTarget.NicId)->ReferenceCount) == 1) { + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: FWD returned SUCCESS, Ref count is 1\n")); + return NDIS_STATUS_SUCCESS; + } else { + + // + // Fill up the changed LocalTarget for the client except in the ITERATE case. + // + if (!fIterate) { + *LocalTarget = Reserved->LocalTarget; + } + + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: FWD returned SUCCESS, sending out on wire\n")); + goto SendPkt; + } + } else if (ret == STATUS_PENDING) { + // + // LocalTarget will get filled up in InternalSendComplete + // + IPX_DEBUG(SEND, ("SendFramePreFwd: FWD returned PENDING\n")); + return NDIS_STATUS_PENDING; + } else if (ret == STATUS_DROP_SILENTLY) { + // + // This was a keepalive which the router is spoofing. Drop it silently. + // + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: FWD returned STATUS_DROP_SILENTLY - dropping pkt.\n")); + return NDIS_STATUS_SUCCESS; + } + + // + // else DISCARD - this means that either the packet failed the send + // or that the preferred NicId was not good. + // + return STATUS_NETWORK_UNREACHABLE; + + } else { + +SendPkt: + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)LOOPBACK_NIC_ID) { + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Recalculate packet counts here. + // Assume an 802_3802_2 header and use that length. + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, 17); + NdisRecalculatePacketCounts (Packet); + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + return IpxSendFrame ( + &Reserved->LocalTarget, + Packet, + PacketLength, + IncludedHeaderLength); + + } +} +#endif + + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + BUGBUG: Check that Binding is not NULL. + +Arguments: + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PDEVICE Device = IpxDevice; + PUCHAR Header; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + ULONG TwoBytes; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + ULONG HeaderLength=0; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + UCHAR SourceRoutingIdentifier; + ULONG HeaderSizeRequired; + PIPX_HEADER TempHeader; + USHORT PktLength; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + +#ifdef SNMP +// +// This should not include the forwarded packets; on host side, it is 0. +// On router, the AdvSysForwPackets are subtracted in the sub-agent code. +// + ++IPX_MIB_ENTRY(Device, SysOutRequests); +#endif SNMP + + // + // Get the lock on the binding array + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &LocalTarget->NicHandle); + + if (Binding == NULL) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG(PNP, ("Invalid NIC handle: %lx\n", LocalTarget->NicHandle)); + // + // [BUGBUGZZ] Return a unique error that NB/SPX see and re-query the NicId. + // +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutMalformedRequests); +#endif SNMP + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + Adapter = Binding->Adapter; + + IpxReferenceAdapter(Adapter); + + // + // Release the lock + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + + // + // If this is on the loopback adapter (Nic 0), queue it on the LoopbackQueue; + // if the LoopbackRtn is not started, start it now. + // + if (LocalTarget->NicId == 0) { + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Assume an 802_3802_2 header and use that length. + // + + // + // Adjust the MAC header's length to the right value + // + // NdisAdjustBufferLength (HeaderBuffer, 17); + + NdisRecalculatePacketCounts (Packet); + + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding == NULL) { + return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? + } + Adapter = Binding->Adapter; +#endif _PNP_POWER + + // + // For IPX and other protocols that are guaranteed to have allocated + // the header from non-paged pool, use the buffer directly. For others, + // query the packet for the pointer to the MDL. + // + if (Reserved->Identifier >= IDENTIFIER_IPX) { + HeaderBuffer = Reserved->HeaderBuffer; + Header = Reserved->Header; + + } else { + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + } + + CTEAssert (Reserved->PaddingBuffer == NULL); + + // + // First move the packet around if needed. + // + + if (Reserved->Identifier < IDENTIFIER_IPX) { + + // + // Only RIP will have IncludedHeaderLength as 0. I don't know + // why we have the comment about RIP inside this if statement. + // + if (IncludedHeaderLength > 0) { + + // + // Spx can handle a virtual net as long as it is + // not 0. Netbios always needs to use the real address. + // We need to hack the ipx source address for packets + // which are sent by spx if we have a fake virtual + // net, and packets sent by netbios unless we are + // bound to only one card. + // + + // + // We handle binding sets as follows, based on who + // sent the frame to us: + // + // RIP: Since we only tell RIP about the masters at + // bind time, and hide slaves on indications, it should + // never be sending on a slave binding. Since RIP knows + // the real net and node of every binding we don't + // need to modify the packet at all. + // + // NB: For broadcasts we want to put the first card's + // address in the IPX source but round-robin the + // actual sends over all cards (broadcasts shouldn't + // be passed in with a slave's NIC ID). For directed + // packets, which may come in on a slave, we should + // put the slave's address in the IPX source. + // + // SPX: SPX does not send broadcasts. For directed + // frames we want to use the slave's net and node + // in the IPX source. + // + + if (Reserved->Identifier == IDENTIFIER_NB) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + // + // Get the packet length from the ipx header. Compare with + // the max. allowed datagram size. + // + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + PktLength = ((TempHeader->PacketLength[0] << 8) | + (TempHeader->PacketLength[1])); + +// +// BUGBUG - Not the most efficient way to do this. NWLNKNB should do this. +// Doing it in ipx means doing it for all packets (even those sent on +// connections). Will remove this later when nwlnknb change has been +// tested. +// + + + if (PktLength > (Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + PktLength, + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))); + +#ifdef _PNP_POWER + // + // Dereference the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutMalformedRequests); +#endif SNMP + return STATUS_INVALID_BUFFER_SIZE; + } + + if (Device->ValidBindings > 1) { + + + // + // Store this now, since even if we round-robin the + // actual send we want the binding set master's net + // and node in the IPX source address. + // + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->BindingSetMember) { + + if (IPX_NODE_BROADCAST(LocalTarget->MacAddress)) { + + // + // This is a broadcast, so we round-robin the + // sends through the binding set. + // +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + Adapter = Binding->Adapter; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxReferenceAdapter(Adapter); +#endif + } + + } + } + + // + // [STEFANS]: Replace all source addresses with the virtualnet# to allow for sends + // on 0 network number WAN lines (typically between routers). + // + if (Device->VirtualNetwork) { + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Device->SourceAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Device->SourceAddress.NodeAddress, 6); + } + + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + + // + // Need to update this if we have multiple cards but + // a zero virtual net. + // + + if (Device->MultiCardZeroVirtual) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + } + + } else { + + // + // For a rip packet it should not be in a binding set, + // or if it is it should be the master. + // +#if DBG + CTEAssert ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding)); +#endif + } + + +#if 0 + // + // There is a header included, we need to adjust it. + // The header will be at Device->IncludedHeaderOffset. + // + + if (LocalTarget->MacAddress[0] & Adapter->MacInfo.BroadcastMask) { + HeaderSizeRequired = Adapter->BcMcHeaderSizes[Binding->FrameType]; + } else { + HeaderSizeRequired = Adapter->DefHeaderSizes[Binding->FrameType]; + } + + if (HeaderSizeRequired != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + &Header[HeaderSizeRequired], + &Header[Device->IncludedHeaderOffset], + IncludedHeaderLength); + } +#endif + } + } + + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + + // + // [FW] This will allow both LINE_UP and LINE_CONFIG states + // + if (!Binding->LineUp) { + // + // Bug #17273 return proper error message + // + // return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + return STATUS_NETWORK_UNREACHABLE; + } + + if (Adapter->MacInfo.MediumAsync) { + + IPX_HEADER UNALIGNED * IpxHeader; + PNDIS_BUFFER IpxNdisBuff; + UINT IpxHeaderLen; + +#if 0 + // + // The header should have been moved here. + // + + CTEAssert(Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] == + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]); + + + IpxHeader = (IPX_HEADER UNALIGNED *) + (&Header[Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]]); +#endif + // + // The Ipx header is always the second ndis buffer in the mdl + // chain. Get it and then query the va of the same. + // + IpxNdisBuff = NDIS_BUFFER_LINKAGE(HeaderBuffer); + NdisQueryBuffer (IpxNdisBuff, (PVOID *)&IpxHeader, &IpxHeaderLen); +// IpxHeader = (IPX_HEADER UNALIGNED *) (&Header[MAC_HEADER_SIZE]); + + // + // If this is a type 20 name frame from Netbios and we are + // on a dialin WAN line, drop it if configured to. + // + // The 0x01 bit of DisableDialinNetbios controls + // internal->WAN packets, which we handle here. + // + // + + // + // SS# 33592: In case of iterative sends, the IncludedHeaderLength is not set properly + // since we dont keep track of the length that came in the first time (we track the PacketLength + // however). The included length field is used here for checking for NB_NAME_FRAMES, but elsewhere + // used only to distinguish between whether RIP or NB/SPX sent the packet (IncludedHeaderLen ==0 for RIP) + // The ideal solution here is to do way with this field altogether, but for the beta we will just use the + // PacketLength field for comparison here since we are assured that this will be equal to the InclHeaderLen + // for any type 0x14 packet that comes down from NB. + // + // BUGBUGZZ: Remove the IncludedHeaderLength field. + // + + // + // [FW] do this only if the forwarder is not bound + // + if (!Device->ForwarderBound && + (!Binding->DialOutAsync) && + (Reserved->Identifier == IDENTIFIER_NB) && + // (IncludedHeaderLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + (PacketLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + ((Device->DisableDialinNetbios & 0x01) != 0) && + (IpxHeader->PacketType == 0x14)) { +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_SUCCESS; + } + + + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact sap and ncp + // packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + IpxHeader, + IncludedHeaderLength, + Packet, + PacketLength); + + + // + // In order for loopback to work properly, we need to put the local MAC address for locally destined + // pkts so NdisWAN can loop them back. + // + if (IPX_NODE_EQUAL(LocalTarget->MacAddress, Binding->LocalAddress.NodeAddress)) { + RtlCopyMemory (Header, Binding->LocalMacAddress.Address, 6); + } else { + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + } + + } else { + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + } + + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + + case ISN_FRAME_TYPE_802_2: + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_802_3: + TwoBytes = PacketLength; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_ETHERNET_II: + TwoBytes = Adapter->BindSap; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_SNAP: + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + //BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + // + // Pad odd-length packets if needed. + // + + if ((((PacketLength + HeaderLength) & 1) != 0) && + (Device->EthernetPadToEven) && + (!Adapter->MacInfo.MediumAsync)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + if ( CurBuffer == HeaderBuffer ) { + BufferLength++; // Just bump this length + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + if (TwoBytes != Adapter->BindSap) { + CTEAssert(TwoBytes & 1); + TwoBytes += 1; + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + } + +#if DBG + ++IpxPadCount; +#endif + } + + break; + + case NdisMedium802_5: + + if (Reserved->Identifier >= IDENTIFIER_IPX) { + + DestinationType = Reserved->DestinationType; + SourceRoutingIdentifier = IDENTIFIER_IPX; + + } else { + + if (LocalTarget->MacAddress[0] & 0x80) { + if (*(UNALIGNED ULONG *)(&LocalTarget->MacAddress[2]) != 0xffffffff) { + DestinationType = DESTINATION_MCAST; + } else { + DestinationType = DESTINATION_BCAST; + } + } else { + DestinationType = DESTINATION_DEF; + } + SourceRoutingIdentifier = Reserved->Identifier; + + } + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + SourceRoutingIdentifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Binding->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_802_3: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_SNAP: + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + + break; + + case NdisMediumFddi: + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_3: + HeaderLength = 13; + break; + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + HeaderLength = 16; + break; + case ISN_FRAME_TYPE_SNAP: + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + HeaderLength = 21; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + case NdisMediumArcnet878_2: + + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Binding->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + + // + // Binding->FrameType is not used. + // + + HeaderLength = 3; +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + } + + // + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, BufferLength); + NdisRecalculatePacketCounts (Packet); + +#if 0 + { + PMDL mdl; + mdl = (PMDL)NDIS_BUFFER_LINKAGE(HeaderBuffer); + if (mdl) { + + KdPrint(("**Bytecount %x %x\n",mdl->ByteCount, mdl)); + if ((LONG)mdl->ByteCount < 0) { + DbgBreakPoint(); + } + } + } +#endif + +#if DBG + { + ULONG SendFlag; + ULONG Temp; + PNDIS_BUFFER FirstPacketBuffer; + PNDIS_BUFFER SecondPacketBuffer; + IPX_HEADER DumpHeader; + UCHAR DumpData[14]; + + NdisQueryPacket (Packet, NULL, NULL, &FirstPacketBuffer, NULL); + SecondPacketBuffer = NDIS_BUFFER_LINKAGE(FirstPacketBuffer); + TdiCopyMdlToBuffer(SecondPacketBuffer, 0, &DumpHeader, 0, sizeof(IPX_HEADER), &Temp); + if (Reserved->Identifier == IDENTIFIER_NB) { + SendFlag = IPX_PACKET_LOG_SEND_NB; + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + SendFlag = IPX_PACKET_LOG_SEND_SPX; + } else if (Reserved->Identifier == IDENTIFIER_RIP) { + SendFlag = IPX_PACKET_LOG_SEND_RIP; + } else { + if (DumpHeader.SourceSocket == IpxPacketLogSocket) { + SendFlag = IPX_PACKET_LOG_SEND_SOCKET | IPX_PACKET_LOG_SEND_OTHER; + } else { + SendFlag = IPX_PACKET_LOG_SEND_OTHER; + } + } + +#if 0 + if (PACKET_LOG(SendFlag)) { + + TdiCopyMdlToBuffer(SecondPacketBuffer, sizeof(IPX_HEADER), &DumpData, 0, 14, &Temp); + + IpxLogPacket( + TRUE, + LocalTarget->MacAddress, + Binding->LocalMacAddress.Address, + (USHORT)PacketLength, + &DumpHeader, + DumpData); + + } +#endif + } +#endif + + ++Device->Statistics.PacketsSent; + + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + +#ifdef _PNP_POWER + if (Status != STATUS_PENDING) { + + if (Reserved->PaddingBuffer) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); +#if DBG + if ((Reserved->Identifier == IDENTIFIER_RIP) && + (LastBufferLength == 1500)) { + DbgPrint("Packet: %lx\n", Packet); + DbgBreakPoint(); + } +#endif DBG + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } + + // + // If this was an NB/SPX packet, and there was an + // iterative send going on, then call the SendComplete + // handler. + // + if ((Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX) && + (Reserved->CurrentNicId)) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + Status); + + Status = STATUS_PENDING; + } + } +#else + if ((Status != STATUS_PENDING) && + (Reserved->PaddingBuffer)) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } +#endif + +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + + return Status; + +} /* IpxSendFrame */ + + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + + // + // BUGBUG: Remove the IncludedHeaderLength parameter from here + // +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + LONG HeaderLength; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + IPX_DEBUG(SEND,("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++PacketLength; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(PacketLength / 256); + Header[13] = (UCHAR)(PacketLength % 256); + + //NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_3 */ + + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17, Packet); + IPX_DEBUG(SEND, ("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); + IPX_DEBUG(SEND, ("packet=%x, usermdl %x\n",Packet,Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0 ) { +#if BACK_FILL + if (0) { +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 17); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 17); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_2 */ + + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + +#if DBG + ++IpxPadCount; +#endif + + } + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3EthernetII */ + + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 22); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 22); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 22); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3Snap */ + + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + + //PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + } + + // + // Create space for the source routing information + // +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17+SourceRoutingLength, Packet); +#endif BACK_FILL + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5802_2 */ + + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + } + } + + // + // Create space for sourcerouting headers + // +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22+SourceRoutingLength, Packet); +#endif BACK_FILL + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5Snap */ + + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 13, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 13); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 13); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 13); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_3 */ + + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 16, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 16); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 16); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 16); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_2 */ + + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 21, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 21); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 21); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 21); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiSnap */ + + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMARCNET878_2 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 3, Packet); +#endif BACK_FILL + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Adapter->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 3); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 3); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 3); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiArcnet878_2 */ + + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMWAN FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(IpxDevice, NIC_FROM_LOCAL_TARGET(LocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + +#else + PBINDING Binding = IpxDevice->Bindings[LocalTarget->NicId]; +#endif _PNP_POWER + + // + // [FW] This will allow both LINE_UP and LINE_CONFIG states + // + if (Binding->LineUp) { + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + + // + // Call UpdateWanInactivity only if this is not a backfill packet, since + // SMB server does not do KeepAlives. In case, of backfilled packets, reset + // the counter regardless. + // + if (!Reserved->BackFill) { + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); + } else { + Binding->WanInactivityCounter = 0; + } + +#else + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact netbios packets + // and rip packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); +#endif BACK_FILL + + // + // In order for loopback to work properly, we need to put the local MAC address for locally destined + // pkts so NdisWAN can loop them back. + // + if (IPX_NODE_EQUAL(LocalTarget->MacAddress, Binding->LocalAddress.NodeAddress)) { + RtlCopyMemory (Header, Binding->LocalMacAddress.Address, 6); + } else { + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + } + // RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return Status; + + } else { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_NETWORK_UNREACHABLE; + } + +} /* IpxSendFrameWanEthernetII */ + + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called when a valid IPX frame is received from + a remote. It gives the source routing database a change to + update itself to include information about this remote. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Adapter - The adapter the frame was received on. + + MacHeader - The MAC header of the received frame. + + MacHeaderLength - The length of the MAC header. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + CTEAssert ((Database >= 0) && (Database <= 3)); + + // + // If this adapter is configured for no source routing, don't + // need to do anything. + // + + if (!Adapter->SourceRouting) { + return; + } + + // + // See if this source routing is relevant. We don't + // care about two-byte source routing since that + // indicates it did not cross a router. If there + // is nothing in the database, then don't add + // this if it is minimal (if it is not, we need + // to add it so we will find it on sending). + // + + if ((Adapter->SourceRoutingEmpty[Database]) && + (MacHeaderLength <= 16)) { + return; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + // + // Try to find this address in the database. + // + + Hash = MacSourceRoutingHash (MacHeader+8); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacHeader+8, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. If the data is the + // same as what we have, update the time since used to + // prevent aging. + // + + if ((Current->SourceRoutingLength == MacHeaderLength-14) && + (RtlEqualMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength-14))) { + + Current->TimeSinceUsed = 0; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + } + + } + + // + // Not found, insert a new node at the front of the list. + // + + Current = (PSOURCE_ROUTE)IpxAllocateMemory (SOURCE_ROUTE_SIZE(MacHeaderLength-14), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + if (Current == (PSOURCE_ROUTE)NULL) { + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + } + + Current->Next = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current; + + Adapter->SourceRoutingEmpty[Database] = FALSE; + + RtlCopyMemory (Current->MacAddress, MacHeader+8, 6); + Current->MacAddress[0] &= 0x7f; + Current->SourceRoutingLength = (UCHAR)(MacHeaderLength - 14); + RtlCopyMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength - 14); + + Current->TimeSinceUsed = 0; + + IPX_DEBUG (SOURCE_ROUTE, ("Adding source route %lx for %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + Current, Current->MacAddress[0], Current->MacAddress[1], + Current->MacAddress[2], Current->MacAddress[3], + Current->MacAddress[4], Current->MacAddress[5])); + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacUpdateSourceRouting */ + + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR MacAddress[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine looks up a target address in the adapter's + source routing database to see if source routing information + needs to be added to the frame. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Binding - The binding the frame is being sent on. + + MacAddress - The destination address. + + SourceRouting - Buffer to hold the returned source routing info. + + SourceRoutingLength - The returned source routing length. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // If this adapter is configured for no source routing, don't + // insert any. + // + + if (!Adapter->SourceRouting) { + *SourceRoutingLength = 0; + return; + } + + // + // See if source routing has not been important so far. + // + // BUGBUG: This is wrong because we may be sending a directed + // packet to somebody on the other side of a router, without + // ever having received a routed packet. We fix this for the + // moment by only setting SourceRoutingEmpty for netbios + // which uses broadcasts for discovery. + // + + if (Adapter->SourceRoutingEmpty[Database]) { + *SourceRoutingLength = 0; + return; + } + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. + // + + if (Current->SourceRoutingLength <= 2) { + *SourceRoutingLength = 0; + } else { + RtlCopyMemory (SourceRouting, Current->SourceRouting, Current->SourceRoutingLength); + SourceRouting[0] = (SourceRouting[0] & TR_LENGTH_MASK); + SourceRouting[1] = (SourceRouting[1] ^ TR_DIRECTION_MASK); + *SourceRoutingLength = Current->SourceRoutingLength; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + // + // We did not find this node, use the default. + // + + if (Binding->AllRouteDirected) { + RtlCopyMemory (SourceRouting, AllRouteSourceRouting, 2); + } else { + RtlCopyMemory (SourceRouting, SingleRouteSourceRouting, 2); + } + *SourceRoutingLength = 2; + +} /* MacLookupSourceRouting */ + + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the source routing timer expires. + It is called every minute. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PADAPTER Adapter; + PBINDING Binding; + PSOURCE_ROUTE Current, OldCurrent, Previous; + UINT i, j, k; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + +#ifdef _PNP_POWER + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + // + // Get a lock on the access path. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + ++Device->SourceRoutingTime; + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + ++Device->SourceRoutingTime; + + for (i = 1; i <= Device->ValidBindings; i++) { + + if (Binding = Device->Bindings[i]) { +#endif _PNP_POWER + + Adapter = Binding->Adapter; + + if (Adapter->LastSourceRoutingTime != Device->SourceRoutingTime) { + + // + // We need to scan this adapter's source routing + // tree for stale routes. To simplify the scan we + // only delete entries that have at least one + // child that is NULL. + // + + Adapter->LastSourceRoutingTime = Device->SourceRoutingTime; + + for (j = 0; j < IDENTIFIER_TOTAL; j++) { + + for (k = 0; k < SOURCE_ROUTE_HASH_SIZE; k++) { + + if (Adapter->SourceRoutingHeads[j][k] == (PSOURCE_ROUTE)NULL) { + continue; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + Current = Adapter->SourceRoutingHeads[j][k]; + Previous = (PSOURCE_ROUTE)NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + ++Current->TimeSinceUsed; + + if (Current->TimeSinceUsed >= Device->SourceRouteUsageTime) { + + // + // A stale entry needs to be aged. + // + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[j][k] = Current->Next; + } + + OldCurrent = Current; + Current = Current->Next; + + IPX_DEBUG (SOURCE_ROUTE, ("Aging out source-route entry %lx\n", OldCurrent)); + IpxFreeMemory (OldCurrent, SOURCE_ROUTE_SIZE (OldCurrent->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } else { + + Previous = Current; + Current = Current->Next; + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + } // for loop through the database's hash list + + } // for loop through the adapter's four databases + + } // if adapter's database needs to be checked + + } // if binding exists + + } // for loop through every binding + } + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Now restart the timer unless we should not (which means + // we are being unloaded). + // + + if (Device->SourceRoutingUsed) { + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + + } else { + + IpxDereferenceDevice (Device, DREF_SR_TIMER); + } + +} /* MacSourceRoutingTimeout */ + + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + MAC address should be removed. + +Arguments: + + Binding - The binding to modify. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + + PSOURCE_ROUTE Current, Previous; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + ULONG Database; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through to find the matching entry in each database. + // + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Previous = NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + } + + IPX_DEBUG (SOURCE_ROUTE, ("IPXROUTE freeing source-route entry %lx\n", Current)); + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + break; + + } else { + + Previous = Current; + Current = Current->Next; + + } + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingRemove */ + + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + binding should be cleared entirely. + +Arguments: + + Binding - The binding to be cleared. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Database, Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through and remove every entry in the database. + // + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } + } + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingClear */ + + + diff --git a/private/ntos/tdi/isn/ipx/mac.h b/private/ntos/tdi/isn/ipx/mac.h new file mode 100644 index 000000000..a88e77ecd --- /dev/null +++ b/private/ntos/tdi/isn/ipx/mac.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Revision History: + +--*/ + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the mac.c routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _NDIS_INFORMATION { + + NDIS_MEDIUM MediumType; + NDIS_MEDIUM RealMediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + UCHAR BroadcastMask; + ULONG CopyLookahead; + ULONG MacOptions; + ULONG MinHeaderLength; + ULONG MaxHeaderLength; + +} NDIS_INFORMATION, * PNDIS_INFORMATION; + + +#define TR_SOURCE_ROUTE_FLAG 0x80 + +#define ARCNET_PROTOCOL_ID 0xFA + diff --git a/private/ntos/tdi/isn/ipx/mp/makefile b/private/ntos/tdi/isn/ipx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/ipx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/ipx/mp/nwlnkipx.prf b/private/ntos/tdi/isn/ipx/mp/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/mp/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isn/ipx/mp/sources b/private/ntos/tdi/isn/ipx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/ipx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/ipx/ndis.c b/private/ntos/tdi/isn/ipx/ndis.c new file mode 100644 index 000000000..db1d41e13 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/ndis.c @@ -0,0 +1,2381 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ndis.c + +Abstract: + + This module contains code which implements the routines used to + initialize the IPX <-> NDIS interface, as well as most of the + interface routines. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + 1. Added the ReceivePacketHandler to the ProtChars. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + + Tony Bell (TonyBe) 10-Dec-1995 + Changes to support new NdisWan Lineup. + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ); + +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxRegisterProtocol) +#pragma alloc_text(INIT,IpxInitializeNdis) +#endif +#endif + + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + NameString - The name of the transport. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + + // + // Set up the characteristics of this protocol + // +#if NDIS40 + ProtChars.MajorNdisVersion = 4; + + ProtChars.ReceivePacketHandler = IpxReceivePacket; +#else + ProtChars.MajorNdisVersion = 3; +#endif + ProtChars.MinorNdisVersion = 0; + + ProtChars.Name = *NameString; + + ProtChars.OpenAdapterCompleteHandler = IpxOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = IpxCloseAdapterComplete; + ProtChars.ResetCompleteHandler = IpxResetComplete; + ProtChars.RequestCompleteHandler = IpxRequestComplete; + + ProtChars.SendCompleteHandler = IpxSendComplete; + ProtChars.TransferDataCompleteHandler = IpxTransferDataComplete; + + ProtChars.ReceiveHandler = IpxReceiveIndication; + ProtChars.ReceiveCompleteHandler = IpxReceiveComplete; + ProtChars.StatusHandler = IpxStatus; + ProtChars.StatusCompleteHandler = IpxStatusComplete; + +#ifdef _PNP_POWER + ProtChars.BindAdapterHandler = IpxBindAdapter; + ProtChars.UnbindAdapterHandler = IpxUnbindAdapter; + ProtChars.TranslateHandler = IpxTranslate; +#endif // _PNP_POWER + + NdisRegisterProtocol ( + &ndisStatus, + &IpxNdisProtocolHandle, + &ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; + +} /* IpxRegisterProtocol */ + + +VOID +IpxDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (IpxNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + IpxNdisProtocolHandle); + IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + } + +} /* IpxDeregisterProtocol */ + + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + Adapter - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + Adapter->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx failed %lx\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid, + NdisStatus)); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + + } else { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx succeeded\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid)); + } + + return NdisStatus; + +} /* IpxSubmitNdisRequest */ + + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + Adapter - Structure describing this binding. + + ConfigAdapter - Configuration information for this binding. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM IpxSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumArcnet878_2, NdisMediumWan }; + UINT SelectedMedium; + NDIS_REQUEST IpxRequest; + ULONG MinimumLookahead; + UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x81, 0x37 }; + UCHAR FunctionalAddress[4] = { 0x00, 0x80, 0x00, 0x00 }; + ULONG WanHeaderFormat = NdisWanHeaderEthernet; + NDIS_OID IpxOid; + ULONG MacOptions; + ULONG PacketFilter; + PNDIS_STRING AdapterString = &ConfigBinding->AdapterName; + + // + // Initialize this adapter for IPX use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + Adapter->NdisBindingHandle = NULL; + + OpenErrorStatus = 0; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &Adapter->NdisBindingHandle, + &SelectedMedium, + IpxSupportedMedia, + sizeof (IpxSupportedMedia) / sizeof(NDIS_MEDIUM), + IpxNdisProtocolHandle, + (NDIS_HANDLE)Adapter, + &ConfigBinding->AdapterName, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + OpenErrorStatus = Adapter->OpenErrorStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("Open %ws failed %lx\n", ConfigBinding->AdapterName.Buffer, NdisStatus)); + + IpxWriteGeneralErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 1, + &OpenErrorStatus); + return STATUS_INSUFFICIENT_RESOURCES; + + } else { + + IPX_DEBUG (NDIS, ("Open %ws succeeded\n", ConfigBinding->AdapterName.Buffer)); + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + IpxSupportedMedia[SelectedMedium], + &Adapter->MacInfo); + + + switch (Adapter->MacInfo.RealMediumType) { + + case NdisMedium802_3: + + IpxOid = OID_802_3_CURRENT_ADDRESS; + break; + + case NdisMedium802_5: + + IpxOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + IpxOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + case NdisMediumArcnet878_2: + + IpxOid = OID_ARCNET_CURRENT_ADDRESS; + break; + + case NdisMediumWan: + + IpxOid = OID_WAN_CURRENT_ADDRESS; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = IpxOid; + + if (IpxOid != OID_ARCNET_CURRENT_ADDRESS) { + + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = Adapter->LocalMacAddress.Address; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + } else { + + // + // We take the arcnet single-byte address and right-justify + // it in a field of zeros. + // + + RtlZeroMemory (Adapter->LocalMacAddress.Address, 5); + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->LocalMacAddress.Address[5]; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 1; + + } + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the maximum packet sizes. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxReceivePacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxSendPacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Query the receive buffer space. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_RECEIVE_BUFFER_SPACE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->ReceiveBufferSpace); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now set the minimum lookahead size. The value we choose + // here is the 128 needed for TDI indications, plus the size + // of the IPX header, plus the largest extra header possible + // (a SNAP header, 8 bytes), plus the largest higher-level + // header (I think it is a Netbios datagram, 34 bytes). + // + // BETABUGBUG: Adapt this based on higher-level bindings and + // configured frame types. + // + + MinimumLookahead = 128 + sizeof(IPX_HEADER) + 8 + 34; + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MediumSpeed); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // For wan, specify our protocol ID and header format. + // We don't query the medium subtype because we don't + // case (since we require ethernet emulation). + // + + if (Adapter->MacInfo.MediumAsync) { + + if (Adapter->BindSap != 0x8137) { + *(UNALIGNED USHORT *)(&WanProtocolId[4]) = Adapter->BindSapNetworkOrder; + } + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Now query the line count. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_LINE_COUNT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->WanNicIdCount; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + Adapter->WanNicIdCount = 1; + } + + if (Adapter->WanNicIdCount == 0) { + + IPX_DEBUG (NDIS, ("OID_WAN_LINE_COUNT returned 0 lines\n")); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_QUERY_OID_FAILED, + NDIS_STATUS_INVALID_DATA, + AdapterString->Buffer, + OID_WAN_LINE_COUNT); + + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + + } + } + + + // + // For 802.5 adapter's configured that way, we enable the + // functional address (C0-00-00-80-00-00). + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->EnableFunctionalAddress)) { + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = FunctionalAddress; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Now query the MAC's optional characteristics. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Adapter->MacInfo.CopyLookahead = + ((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0; + Adapter->MacInfo.MacOptions = MacOptions; + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMedium802_5: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMediumFddi: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + break; + + case NdisMediumArcnet878_2: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + break; + + } + + // + // BUGBUG: If functional filtering is set, set the address + // for the appropriate binding. + // + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumFddi: + case NdisMedium802_5: + case NdisMediumArcnet878_2: + + // + // If we have a virtual network number we need to receive + // broadcasts (either the router will be bound in which + // case we want them, or we need to respond to rip requests + // ourselves). + // + + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + + if (Adapter->Device->VirtualNetworkNumber != 0) { + + Adapter->BroadcastEnabled = TRUE; + Adapter->Device->EnableBroadcastCount = 1; + PacketFilter |= NDIS_PACKET_TYPE_BROADCAST; + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter |= NDIS_PACKET_TYPE_FUNCTIONAL; + } + + } else { + + Adapter->BroadcastEnabled = FALSE; + Adapter->Device->EnableBroadcastCount = 0; + + } + + break; + + default: + + CTEAssert (FALSE); + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* IpxInitializeNdis */ + + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when another reason for enabling + broadcast reception is added. If it is the first, then + reception on the card is enabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + ++Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 1) { + + // + // Broadcasts should be enabled. + // + + if (!Device->EnableBroadcastPending) { + + if (Device->DisableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxAddBroadcast */ + + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when a reason for enabling + broadcast reception is removed. If it is the last, then + reception on the card is disabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + --Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 0) { + + // + // Broadcasts should be disabled. + // + + if (!Device->DisableBroadcastPending) { + + if (Device->EnableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxRemoveBroadcast */ + + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine is used to change whether broadcast reception + is enabled or disabled. It performs the requested operation + on every adapter bound to by IPX. + + This routine is called by a worker thread queued when a + bind/unbind operation changes the broadcast state. + +Arguments: + + Parameter - TRUE if broadcasts should be enabled, FALSE + if they should be disabled. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + BOOLEAN Enable = (BOOLEAN)Parameter; + UINT i; + PBINDING Binding; + PADAPTER Adapter; + ULONG PacketFilter; + NDIS_REQUEST IpxRequest; + NDIS_STRING AdapterName; + CTELockHandle LockHandle; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); +#else + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; +#endif + if (Binding == NULL) { + continue; + } + + Adapter = Binding->Adapter; + if (Adapter->BroadcastEnabled == Enable) { + continue; + } + + if (Enable) { + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_FUNCTIONAL); + } else { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST); + } + } else { + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + AdapterName.Buffer = Adapter->AdapterName; + AdapterName.Length = (USHORT)Adapter->AdapterNameLength; + AdapterName.MaximumLength = (USHORT)(Adapter->AdapterNameLength + sizeof(WCHAR)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + (VOID)IpxSubmitNdisRequest (Adapter, &IpxRequest, &AdapterName); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + Adapter->BroadcastEnabled = Enable; + + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Enable) { + + CTEAssert (Device->EnableBroadcastPending); + Device->EnableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } else { + + CTEAssert (Device->DisableBroadcastPending); + Device->DisableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } + + CTEFreeLock (&Device->Lock, LockHandle); + +}/* IpxBroadcastOperation */ + + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in IpxInitializeNdis. + It is written so that it can be called from within IpxInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (Adapter->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + Adapter->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + +#if 0 + if (Adapter->SendPacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->SendPacketPoolHandle); + } + + if (Adapter->ReceivePacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->ReceivePacketPoolHandle); + } + + if (Adapter->NdisBufferPoolHandle != NULL) { + NdisFreeBufferPool (Adapter->NdisBufferPoolHandle); + } +#endif + +} /* IpxCloseNdis */ + + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + Adapter->OpenErrorStatus = OpenErrorStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxOpenAdapterComplete */ + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxCloseAdapterComplete */ + + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + +} /* IpxResetComplete */ + + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxRequestComplete */ + + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PADAPTER Adapter, TmpAdapter; + + PNDIS_WAN_LINE_UP LineUp; + PNDIS_WAN_LINE_DOWN LineDown; + PIPXCP_CONFIGURATION Configuration; // contains ipx net and node + + BOOLEAN UpdateLineUp; + PBINDING Binding, TmpBinding; + PDEVICE Device; + PADDRESS Address; + ULONG CurrentHash; + PIPX_ROUTE_ENTRY RouteEntry; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + PIPX_ADDRESS_DATA IpxAddressData; + PREQUEST Request; + UINT BufferLength; + IPX_LINE_INFO LineInfo; + ULONG Segment; + ULONG LinkSpeed; + PLIST_ENTRY p; + NTSTATUS Status; + UINT i, j; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + IPX_DEFINE_LOCK_HANDLE (OldIrq) + NTSTATUS ntStatus; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + Adapter = (PADAPTER)NdisBindingContext; + + IpxReferenceAdapter(Adapter); +#else + Adapter = (PADAPTER)NdisBindingContext; +#endif + + Device = Adapter->Device; + + switch (NdisStatus) { + + case NDIS_STATUS_WAN_LINE_UP: + + + // + // If the line is already up, then we are just getting + // a change in line conditions, and the IPXCP_CONFIGURATION + // information is not included. If it turns out we need + // all the info, we check the size again later. + // + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_UP)) { + IPX_DEBUG (WAN, ("Line up, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_UP))); +#ifdef _PNP_POWER + goto error_no_lock; +#else + return; +#endif + } + + LineUp = (PNDIS_WAN_LINE_UP)StatusBuffer; + + // + // We scan through the adapter's NIC ID range looking + // for an active binding with the same remote address. + // + + UpdateLineUp = FALSE; + + // + // See if this is a new lineup or not + // + *((ULONG UNALIGNED *)(&Binding)) = + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Binding != NULL) { + UpdateLineUp = TRUE; + } + + if (LineUp->ProtocolType != Adapter->BindSap) { + IPX_DEBUG (WAN, ("Line up, wrong protocol type %lx\n", LineUp->ProtocolType)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; +#else + return; +#endif + } + + Configuration = (PIPXCP_CONFIGURATION)LineUp->ProtocolBuffer; + + // + // PNP_POWER - We hold the exclusive lock to the binding array (thru both the device and adapter) + // and the reference to the adapter at this point. + // + + // + // If this line was previously down, create a new binding + // if needed. + // + + if (!UpdateLineUp) { + + // + // We look for a binding that is allocated but down, if + // we can't find that then we look for any empty spot in + // the adapter's NIC ID range and allocate a binding in it. + // Since we always allocate this way, the allocated + // bindings are all clumped at the beginning and once + // we find a NULL spot we know there are no more + // allocated ones. + // + // We keep track of the first binding on this adapter + // in TmpBinding in case we need config info from it. + // + + TmpBinding = NULL; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (i = Adapter->FirstWanNicId; + i <= Adapter->LastWanNicId; + i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if (TmpBinding == NULL) { + TmpBinding = Binding; + } + + if ((Binding == NULL) || + (!Binding->LineUp)) { + break; + } + } + + if (i > Adapter->LastWanNicId) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, no WAN binding available\n")); + return; + } + + if (Binding == NULL) { + + // + // We need to allocate one. + // + + CTEAssert (TmpBinding != NULL); + +#ifdef _PNP_POWER + // + // CreateBinding does an InterLockedPop with the DeviceLock. + // So, release the lock here. + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + Status = IpxCreateBinding( + Device, + NULL, + 0, + Adapter->AdapterName, + &Binding); + + if (Status != STATUS_SUCCESS) { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + goto error_no_lock; +#else + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + return; +#endif + } + +#ifdef _PNP_POWER + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + // + // Binding->AllRouteXXX doesn't matter for WAN. + // + + Binding->FrameType = ISN_FRAME_TYPE_ETHERNET_II; + Binding->SendFrameHandler = IpxSendFrameWanEthernetII; + ++Adapter->BindingCount; + Binding->Adapter = Adapter; + + Binding->NicId = i; +#ifdef _PNP_POWER + INSERT_BINDING(Device, i, Binding); +#else + Device->Bindings[i] = Binding; +#endif + + // + // Other fields are filled in below. + // + + } + + // + // This is not an update, so note that the line is active. + // + // [FW] Binding->LineUp = TRUE; + Binding->LineUp = LINE_UP; + + if (Configuration->ConnectionClient == 1) { + Binding->DialOutAsync = TRUE; + } else { + Binding->DialOutAsync = FALSE; + } + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + if ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0)) { + + Device->HighestType20NicId = i; + } + } + + // + // We could error out below, trying to insert this network number. In RipShortTimeout + // we dont check for LineUp when calculating the tick counts; set this before the insert + // attempt. + // + Binding->MediumSpeed = LineUp->LinkSpeed; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // [FW] No need to update these if this flag is on since these values will be + // provided with IPX_WAN_CONFIG_DONE ioctl; instead we zero out the fields so that + // IPXWAN packets have proper source addresses. + // + if (Device->ForwarderBound && + Configuration->IpxwanConfigRequired) { + Binding->LocalAddress.NetworkAddress = 0; + RtlZeroMemory (Binding->LocalAddress.NodeAddress, 6); + + } else { + + // + // Add a router entry for this net if there is no router. + // We want the number of ticks for a 576-byte frame, + // given the link speed in 100 bps units, so we calculate + // as: + // + // seconds 18.21 ticks 4608 bits + // --------------------- * ----------- * --------- + // link_speed * 100 bits second frame + // + // to get the formula + // + // ticks/frame = 839 / link_speed. + // + // We add link_speed to the numerator also to ensure + // that the value is at least 1. + // + + if ((!Device->UpperDriverBound[IDENTIFIER_RIP]) && + (*(UNALIGNED ULONG *)Configuration->Network != 0)) { + if (RipInsertLocalNetwork( + *(UNALIGNED ULONG *)Configuration->Network, + Binding->NicId, + Adapter->NdisBindingHandle, + (USHORT)((839 + LineUp->LinkSpeed) / LineUp->LinkSpeed)) != STATUS_SUCCESS) { + // + // This means we couldn't allocate memory, or + // the entry already existed. If it already + // exists we can ignore it for the moment. + // + // BUGBUG: Now it will succeed if the network + // exists. + // + + IPX_DEBUG (WAN, ("Line up, could not insert local network\n")); + // [FW] Binding->LineUp = FALSE; + Binding->LineUp = LINE_DOWN; + + #ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; + #else + return; + #endif + } + } + + + // + // Update our addresses. + // + Binding->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + RtlCopyMemory (Binding->WanRemoteNode, Configuration->RemoteNode, 6); + + // + // Update the device node and all the address + // nodes if we have only one bound, or this is + // binding one. + // + + if (!Device->VirtualNetwork) { + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + Device->SourceAddress.NetworkAddress = *(UNALIGNED ULONG *)(Configuration->Network); + RtlCopyMemory (Device->SourceAddress.NodeAddress, Configuration->LocalNode, 6); + } + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Address->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + } + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + } + + // + // Return the binding context for this puppy! + // + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])) = + *((ULONG UNALIGNED *)(&Binding)); + + RtlCopyMemory (Binding->LocalMacAddress.Address, LineUp->LocalAddress, 6); + RtlCopyMemory (Binding->RemoteMacAddress.Address, LineUp->RemoteAddress, 6); + + // + // Reset this since the line just came up. + // + + Binding->WanInactivityCounter = 0; + + // + // [FW] Update the InterfaceIndex and ConnectionId. + // + Binding->InterfaceIndex = Configuration->InterfaceIndex; + Binding->ConnectionId = Configuration->ConnectionId; + Binding->IpxwanConfigRequired = Configuration->IpxwanConfigRequired; + + // + // [FW] We need to keep track of WAN inactivity counters ourselves. + // Every minute, the wan inactivity counters are incremented for all + // UP WAN lines. + // + IPX_GET_LOCK (&Device->Lock, &LockHandle); + if (Device->UpWanLineCount == 0) { +/* + // + // [BUGBUGZZ] remove this... + // + CTEStartTimer( + &Device->WanInactivityTimer, + 60000, // 1 minute + IpxInternalIncrementWanInactivity, + (PVOID)Device); +*/ + } + + Device->UpWanLineCount++; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + + LinkSpeed = LineUp->LinkSpeed; + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count WAN + // bindings when doing this (although it is unlikely + // a LAN binding would be the winner). + // + // BUGBUG: Update other device information? + // + + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + +#ifdef _PNP_POWER + // + // Release the lock after incrementing the reference count + // + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Device->LinkSpeed = LinkSpeed; + + if ((Adapter->ConfigMaxPacketSize == 0) || + (LineUp->MaximumTotalSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = LineUp->MaximumTotalSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + MacInitializeBindingInfo (Binding, Adapter); + + // + // [FW] If the IpxwanConfigRequired flag is true, we don't inform + // the upper layers until IPXWAN sends down the ioctl to do so. + // + // Inform IpxWan only if this is not an Update; it will be an update in + // the case of multilink. In fact, do not access the Configuration param in + // case UpdateLineUp is TRUE. + // + if (!UpdateLineUp && + Configuration->IpxwanConfigRequired) { + + IPX_DEBUG(WAN, ("IPXWAN configuration required on LineUp: %lx\n", LineUp)); + CTEAssert(!UpdateLineUp); + Binding->LineUp = LINE_CONFIG; + goto InformIpxWan; + } + +#ifdef _PNP_POWER + + // + // We dont give lineups; instead indicate only if the PnP reserved address + // changed to SPX. NB gets all PnP indications with the reserved address case + // marked out. + // + { + IPX_PNP_INFO NBPnPInfo; + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + // + // NB's reserved address changed. + // + NBPnPInfo.NewReservedAddress = TRUE; + + if (!Device->VirtualNetwork) { + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + IpxPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + } + } else { + NBPnPInfo.NewReservedAddress = FALSE; + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + Binding->IsnInformed[IDENTIFIER_NB] = TRUE; + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + NBPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADD_DEVICE (lineup) to NB: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + } + + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + + // + // Give line up to RIP as it is not PnP aware. + // Give lineup to FWD only if it opened this adapter first. + // + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + + // + // Line status, after lineup. + // + if (UpdateLineUp) { + // + // was the lineup given earlier? if not, then dont send this up. + // + if (Binding->IsnInformed[IDENTIFIER_RIP]) { + CTEAssert(Binding->FwdAdapterContext); + + (*Device->UpperDrivers[IDENTIFIER_RIP].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + NULL); + } + + } else { + Binding->IsnInformed[IDENTIFIER_RIP] = TRUE; + (*Device->UpperDrivers[IDENTIFIER_RIP].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + Configuration); + } + } +#else + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + UpdateLineUp ? NULL : Configuration); + } + } +#endif + if (!UpdateLineUp) { + + if ((Device->SingleNetworkActive) && + (Configuration->ConnectionClient == 1)) { + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = TRUE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + // + // If we have a virtual net, do a broadcast now so + // the router on the other end will know about us. + // + // BUGBUG: Use RipSendResponse, and do it even + // if SingleNetworkActive is FALSE?? + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + } + + // + // Find a queued address notify and complete it. + // If WanGlobalNetworkNumber is TRUE, we only do + // this when the first dialin line comes up. + // + + if ((!Device->WanGlobalNetworkNumber || + (!Device->GlobalNetworkIndicated && !Binding->DialOutAsync)) + && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + if (Device->WanGlobalNetworkNumber) { + Device->GlobalWanNetwork = Binding->LocalAddress.NetworkAddress; + Device->GlobalNetworkIndicated = TRUE; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + if (Device->WanGlobalNetworkNumber) { + IpxAddressData->adapternum = Device->SapNicCount - 1; + } else { + IpxAddressData->adapternum = Binding->NicId - 1; + } + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = TRUE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + +InformIpxWan: + Binding->fInfoIndicated = FALSE; + if ((p = ExInterlockedRemoveHeadList( + &Device->NicNtfQueue, + &Device->Lock)) != NULL) + { + Request = LIST_ENTRY_TO_REQUEST(p); + + DbgPrint("IpxStatus: WAN LINE UP\n"); + Status = GetNewNics(Device, Request, FALSE, NULL, 0, FALSE); + if (Status != STATUS_SUCCESS) + { + DbgPrint("WAN Line up screw up\n"); + } + else + { + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(OldIrq); + + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + IpxDereferenceDevice (Device, DREF_NIC_NOTIFY); + } + + } + } + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_LINE_DOWN: + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_DOWN)) { + IPX_DEBUG (WAN, ("Line down, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_DOWN))); + return; + } + + LineDown = (PNDIS_WAN_LINE_DOWN)StatusBuffer; + + *((ULONG UNALIGNED*)(&Binding)) = *((ULONG UNALIGNED*)(&LineDown->LocalAddress[2])); + + CTEAssert(Binding != NULL); + + // + // Note that the WAN line is down. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + // [FW] Binding->LineUp = FALSE; + Binding->LineUp = LINE_DOWN; + + // + // PNP_POWER - we hold the exclusive lock to the binding + // and reference to the adapter at this point. + // + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Binding->NicId == MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + // + // This was the old limit, so we have to scan + // backwards to update it -- we stop when we hit + // a non-WAN binding, or a wan binding that is up and + // dialout, or any wan binding if bit 1 in + // DisableDialinNetbios is off. + // + + for (i = Binding->NicId-1; i >= 1; i--) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, i); +#else + TmpBinding = Device->Bindings[i]; +#endif + + if ((TmpBinding != NULL) && + ((!TmpBinding->Adapter->MacInfo.MediumAsync) || + (TmpBinding->LineUp && + ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0))))) { + + break; + } + } + + Device->HighestType20NicId = i; + + } + + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count LAN + // bindings when doing this. + // + // BUGBUG: Update other device information? + // + + LinkSpeed = 0xffffffff; + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || !TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + + if (LinkSpeed != 0xffffffff) { + Device->LinkSpeed = LinkSpeed; + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + // + // Remove our router entry for this net. + // + + // + // [FW] if this was a line on which IPXWAN config was happening, then we dont do this. + // + if (!Binding->IpxwanConfigRequired && + !Device->UpperDriverBound[IDENTIFIER_RIP]) { + + Segment = RipGetSegment ((PUCHAR)&Binding->LocalAddress.NetworkAddress); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&Binding->LocalAddress.NetworkAddress); + + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + RipDeleteRoute (Segment, RouteEntry); + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + } + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDown); + + } + + // + // [FW] If this was the last UpWanLine, cancel the inactivity timer. + // + /* + IPX_GET_LOCK (&Device->Lock, &LockHandle); + if (--Device->UpWanLineCount == 0) { + if (!CTEStopTimer (&IpxDevice->WanInactivityTimer)) { + DbgPrint("Could not stop the WanInactivityTimer\n"); + DbgBreakPoint(); + } + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + */ + + // + // If this was a line on which IPXWAN config was going on, then we need to tell only the + // IPXWAN layer that the line went down since none of the other clients were informed of + // the line up in the first place. + // + if (Binding->IpxwanConfigRequired) { + goto InformIpxWan1; + } + + // + // Indicate to the upper drivers. + // +#ifdef _PNP_POWER + + // + // DeRegister this address with the TDI clients. + // + { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_PNP_INFO NBPnPInfo; + + CTEAssert(Binding->IsnInformed[IDENTIFIER_NB]); + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NewReservedAddress = FALSE; + NBPnPInfo.FirstORLastDevice = FALSE; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_DELETE_DEVICE (linedown) to NB: addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } + + // + // Indicate to the Fwd only if it opened this adapter first. + // + if (Device->UpperDriverBound[IDENTIFIER_RIP] && + (!Device->ForwarderBound || Binding->FwdAdapterContext)) { + + (*Device->UpperDrivers[IDENTIFIER_RIP].LineDownHandler)( + Binding->NicId, + Binding->FwdAdapterContext); + + CTEAssert(Binding->IsnInformed[IDENTIFIER_RIP]); + + Binding->IsnInformed[IDENTIFIER_RIP] = FALSE; + } +#else + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineDownHandler)( + Binding->NicId); + } + } +#endif + + if ((Device->SingleNetworkActive) && + (Binding->DialOutAsync)) { + + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = FALSE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + } + + // + // Find a queued address notify and complete it. + // + + if ((!Device->WanGlobalNetworkNumber) && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + IpxAddressData->adapternum = Binding->NicId - 1; + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = FALSE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + +InformIpxWan1: + Binding->fInfoIndicated = FALSE; + if ((p = ExInterlockedRemoveHeadList( + &Device->NicNtfQueue, + &Device->Lock)) != NULL) + { + + Request = LIST_ENTRY_TO_REQUEST(p); + DbgPrint("IpxStatus: WAN LINE DOWN\n"); + + Status = GetNewNics(Device, Request, FALSE, NULL, 0, FALSE); + if (Status != STATUS_SUCCESS) + { + DbgPrint("WAN Line down screw up\n"); + } + else + { + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(OldIrq); + + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); //noop + IpxDereferenceDevice (Device, DREF_NIC_NOTIFY); + } + } + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_FRAGMENT: + + // + // No response needed, IPX is a datagram service. + // + // BUGBUG: What about telling Netbios/SPX? + // + + break; + + default: + + break; + + } + +#ifdef _PNP_POWER +error_no_lock: + IpxDereferenceAdapter(Adapter); +#endif + +} /* IpxStatus */ + + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} /* IpxStatusComplete */ + + + diff --git a/private/ntos/tdi/isn/ipx/nwlnkipx.ini b/private/ntos/tdi/isn/ipx/nwlnkipx.ini new file mode 100644 index 000000000..416f0c6b7 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/nwlnkipx.ini @@ -0,0 +1,191 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkIpx + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnkipx.sys + DisplayName = NWLINK2 IPX Protocol + Group = TDI + DependOnService = REG_MULTI_SZ + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\Elnkii1" + Export = REG_MULTI_SZ "\Device\NwlnkIpx" + Route = REG_MULTI_SZ ""Elnkii" "Elnkii1"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + NetConfig + Elnkii1 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard2 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard3 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard4 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard5 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Parameters + DedicatedRouter = REG_DWORD 0x00000000 + InitDatagrams = REG_DWORD 0x0000000a + MaxDatagrams = REG_DWORD 0x00000032 + RipAgeTime = REG_DWORD 0x00000005 + RipCount = REG_DWORD 0x00000005 + RipTimeout = REG_DWORD 0x00000001 + RipUsageTime = REG_DWORD 0x0000000f + SourceRouteUsageTime = REG_DWORD 0x0000000a + SocketUniqueness = REG_DWORD 0x00000008 + VirtualNetworkNumber = REG_DWORD 0x00000000 + VirtualNetworkOptional = REG_DWORD 0x00000001 + Winsock + Mapping = REG_BINARY 0x00000c08 + 0x00000100 0x00000003 0x00000006 0x00000002 0x000003e8 0x00000006 0x00000002 0x000003e9 + 0x00000006 0x00000002 0x000003ea 0x00000006 0x00000002 0x000003eb 0x00000006 0x00000002 + 0x000003ec 0x00000006 0x00000002 0x000003ed 0x00000006 0x00000002 0x000003ee 0x00000006 + 0x00000002 0x000003ef 0x00000006 0x00000002 0x000003f0 0x00000006 0x00000002 0x000003f1 + 0x00000006 0x00000002 0x000003f2 0x00000006 0x00000002 0x000003f3 0x00000006 0x00000002 + 0x000003f4 0x00000006 0x00000002 0x000003f5 0x00000006 0x00000002 0x000003f6 0x00000006 + 0x00000002 0x000003f7 0x00000006 0x00000002 0x000003f8 0x00000006 0x00000002 0x000003f9 + 0x00000006 0x00000002 0x000003fa 0x00000006 0x00000002 0x000003fb 0x00000006 0x00000002 + 0x000003fc 0x00000006 0x00000002 0x000003fd 0x00000006 0x00000002 0x000003fe 0x00000006 + 0x00000002 0x000003ff 0x00000006 0x00000002 0x00000400 0x00000006 0x00000002 0x00000401 + 0x00000006 0x00000002 0x00000402 0x00000006 0x00000002 0x00000403 0x00000006 0x00000002 + 0x00000404 0x00000006 0x00000002 0x00000405 0x00000006 0x00000002 0x00000406 0x00000006 + 0x00000002 0x00000407 0x00000006 0x00000002 0x00000408 0x00000006 0x00000002 0x00000409 + 0x00000006 0x00000002 0x0000040a 0x00000006 0x00000002 0x0000040b 0x00000006 0x00000002 + 0x0000040c 0x00000006 0x00000002 0x0000040d 0x00000006 0x00000002 0x0000040e 0x00000006 + 0x00000002 0x0000040f 0x00000006 0x00000002 0x00000410 0x00000006 0x00000002 0x00000411 + 0x00000006 0x00000002 0x00000412 0x00000006 0x00000002 0x00000413 0x00000006 0x00000002 + 0x00000414 0x00000006 0x00000002 0x00000415 0x00000006 0x00000002 0x00000416 0x00000006 + 0x00000002 0x00000417 0x00000006 0x00000002 0x00000418 0x00000006 0x00000002 0x00000419 + 0x00000006 0x00000002 0x0000041a 0x00000006 0x00000002 0x0000041b 0x00000006 0x00000002 + 0x0000041c 0x00000006 0x00000002 0x0000041d 0x00000006 0x00000002 0x0000041e 0x00000006 + 0x00000002 0x0000041f 0x00000006 0x00000002 0x00000420 0x00000006 0x00000002 0x00000421 + 0x00000006 0x00000002 0x00000422 0x00000006 0x00000002 0x00000423 0x00000006 0x00000002 + 0x00000424 0x00000006 0x00000002 0x00000425 0x00000006 0x00000002 0x00000426 0x00000006 + 0x00000002 0x00000427 0x00000006 0x00000002 0x00000428 0x00000006 0x00000002 0x00000429 + 0x00000006 0x00000002 0x0000042a 0x00000006 0x00000002 0x0000042b 0x00000006 0x00000002 + 0x0000042c 0x00000006 0x00000002 0x0000042d 0x00000006 0x00000002 0x0000042e 0x00000006 + 0x00000002 0x0000042f 0x00000006 0x00000002 0x00000430 0x00000006 0x00000002 0x00000431 + 0x00000006 0x00000002 0x00000432 0x00000006 0x00000002 0x00000433 0x00000006 0x00000002 + 0x00000434 0x00000006 0x00000002 0x00000435 0x00000006 0x00000002 0x00000436 0x00000006 + 0x00000002 0x00000437 0x00000006 0x00000002 0x00000438 0x00000006 0x00000002 0x00000439 + 0x00000006 0x00000002 0x0000043a 0x00000006 0x00000002 0x0000043b 0x00000006 0x00000002 + 0x0000043c 0x00000006 0x00000002 0x0000043d 0x00000006 0x00000002 0x0000043e 0x00000006 + 0x00000002 0x0000043f 0x00000006 0x00000002 0x00000440 0x00000006 0x00000002 0x00000441 + 0x00000006 0x00000002 0x00000442 0x00000006 0x00000002 0x00000443 0x00000006 0x00000002 + 0x00000444 0x00000006 0x00000002 0x00000445 0x00000006 0x00000002 0x00000446 0x00000006 + 0x00000002 0x00000447 0x00000006 0x00000002 0x00000448 0x00000006 0x00000002 0x00000449 + 0x00000006 0x00000002 0x0000044a 0x00000006 0x00000002 0x0000044b 0x00000006 0x00000002 + 0x0000044c 0x00000006 0x00000002 0x0000044d 0x00000006 0x00000002 0x0000044e 0x00000006 + 0x00000002 0x0000044f 0x00000006 0x00000002 0x00000450 0x00000006 0x00000002 0x00000451 + 0x00000006 0x00000002 0x00000452 0x00000006 0x00000002 0x00000453 0x00000006 0x00000002 + 0x00000454 0x00000006 0x00000002 0x00000455 0x00000006 0x00000002 0x00000456 0x00000006 + 0x00000002 0x00000457 0x00000006 0x00000002 0x00000458 0x00000006 0x00000002 0x00000459 + 0x00000006 0x00000002 0x0000045a 0x00000006 0x00000002 0x0000045b 0x00000006 0x00000002 + 0x0000045c 0x00000006 0x00000002 0x0000045d 0x00000006 0x00000002 0x0000045e 0x00000006 + 0x00000002 0x0000045f 0x00000006 0x00000002 0x00000460 0x00000006 0x00000002 0x00000461 + 0x00000006 0x00000002 0x00000462 0x00000006 0x00000002 0x00000463 0x00000006 0x00000002 + 0x00000464 0x00000006 0x00000002 0x00000465 0x00000006 0x00000002 0x00000466 0x00000006 + 0x00000002 0x00000467 0x00000006 0x00000002 0x00000468 0x00000006 0x00000002 0x00000469 + 0x00000006 0x00000002 0x0000046a 0x00000006 0x00000002 0x0000046b 0x00000006 0x00000002 + 0x0000046c 0x00000006 0x00000002 0x0000046d 0x00000006 0x00000002 0x0000046e 0x00000006 + 0x00000002 0x0000046f 0x00000006 0x00000002 0x00000470 0x00000006 0x00000002 0x00000471 + 0x00000006 0x00000002 0x00000472 0x00000006 0x00000002 0x00000473 0x00000006 0x00000002 + 0x00000474 0x00000006 0x00000002 0x00000475 0x00000006 0x00000002 0x00000476 0x00000006 + 0x00000002 0x00000477 0x00000006 0x00000002 0x00000478 0x00000006 0x00000002 0x00000479 + 0x00000006 0x00000002 0x0000047a 0x00000006 0x00000002 0x0000047b 0x00000006 0x00000002 + 0x0000047c 0x00000006 0x00000002 0x0000047d 0x00000006 0x00000002 0x0000047e 0x00000006 + 0x00000002 0x0000047f 0x00000006 0x00000002 0x00000480 0x00000006 0x00000002 0x00000481 + 0x00000006 0x00000002 0x00000482 0x00000006 0x00000002 0x00000483 0x00000006 0x00000002 + 0x00000484 0x00000006 0x00000002 0x00000485 0x00000006 0x00000002 0x00000486 0x00000006 + 0x00000002 0x00000487 0x00000006 0x00000002 0x00000488 0x00000006 0x00000002 0x00000489 + 0x00000006 0x00000002 0x0000048a 0x00000006 0x00000002 0x0000048b 0x00000006 0x00000002 + 0x0000048c 0x00000006 0x00000002 0x0000048d 0x00000006 0x00000002 0x0000048e 0x00000006 + 0x00000002 0x0000048f 0x00000006 0x00000002 0x00000490 0x00000006 0x00000002 0x00000491 + 0x00000006 0x00000002 0x00000492 0x00000006 0x00000002 0x00000493 0x00000006 0x00000002 + 0x00000494 0x00000006 0x00000002 0x00000495 0x00000006 0x00000002 0x00000496 0x00000006 + 0x00000002 0x00000497 0x00000006 0x00000002 0x00000498 0x00000006 0x00000002 0x00000499 + 0x00000006 0x00000002 0x0000049a 0x00000006 0x00000002 0x0000049b 0x00000006 0x00000002 + 0x0000049c 0x00000006 0x00000002 0x0000049d 0x00000006 0x00000002 0x0000049e 0x00000006 + 0x00000002 0x0000049f 0x00000006 0x00000002 0x000004a0 0x00000006 0x00000002 0x000004a1 + 0x00000006 0x00000002 0x000004a2 0x00000006 0x00000002 0x000004a3 0x00000006 0x00000002 + 0x000004a4 0x00000006 0x00000002 0x000004a5 0x00000006 0x00000002 0x000004a6 0x00000006 + 0x00000002 0x000004a7 0x00000006 0x00000002 0x000004a8 0x00000006 0x00000002 0x000004a9 + 0x00000006 0x00000002 0x000004aa 0x00000006 0x00000002 0x000004ab 0x00000006 0x00000002 + 0x000004ac 0x00000006 0x00000002 0x000004ad 0x00000006 0x00000002 0x000004ae 0x00000006 + 0x00000002 0x000004af 0x00000006 0x00000002 0x000004b0 0x00000006 0x00000002 0x000004b1 + 0x00000006 0x00000002 0x000004b2 0x00000006 0x00000002 0x000004b3 0x00000006 0x00000002 + 0x000004b4 0x00000006 0x00000002 0x000004b5 0x00000006 0x00000002 0x000004b6 0x00000006 + 0x00000002 0x000004b7 0x00000006 0x00000002 0x000004b8 0x00000006 0x00000002 0x000004b9 + 0x00000006 0x00000002 0x000004ba 0x00000006 0x00000002 0x000004bb 0x00000006 0x00000002 + 0x000004bc 0x00000006 0x00000002 0x000004bd 0x00000006 0x00000002 0x000004be 0x00000006 + 0x00000002 0x000004bf 0x00000006 0x00000002 0x000004c0 0x00000006 0x00000002 0x000004c1 + 0x00000006 0x00000002 0x000004c2 0x00000006 0x00000002 0x000004c3 0x00000006 0x00000002 + 0x000004c4 0x00000006 0x00000002 0x000004c5 0x00000006 0x00000002 0x000004c6 0x00000006 + 0x00000002 0x000004c7 0x00000006 0x00000002 0x000004c8 0x00000006 0x00000002 0x000004c9 + 0x00000006 0x00000002 0x000004ca 0x00000006 0x00000002 0x000004cb 0x00000006 0x00000002 + 0x000004cc 0x00000006 0x00000002 0x000004cd 0x00000006 0x00000002 0x000004ce 0x00000006 + 0x00000002 0x000004cf 0x00000006 0x00000002 0x000004d0 0x00000006 0x00000002 0x000004d1 + 0x00000006 0x00000002 0x000004d2 0x00000006 0x00000002 0x000004d3 0x00000006 0x00000002 + 0x000004d4 0x00000006 0x00000002 0x000004d5 0x00000006 0x00000002 0x000004d6 0x00000006 + 0x00000002 0x000004d7 0x00000006 0x00000002 0x000004d8 0x00000006 0x00000002 0x000004d9 + 0x00000006 0x00000002 0x000004da 0x00000006 0x00000002 0x000004db 0x00000006 0x00000002 + 0x000004dc 0x00000006 0x00000002 0x000004dd 0x00000006 0x00000002 0x000004de 0x00000006 + 0x00000002 0x000004df 0x00000006 0x00000002 0x000004e0 0x00000006 0x00000002 0x000004e1 + 0x00000006 0x00000002 0x000004e2 0x00000006 0x00000002 0x000004e3 0x00000006 0x00000002 + 0x000004e4 0x00000006 0x00000002 0x000004e5 0x00000006 0x00000002 0x000004e6 0x00000006 + 0x00000002 0x000004e7 + + HelperDllName = REG_EXPAND_SZ %SystemRoot%\system32\wshisn.dll + MinSockaddrLength = REG_DWORD 0x0000000e + MaxSockaddrLength = REG_DWORD 0x00000010 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkIpx + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isn/ipx/nwlnkipx.rc b/private/ntos/tdi/isn/ipx/nwlnkipx.rc new file mode 100644 index 000000000..0f437a15d --- /dev/null +++ b/private/ntos/tdi/isn/ipx/nwlnkipx.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkipx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkipx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/ipx/packet.c b/private/ntos/tdi/isn/ipx/packet.c new file mode 100644 index 000000000..f70154b03 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/packet.c @@ -0,0 +1,1560 @@ +/*++ +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisMacBuffer, + Device->NdisBufferPoolHandle, + Header, + MAC_HEADER_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisIpxBuffer, + Device->NdisBufferPoolHandle, + Header + MAC_HEADER_SIZE, + IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisMacBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisIpxBuffer); + + // + // This flag optimizes the virtual to physical address X-ln + // in the MAC drivers on x86 + // + NdisMacBuffer->MdlFlags|=MDL_NETWORK_HEADER; + NdisIpxBuffer->MdlFlags|=MDL_NETWORK_HEADER; + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisMacBuffer; + Reserved->PaddingBuffer = NULL; +#if BACK_FILL + Reserved->BackFill = FALSE; +#endif + + ExInterlockedInsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeSendPacket */ + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + + IPX_DEBUG (PACKET, ("Initializing backfill packet\n")); + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = NULL; + Reserved->HeaderBuffer = NULL; + Reserved->PaddingBuffer = NULL; + Reserved->BackFill = TRUE; + + ExInterlockedInsertHeadList( + &Device->GlobalBackFillPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + IPX_DEBUG (PACKET, ("Initializing backfill packet Done\n")); + return STATUS_SUCCESS; + +} /* IpxInitializeBackFillPacket */ +#endif + + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PIPX_RECEIVE_RESERVED Reserved; + + IpxAllocateReceivePacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->TransferInProgress = FALSE; + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = NULL; + InitializeListHead (&Reserved->Requests); + + ExInterlockedInsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceivePacket */ + + +NTSTATUS +IpxInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + PDEVICE Device = Adapter->Device; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + ExInterlockedInsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceiveBuffer */ + + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a padding buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + PaddingBuffer - The receive buffer to initialize. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + PaddingBuffer->Data, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NDIS_BUFFER_LINKAGE(NdisBuffer) = (PNDIS_BUFFER)NULL; + PaddingBuffer->NdisBuffer = NdisBuffer; + PaddingBuffer->DataLength = DataBufferLength; + RtlZeroMemory (PaddingBuffer->Data, DataBufferLength); + + return STATUS_SUCCESS; + +} /* IpxInitializePaddingBuffer */ + + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Free the packet in a slightly unconventional way; this + // allows us to not have to NULL out HeaderBuffer's linkage + // field during normal operations when we put it back in + // the free pool. + // + + NdisBuffer = Reserved->HeaderBuffer; + NdisIpxBuffer = NDIS_BUFFER_LINKAGE(NdisBuffer); + NDIS_BUFFER_LINKAGE (NdisBuffer) = NULL; + NDIS_BUFFER_LINKAGE (NdisIpxBuffer) = NULL; + +#if 0 + NdisAdjustBufferLength (NdisBuffer, PACKET_HEADER_SIZE); +#endif + NdisAdjustBufferLength (NdisBuffer, MAC_HEADER_SIZE); + NdisAdjustBufferLength (NdisIpxBuffer, IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + NdisFreeBuffer (NdisBuffer); + NdisFreeBuffer (NdisIpxBuffer); + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + +} /* IpxDeinitializeSendPacket */ + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a back fill packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + IPX_DEBUG (PACKET, ("DeInitializing backfill packet\n")); + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + IPX_DEBUG (PACKET, ("DeInitializing backfill packet Done\n")); + + +} /* IpxDeinitializeBackFillPacket */ +#endif + + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + IpxFreeReceivePacket (Device, Packet); + +} /* IpxDeinitializeReceivePacket */ + + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + + DataBufferLength - The allocated length of the receive buffer. + +Return Value: + + None. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = Adapter->Device; + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + NdisAdjustBufferLength (ReceiveBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* IpxDeinitializeReceiveBuffer */ + + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a padding buffer. + +Arguments: + + Device - The device. + + PaddingBuffer - The padding buffer. + + DataBufferLength - The allocated length of the padding buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisAdjustBufferLength (PaddingBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (PaddingBuffer->NdisBuffer); + +} /* IpxDeinitializePaddingBuffer */ + + + +#ifdef IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (PACKET_HEADER_SIZE * Device->InitDatagrams); + + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitDatagrams]); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (IpxInitializeSendPacket (Device, Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += PACKET_HEADER_SIZE; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedDatagrams += SendPool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateSendPool */ + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (BackFillPoolSize, MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate BackFill pool memory\n")); + return; + } + + + + + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + + if (IpxInitializeBackFillPacket (Device, Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + + } + + BackFillPool->PacketCount = PacketNum; + BackFillPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < BackFillPool->PacketCount; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + + IPX_DEBUG (PACKET, ("Allocation of backfill pool done\n")); + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PIPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + ReceivePool = (PIPX_RECEIVE_POOL)IpxAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (IpxInitializeReceivePacket (Device, Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceivePool */ + + +#else // IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT HeaderSize; + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + NDIS_STATUS Status; + + CTELockHandle LockHandle; + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + HeaderSize = PACKET_HEADER_SIZE * Device->InitDatagrams; + + Header = (PUCHAR)IpxAllocateMemory (HeaderSize, MEMORY_PACKET, "SendPool"); + + if (Header == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate header memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &SendPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + SendPool->Header = Header; + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), SendPool->PoolHandle); + + if (IpxInitializeSendPacket (Device, &Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + Header += PACKET_HEADER_SIZE; + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedDatagrams += PacketNum; + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateSendPool */ + + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + PIPX_SEND_POOL BackFillPool; + NDIS_STATUS Status; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate backfill pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &BackFillPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), BackFillPool->PoolHandle); + + if (IpxInitializeBackFillPacket (Device, &Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + } + + CTEGetLock (&Device->Lock, &LockHandle); + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT PacketNum; + IPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + NDIS_STATUS Status; + + ReceivePool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &ReceivePool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitReceivePackets * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), ReceivePool->PoolHandle); + + if (IpxInitializeReceivePacket (Device, &Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedReceivePackets += PacketNum; + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateReceivePool */ +#endif // IPX_OWN_PACKETS + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this adapter. + +Arguments: + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + UINT DataLength; + PUCHAR Data; + CTELockHandle LockHandle; + + DataLength = Adapter->MaxReceivePacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (DataLength * Device->InitReceiveBuffers); + + ReceiveBufferPool = (PIPX_RECEIVE_BUFFER_POOL)IpxAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Init recv buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitReceiveBuffers, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitReceiveBuffers]); + + + for (BufferNum = 0; BufferNum < Device->InitReceiveBuffers; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (IpxInitializeReceiveBuffer (Adapter, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef IPX_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + IPX_PUSH_ENTRY_LIST (&Adapter->ReceiveBufferList, &ReceiveBuffer->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Adapter->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Adapter->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceiveBufferPool */ + + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateSendPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopSendPacket */ + +#if BACK_FILL + +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + IPX_DEBUG (PACKET, ("Popping backfill packet\n")); + + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBackFillPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + + IPX_DEBUG (PACKET, ("Popping backfill packet done\n")); + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBackFillPacket */ +#endif //BackFill + + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxReceivePackets) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceivePool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the adapter's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Adapter - Pointer to our adapter to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PDEVICE Device = Adapter->Device; + + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Adapter->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceiveBufferPool (Adapter); + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceiveBuffer */ + + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a padding buffer for use by all devices. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the allocated padding buffer. + +--*/ + +{ + PIPX_PADDING_BUFFER PaddingBuffer; + ULONG PaddingBufferSize; + + // + // We are assuming that we can use 1 global padding buffer for ALL + // transmits! We must therefore test to make sure that EthernetExtraPadding + // is not greater than 1. Otherwise, we must assume that the extra padding + // is being used for something and we therefore cannot share across all + // transmit requests. + // + + // + // We cannot support more than 1 byte padding space, since we allocate only + // one buffer for all transmit requests. + // + + if ( Device->EthernetExtraPadding > 1 ) { + IPX_DEBUG (PACKET, ("Padding buffer cannot be more than 1 byte\n")); + DbgBreakPoint(); + } + + // + // Allocate a padding buffer if possible. + // + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + + PaddingBuffer = IpxAllocateMemory (PaddingBufferSize, MEMORY_PACKET, "PaddingBuffer"); + + if (PaddingBuffer != NULL) { + + if (IpxInitializePaddingBuffer (Device, PaddingBuffer, Device->EthernetExtraPadding) != + STATUS_SUCCESS) { + IpxFreeMemory (PaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer"); + } else { + IPX_DEBUG (PACKET, ("Allocate padding buffer %lx\n", PaddingBuffer)); + return PaddingBuffer; + } + } + + return NULL; + +} /* IpxAllocatePaddingBuffer */ + + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine deallocates the padding buffer. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + None + +--*/ + +{ + ULONG PaddingBufferSize; + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + return; + } + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + IpxFreeMemory( IpxPaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer" ); + IpxPaddingBuffer = (PIPX_PADDING_BUFFER)NULL; + +} /* IpxFreePaddingBuffer */ + diff --git a/private/ntos/tdi/isn/ipx/precomp.h b/private/ntos/tdi/isn/ipx/precomp.h new file mode 100644 index 000000000..21775002a --- /dev/null +++ b/private/ntos/tdi/isn/ipx/precomp.h @@ -0,0 +1,45 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 +#define _HIPX_SUBAGNT + +#include +#include +#include +#include +#include +#include "isnipx.h" +#include "config.h" +#include "mac.h" +#include "ipxtypes.h" +#include "ipxprocs.h" +#include diff --git a/private/ntos/tdi/isn/ipx/query.c b/private/ntos/tdi/isn/ipx/query.c new file mode 100644 index 000000000..28b38df5c --- /dev/null +++ b/private/ntos/tdi/isn/ipx/query.c @@ -0,0 +1,297 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PADDRESS_FILE AddressFile; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PBINDING Binding; + union { + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS IpxAddress; + } AddressInfo; + TDI_DATAGRAM_INFO DatagramInfo; + TDI_ADDRESS_IPX IpxAddress; + } TempBuffer; + UINT i; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // what type of status do we want? + // + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + status = IpxVerifyAddressFile (AddressFile); + + if (status == STATUS_SUCCESS) { + + TempBuffer.AddressInfo.ActivityCount = 0; + + IpxBuildTdiAddress( + &TempBuffer.AddressInfo.IpxAddress, + Device->SourceAddress.NetworkAddress, + Device->SourceAddress.NodeAddress, + AddressFile->Address->Socket); + + status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(TempBuffer.AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(Device->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS: + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + + TransportAddress = IpxAllocateMemory(sizeof(int) + (ElementSize * MIN (Device->MaxBindings, Device->ValidBindings)), MEMORY_QUERY, "NetworkAddress"); + + if (TransportAddress == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } +#endif + status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + CTEFreeMem (TransportAddress); + + } + + break; + + default: + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* IpxTdiQueryInformation */ + + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* IpxTdiSetInformation */ + + diff --git a/private/ntos/tdi/isn/ipx/receive.c b/private/ntos/tdi/isn/ipx/receive.c new file mode 100644 index 000000000..6bed71e3c --- /dev/null +++ b/private/ntos/tdi/isn/ipx/receive.c @@ -0,0 +1,493 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceiveDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to complete any pended requests to our clients. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + PIPX_RECEIVE_RESERVED Reserved = (PIPX_RECEIVE_RESERVED)(NdisPacket->ProtocolReserved); + PREQUEST Request, LastRequest; + PADDRESS_FILE AddressFile; + ULONG ByteOffset; + PLIST_ENTRY p; + PDEVICE Device; + + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + + if (!Reserved->pContext) { + + if (Reserved->SingleRequest) { + + // + // The transfer was directly into the client buffer, + // so simply complete the request. + // + + Request = Reserved->SingleRequest; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (RECEIVE, ("Transferred %d bytes\n", BytesTransferred)); + REQUEST_INFORMATION(Request) = BytesTransferred; + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RECEIVE, ("Transfer failed\n")); + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + LastRequest = Request; + Reserved->SingleRequest = NULL; + + } else { + + // + // Multiple clients requested this datagram. Save + // the last one to delay queueing it for completion. + // + + LastRequest = LIST_ENTRY_TO_REQUEST (Reserved->Requests.Blink); + + while (TRUE) { + + p = RemoveHeadList (&Reserved->Requests); + if (p == &Reserved->Requests) { + break; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + if (AddressFile->ReceiveIpxHeader) { + ByteOffset = 0; + } else { + ByteOffset = sizeof(IPX_HEADER); + } + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl( + Reserved->ReceiveBuffer->Data, + ByteOffset + REQUEST_INFORMATION(Request), + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + if (Request != LastRequest) { + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + } + + } + + // + // Now free the receive buffer back. + // + + IPX_PUSH_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Reserved->ReceiveBuffer->PoolLinkage, + &Adapter->Device->SListsLock); + + Reserved->ReceiveBuffer = NULL; + + } + + } else { + //IpxPrint0("IpxTransferDataComplete: Calling PassDgToRt\n"); + //ByteOffset = sizeof(IPX_HEADER); + ByteOffset = 0; + PassDgToRt(IpxDevice, Reserved->pContext, Reserved->Index, + &Reserved->ReceiveBuffer->Data[ByteOffset], + BytesTransferred); + + // + // Free the memory allocated for options. + // + IpxFreeMemory(Reserved->pContext, sizeof(IPX_DATAGRAM_OPTIONS2), + MEMORY_PACKET, "RT OPTIONS"); + // + // Now free the receive buffer back. + // + + IPX_PUSH_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Reserved->ReceiveBuffer->PoolLinkage, + Adapter->DeviceLock); + + Reserved->ReceiveBuffer = NULL; + } + + // + // Now free the packet. + // + + NdisReinitializePacket (NdisPacket); + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->ReceivePacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->ReceivePacketInUse); + + } else { + + Device = Adapter->Device; + + IPX_PUSH_ENTRY_LIST( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + if (!Reserved->pContext) { + // + // We Delay inserting the last request (or the only one) + // until after we have put the packet back, to keep the + // address around if needed (the address won't go away + // until the last address file does, and the address file + // won't go away until the datagram is completed). + // + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(LastRequest), + Adapter->DeviceLock); + } + + IpxReceiveComplete ((NDIS_HANDLE)Adapter); + + break; + + default: + + Device = Adapter->Device; + + (*Device->UpperDrivers[Reserved->Identifier].TransferDataCompleteHandler)( + NdisPacket, + NdisStatus, + BytesTransferred); + + break; + + } + +} /* IpxTransferDataComplete */ + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called by all tightly bound clients instead of NdisTransferData. + If this is a loopback packet, the transfer is done directly here, else NdisTransferData + is called. + +Arguments: + + Status - status of operation + NdisBindingHandle - Loopback cookie or Ndis context + MacReceiveContext - Loopback packet or Mac context + ByteOffset - Source offset + BytesToTransfer - length of the transfer desired + Packet - dest packet + BytesTransferred - length of successful transfer + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + // + // If this is a loopback packet, copy the data directly + // + if (NdisBindingHandle == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("LoopbXfer: src: %lx, dest: %lx, bytestoxfer: %lx\n", + MacReceiveContext, Packet, BytesToTransfer)); + + NdisCopyFromPacketToPacket( + Packet, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset + BytesTransferred); // BytesCopied + + *Status = NDIS_STATUS_SUCCESS; + } else { + NdisTransferData( + Status, + NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + Packet, + BytesTransferred); + } +} + + + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS Address; + PADDRESS_FILE AddressFile; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + Address = AddressFile->Address; + + if ((Address == NULL) || + (Address->Size != sizeof (ADDRESS)) || + (Address->Type != IPX_ADDRESS_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + IPX_BEGIN_SYNC (&SyncContext); + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_INVALID_HANDLE; + } + + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, IpxCancelReceiveDatagram); + + if (Request->Cancel) { + + (VOID)RemoveTailList (&AddressFile->ReceiveDatagramQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_CANCELLED; + } + + IPX_DEBUG (RECEIVE, ("RDG posted on %lx\n", AddressFile)); + + IpxReferenceAddressFileLock (AddressFile, AFREF_RCV_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + +} /* IpxTdiReceiveDatagram */ + + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + IPX_DEBUG(RECEIVE, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + IpxFreeRequest(IpxDevice, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* IpxCancelReceiveDatagram */ + + diff --git a/private/ntos/tdi/isn/ipx/rip.c b/private/ntos/tdi/isn/ipx/rip.c new file mode 100644 index 000000000..950949ca0 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/rip.c @@ -0,0 +1,2700 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rip.c + +Abstract: + + This module contains code that implements the client-side + RIP support and simple router table support. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ) + +/*++ + +Routine Description: + + This routine looks up the proper route for the specified remote + address. If a RIP request needs to be generated it does so. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD. + NOTE: IN THE CASE OF PnP, THIS COMES WITH THE BIND LOCK SHARED. + +Arguments: + + Segment - The segment associate with the remote address. + + RemoteAddress - The IPX address of the remote. + + Type - One of IPX_FIND_ROUTE_NO_RIP, IPX_FIND_ROUTE_RIP_IF_NEEDED, + or IPX_FIND_ROUTE_FORCE_RIP. + + LocalTarget - Returns the next router information. + + Counts - If specified, used to return the tick and hop count. + +Return Value: + + STATUS_SUCCESS if a route is found, STATUS_PENDING if a + RIP request needs to be generated, failure status if a + RIP request packet cannot be allocated. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + PBINDING Binding; + UINT i; + + + // + // Packets sent to network 0 go on the first adapter also. + // + + if (RemoteAddress->NetworkAddress == 0) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 1); + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed) / + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#else + LocalTarget->NicId = 1; + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + Device->Bindings[1]->MediumSpeed) / + Device->Bindings[1]->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#endif + return STATUS_SUCCESS; + } + + // + // See if this is a packet sent to our virtual network. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + // + // Send it through adapter 1. + // BUGBUG: Do real loopback. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, LOOPBACK_NIC_ID); + RtlCopyMemory (LocalTarget->MacAddress, NIC_ID_TO_BINDING(Device, 1)->LocalMacAddress.Address, 6); +#else + // + // Loopback this packet + // + LocalTarget->NicId = 0; + RtlCopyMemory (LocalTarget->MacAddress, Device->Bindings[1]->LocalMacAddress.Address, 6); +#endif + + IPX_DEBUG (LOOPB, ("Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = 1; // tick count + Counts[1] = 1; // hop count + } + return STATUS_SUCCESS; + + } + + // + // Look up the route in the table. If the net is one + // of the ones we are directly attached to, this will + // return an entry with the correct flag set. + // + + RouteEntry = RipGetRoute(Segment, (PUCHAR)&(RemoteAddress->NetworkAddress)); + + if (RouteEntry != NULL) { + + RouteEntry->Timer = 0; +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, RouteEntry->NicId); +#else + LocalTarget->NicId = RouteEntry->NicId; +#endif + if (RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) { + + // + // The machine is on the same net, so send it directly. + // + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + + if (RouteEntry->Flags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // + // The NicId here is bogus, we have to scan through + // our bindings until we find one whose indicated + // IPX remote node matches the destination node of + // this frame. We don't scan into the duplicate + // binding set members since they won't be WANs. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != (PBINDING)NULL) && + (Binding->Adapter->MacInfo.MediumAsync) && + (RtlEqualMemory( + Binding->WanRemoteNode, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + break; + + } + } + } + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + } + + } else { + // + // Find out if this is a loopback packet. If so, return NicId 0 + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + // + // Self-directed - loopback + // + if ((Binding != (PBINDING)NULL) && + (RtlEqualMemory( + Binding->LocalAddress.NodeAddress, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, LOOPBACK_NIC_ID); +#else + LocalTarget->NicId = 0; +#endif + + IPX_DEBUG (LOOPB, ("2.Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + break; + + } + } + } + } + + } else { + + CTEAssert ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0); + + // + // This is not a locally attached net, so if the caller + // is forcing a re-RIP then do that. + // + + if (Type == IPX_FIND_ROUTE_FORCE_RIP) { + goto QueueUpRequest; + } + + // + // Fill in the address of the next router in the route. + // + + RtlCopyMemory (LocalTarget->MacAddress, RouteEntry->NextRouter, 6); + + } + + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = RouteEntry->TickCount; + Counts[1] = RouteEntry->HopCount; + } + + return STATUS_SUCCESS; + + } + +QueueUpRequest: + + if (Type == IPX_FIND_ROUTE_NO_RIP) { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + + } else { + + return RipQueueRequest (RemoteAddress->NetworkAddress, RIP_REQUEST); + + } + +} /* RipGetLocalTarget */ + + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ) + +/*++ + +Routine Description: + + This routine queues up a request for a RIP route. It can be + used to find a specific route or to discover the locally + attached network (if Network is 0). It can also be used + to do a periodic announcement of the virtual net, which + we do once a minute if the router is not bound. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD + IF IT IS A REQUEST AND THE NETWORK IS NOT 0xffffffff. + +Arguments: + + Network - The network to discover. + + Operation - One of RIP_REQUEST, RIP_RESPONSE, or RIP_DOWN. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + PLIST_ENTRY p; + PRIP_PACKET RipPacket; + TDI_ADDRESS_IPX RemoteAddress; + TDI_ADDRESS_IPX LocalAddress; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PNDIS_BUFFER pNdisIpxBuff; + + + // + // Make sure we only queue a request for net 0xffffffff if we + // are auto-detecting, because we assume that in other places. + // + + if ((Network == 0xffffffff) && + (Device->AutoDetectState != AUTO_DETECT_STATE_RUNNING)) { + + return STATUS_BAD_NETWORK_PATH; + + } + + // + // Try to get a packet to use for the RIP request. We + // allocate this now, but check if it succeeded later, + // to make the locking work better (we need to keep + // the lock between when we check for an existing + // request on this network and when we queue this + // request). + // + + s = IpxPopSendPacket (Device); + + // + // There was no router table entry for this network, first see + // if there is already a pending request for this route. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Operation == RIP_REQUEST) { + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + // + // Skip responses. + // + + if (Reserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (Reserved->u.SR_RIP.Network == Network && + !Reserved->u.SR_RIP.RouteFound) { + + // + // There is already one pending, put back the packet if + // we got one (we hold the lock already). + // + + if (s != NULL) { + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, s, &Device->SListsLock); + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + } + } + + } + + + if (s == NULL) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_INTERNAL; + Reserved->SendInProgress = FALSE; + Reserved->DestinationType = DESTINATION_BCAST; + Reserved->u.SR_RIP.CurrentNicId = 0; + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + switch (Operation) { + case RIP_REQUEST: Reserved->u.SR_RIP.RetryCount = 0; break; + case RIP_RESPONSE: Reserved->u.SR_RIP.RetryCount = 0xfe; break; + case RIP_DOWN: Reserved->u.SR_RIP.RetryCount = 0xff; break; + } + Reserved->u.SR_RIP.RouteFound = FALSE; + Reserved->u.SR_RIP.Network = Network; + Reserved->u.SR_RIP.SendTime = Device->RipSendTime; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // Fill in the IPX header at the standard offset (for sending + // to actual bindings it will be moved around if needed). We + // have to construct the local and remote addresses so they + // are in the format that IpxConstructHeader expects. + // + + RemoteAddress.NetworkAddress = Network; + RtlCopyMemory (RemoteAddress.NodeAddress, BroadcastAddress, 6); + RemoteAddress.Socket = RIP_SOCKET; + + RtlCopyMemory (&LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + LocalAddress.Socket = RIP_SOCKET; + + IpxConstructHeader( +// &Reserved->Header[Device->IncludedHeaderOffset], + &Reserved->Header[MAC_HEADER_SIZE], + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + &RemoteAddress, + &LocalAddress); + + // + // Fill in the RIP request also. + // + +#if 0 + RipPacket = (PRIP_PACKET)(&Reserved->Header[Device->IncludedHeaderOffset + sizeof(IPX_HEADER)]); +#endif + RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + RipPacket->Operation = Operation & 0x7fff; + RipPacket->NetworkEntry.NetworkNumber = Network; + + if (Operation == RIP_REQUEST) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(0xffff); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(0xffff); + } else if (Operation == RIP_RESPONSE) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(2); // will be modified when sent + } else { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(16); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(16); + } + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now insert this packet in the queue of pending RIP + // requests and start the timer if needed (this is done + // to ensure the RIP_GRANULARITY milliseconds inter-RIP-packet + // delay). + // + + IPX_DEBUG (RIP, ("RIP %s for network %lx\n", + (Operation == RIP_REQUEST) ? "request" : ((Operation == RIP_RESPONSE) ? "announce" : "down"), + REORDER_ULONG(Network))); + + InsertHeadList( + &Device->WaitingRipPackets, + &Reserved->WaitLinkage); + + ++Device->RipPacketCount; + + if (!Device->RipShortTimerActive) { + + Device->RipShortTimerActive = TRUE; + IpxReferenceDevice (Device, DREF_RIP_TIMER); + + CTEStartTimer( + &Device->RipShortTimer, + 1, // 1 ms, i.e. expire immediately + RipShortTimeout, + (PVOID)Device); + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + +} /* RipQueueRequest */ + + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ) + +/*++ + +Routine Description: + + This routine sends a respond to a RIP request from a client -- + this is only used if we have a virtual network and the router + is not bound, and somebody queries on the virtual network. + +Arguments: + + Binding - The binding on which the request was received. + + RemoteAddress - The IPX source address of the request. + + LocalTarget - The local target of the received packet. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PIPX_SEND_RESERVED Reserved; + TDI_ADDRESS_IPX LocalAddress; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + PRIP_PACKET RipPacket; + PDEVICE Device = IpxDevice; + PBINDING MasterBinding; + NDIS_STATUS NdisStatus; + USHORT TickCount; + PNDIS_BUFFER pNdisIpxBuff; + + // + // Get a packet to use for the RIP response. + // + + s = IpxPopSendPacket (Device); + + if (s == NULL) { + return; + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_RESPONSE; + Reserved->DestinationType = DESTINATION_DEF; + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // If this binding is a binding set member, round-robin through + // the various bindings when responding. We will get some natural + // round-robinning because broadcast requests are received on + // binding set members in turn, but they are only rotated once + // a second. + // + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + + // + // Fill in the IPX header at the correct offset. + // + + LocalAddress.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (LocalAddress.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + LocalAddress.Socket = RIP_SOCKET; +#if 0 + IpxHeader = (PIPX_HEADER)(&Reserved->Header[Binding->DefHeaderSize]); +#endif + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + IpxConstructHeader( + (PUCHAR)IpxHeader, + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + RemoteAddress, + &LocalAddress); + + // + // In case the request comes from net 0, fill that in too. + // + + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + + + // + // Fill in the RIP request. + // + + RipPacket = (PRIP_PACKET)(IpxHeader+1); + + RipPacket->Operation = RIP_RESPONSE; + RipPacket->NetworkEntry.NetworkNumber = Device->VirtualNetworkNumber; + + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + TickCount = (USHORT)(((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + IPX_DEBUG (RIP, ("RIP response for virtual network %lx\n", + REORDER_ULONG(Device->VirtualNetworkNumber))); + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + +#ifdef _PNP_POWER + if (Binding->BindingSetMember) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + return; + +} /* RipSendResponse */ + + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP short timer expires. + It is called every RIP_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p; + PIPX_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + USHORT OldNicId, NewNicId; + ULONG OldOffset, NewOffset; + PIPX_HEADER IpxHeader; + PBINDING Binding, MasterBinding; + NDIS_STATUS NdisStatus; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + +#ifdef _PNP_LATER + static IPX_LOCAL_TARGET BroadcastTarget = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, {0, 0, 0} }; +#else + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif + + static ULONG ZeroNetwork = 0; +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->RipSendTime; + + if (Device->RipPacketCount == 0) { + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // + + while (TRUE) { + + p = Device->WaitingRipPackets.Flink; + if (p == &Device->WaitingRipPackets) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if ((Reserved->u.SR_RIP.RouteFound) && (!Reserved->SendInProgress)) { + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + } + + if ((((SHORT)(Device->RipSendTime - Reserved->u.SR_RIP.SendTime)) < 0) || + Reserved->SendInProgress) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + + // + // Find the right binding to send to. If NoIdAdvance + // is set, then the binding doesn't need to be changed + // this time (this means we wrapped last time). + // + + OldNicId = Reserved->u.SR_RIP.CurrentNicId; + + if (!Reserved->u.SR_RIP.NoIdAdvance) { + + BOOLEAN FoundNext = FALSE; + +#ifdef _PNP_POWER +// +// To maintain the lock order, release Device lock here and re-acquire later +// + USHORT StartId; + + if (Device->ValidBindings == 0) { + IPX_DEBUG(PNP, ("ValidBindings 0 in RipShortTimeOut\n")); + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + return; + } + + StartId = (USHORT)((OldNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); + + NewNicId = StartId; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#else + + USHORT StartId = (USHORT)((OldNicId % Device->BindingCount) + 1); + + NewNicId = StartId; +#endif + do { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NewNicId); +#else + Binding = Device->Bindings[NewNicId]; +#endif + if (Reserved->u.SR_RIP.Network != 0xffffffff) { + + // + // We are looking for a real net; check that + // the next binding is valid. If it is a WAN + // binding, we don't send queries if the router + // is bound. If it is a LAN binding, we don't + // send queries if we are configured for + // SingleNetworkActive and the WAN is up. + // We also don't send queries on binding set + // members which aren't masters. + // + + if ((Binding != NULL) + && + ((!Binding->Adapter->MacInfo.MediumAsync) || + (!Device->UpperDriverBound[IDENTIFIER_RIP])) + && + ((Binding->Adapter->MacInfo.MediumAsync) || + (!Device->SingleNetworkActive) || + (!Device->ActiveNetworkWan)) + && + ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding))) { + + FoundNext = TRUE; + break; + } + + } else { + + // + // We are sending out the initial request to net + // 0xffffffff, to generate traffic so we can figure + // out our real network number. We don't do this + // to nets that already have a number and we don't + // do it on WAN links. We also don't do it on + // auto-detect nets if we have found the default. + // + + + if ((Binding != NULL) && + (Binding->TentativeNetworkAddress == 0) && + (!Binding->Adapter->MacInfo.MediumAsync) && + (!Binding->AutoDetect || !Binding->Adapter->DefaultAutoDetected)) { + FoundNext = TRUE; + break; + } + } +#ifdef _PNP_POWER + // + // [BUGBUGZZ] Why cycle thru the entire list? + // + NewNicId = (USHORT)((NewNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); +#else + NewNicId = (USHORT)((NewNicId % Device->BindingCount) + 1); +#endif + } while (NewNicId != StartId); + + if (!FoundNext) { + + // + // Nothing more needs to be done with this packet, + // leave it off the queue and since we didn't send + // a packet we can check for more. + // +#ifndef _PNP_POWER + // + // This was released above (before the BindAccessLock was taken + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + RipCleanupPacket(Device, Reserved); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + + } + +#ifdef _PNP_POWER + + IPX_DEBUG(RIP, ("RIP: FoundNext: %lx, StartId: %lx, OldNicId: %lx, NewNicId: %lx\n", FoundNext, StartId, OldNicId, NewNicId)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Re-acquire the Device lock + // + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + + Reserved->u.SR_RIP.CurrentNicId = NewNicId; + + // + // Move the data around if needed. + // + +#if 0 + if (OldNicId != NewNicId) { + + if (OldNicId == 0) { + OldOffset = Device->IncludedHeaderOffset; + } else { + OldOffset = Device->Bindings[OldNicId]->BcMcHeaderSize; + } + + NewOffset = Binding->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + + } + + } +#endif + + if (NewNicId <= OldNicId) { + + // + // We found a new binding but we wrapped, so increment + // the counter. If we have done all the resends, or + // this is a response (indicated by retry count of 0xff; + // they are only sent once) then clean up. + // + + if ((Reserved->u.SR_RIP.RetryCount >= 0xfe) || + ((++Reserved->u.SR_RIP.RetryCount) == Device->RipCount)) { + + // + // This packet is stale, clean it up and continue. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + RipCleanupPacket(Device, Reserved); + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + // + // We wrapped, so put ourselves back in the queue + // at the end. + // + + Reserved->u.SR_RIP.SendTime = (USHORT)(Device->RipSendTime + Device->RipTimeout - 1); + Reserved->u.SR_RIP.NoIdAdvance = TRUE; + InsertTailList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + +#ifdef _PNP_POWER + // + // Free the Device lock before deref'ing the Binding so we maintain + // the lock order: BindingAccess > GlobalInterLock > Device + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + } + + continue; + + } +#ifdef _PNP_POWER +// +// To prevent the re-acquire of the device lock, this is moved up... +// + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + } else { + + // + // Next time we need to advance the binding. + // + + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + NewNicId = OldNicId; +#ifdef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NewNicId); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[NewNicId]; +#endif + + } +#ifndef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + // + // This packet should be sent on binding NewNicId; first + // move the data to the right location for the current + // binding. + // +#ifdef _PNP_POWER + CTEAssert (Binding == NIC_ID_TO_BINDING(Device, NewNicId)); // temp, just to make sure +#else + CTEAssert (Binding == Device->Bindings[NewNicId]); // temp, just to make sure +#endif +// NewOffset = Binding->BcMcHeaderSize; + + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&BroadcastTarget, NewNicId); +#else + BroadcastTarget.NicId = NewNicId; +#endif + + // + // Modify the header so the packet comes from this + // specific adapter, not the virtual network. + // + + // IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + if (Reserved->u.SR_RIP.Network == 0xffffffff) { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = 0; + } else { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + } + + if (Reserved->u.SR_RIP.RetryCount < 0xfe) { + + // + // This is an outgoing query. We round-robin these through + // binding sets. + // + + if (Binding->BindingSetMember) { + + // + // Shouldn't have any binding sets during initial + // discovery. + // + + CTEAssert (Reserved->u.SR_RIP.Network != 0xffffffff); + + // + // If we are in a binding set, then use the current binding + // in the set for this send, and advance the current binding. + // The places we have used Binding before here will be fine + // since the binding set members all have the same media + // and frame type. + // + + CTEAssert (Binding->CurrentSendBinding); // should be a master. + MasterBinding = Binding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(MasterBinding, BREF_DEVICE_ACCESS); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + } + + + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + // + // Bug# 6485 + // Rip request, general or specific, is putting the network of the + // node to which the route has to be found in the ipx header remote + // network field. Some novell routers don't like that. This network + // field should be 0. + // + { + PRIP_PACKET RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + + if (RipPacket->Operation != RIP_REQUEST) { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + } else { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = 0; + } + } + + // + // If this is a RIP_RESPONSE, set the tick count for this + // binding. + // + + if (Reserved->u.SR_RIP.RetryCount == 0xfe) { + + PRIP_PACKET RipPacket = (PRIP_PACKET)(IpxHeader+1); + USHORT TickCount = (USHORT) + (((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + } + + if ((NdisStatus = IpxSendFrame( + &BroadcastTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + + break; + + } + + CTEStartTimer( + &Device->RipShortTimer, + RIP_GRANULARITY, + RipShortTimeout, + (PVOID)Device); + +} /* RipShortTimeout */ + + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP long timer expires. + It is called every minute and handles periodic re-RIPping + to ensure that entries are accurate, as well as aging out + of entries if the rip router is not bound. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + UINT i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // [FW] TRUE if there are no more entries to age out. + // + BOOLEAN fMoreToAge=FALSE; + + // + // Rotate the broadcast receiver on all binding sets. + // We can loop up to HighestExternal only since we + // are only interested in finding binding set masters. + // +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != NULL) && + (Binding->CurrentSendBinding)) { + + // + // It is a master, so find the current broadcast + // receiver, then advance it. + // + + while (TRUE) { + if (Binding->ReceiveBroadcast) { + Binding->ReceiveBroadcast = FALSE; + Binding->NextBinding->ReceiveBroadcast = TRUE; + break; + } else { + Binding = Binding->NextBinding; + } + } + } + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + + // + // If RIP is bound, we don't do any of this, and + // we stop the timer from running. + // + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + // + // [FW] For the case when the Forwarder appears after our table has + // been primed, we need to age out these entries.... + // + if (Device->ForwarderBound) { + goto ageout; + } + + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + return; + } + + + // + // If we have a virtual net, do our periodic broadcast. + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Device->TempDatagramBytesSent); + Device->Statistics.DatagramsSent += Device->TempDatagramsSent; + + Device->TempDatagramBytesSent = 0; + Device->TempDatagramsSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + Device->TempDatagramBytesReceived); + Device->Statistics.DatagramsReceived += Device->TempDatagramsReceived; + + Device->TempDatagramBytesReceived = 0; + Device->TempDatagramsReceived = 0; + + + // + // We need to scan each hash bucket to see if there + // are any active entries which need to be re-RIPped + // for. We also scan for entries that should be timed + // out. + // + +ageout: + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + RouterSegment = &IpxDevice->Segments[Segment]; + + // + // Don't take the lock if the bucket is empty. + // + + if (RouterSegment->Entries.Flink == &RouterSegment->Entries) { + continue; + } + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry looking for ones to age. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) { + continue; + } + + // + // [FW] There are more entries to age + // + fMoreToAge = TRUE; + + ++RouteEntry->Timer; + if (RouteEntry->Timer >= Device->RipUsageTime) { + + RipDeleteRoute (Segment, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + continue; + + } + + // + // See if we should re-RIP for this segment. It has + // to have been around for RipAgeTime, and we also + // make sure that the Timer is not too high to + // prevent us from re-RIPping on unused routes. + // + + ++RouteEntry->PRIVATE.Reserved[0]; + + if ((RouteEntry->PRIVATE.Reserved[0] >= Device->RipAgeTime) && + (RouteEntry->Timer <= Device->RipAgeTime) && + !Device->ForwarderBound) { + + // + // If we successfully queue a request, then reset + // Reserved[0] so we don't re-RIP for a while. + // + + if (RipQueueRequest (*(UNALIGNED ULONG *)RouteEntry->Network, RIP_REQUEST) == STATUS_PENDING) { + RouteEntry->PRIVATE.Reserved[0] = 0; + } + } + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + } + + + // + // [FW] If RIP installed, restart the timer only if there was at least + // one entry which could be aged. + // + + if (Device->ForwarderBound) { + + if (fMoreToAge) { + + IPX_DEBUG(RIP, ("More entries to age - restarting long timer\n")); + CTEStartTimer( + &Device->RipLongTimer, + 60000, // one minute timeout + RipLongTimeout, + (PVOID)Device); + + } else { + + // + // Else, dont restart the timer and deref the device + // + + IPX_DEBUG(RIP, ("No more entries to age - derefing the device\n")); + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + } + } else { + // + // Now restart the timer for the next timeout. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + CTEStartTimer( + &Device->RipLongTimer, + 60000, // one minute timeout + RipLongTimeout, + (PVOID)Device); + + } else { + + // + // Send a DOWN packet if needed, then stop ourselves. + // + + if (Device->RipResponder) { + + if (RipQueueRequest (Device->VirtualNetworkNumber, RIP_DOWN) != STATUS_PENDING) { + + // + // We need to kick this event because the packet completion + // won't. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + } + + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + } + } + +} /* RipLongTimeout */ + + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ) + +/*++ + +Routine Description: + + This routine cleans up when a RIP packet times out. + +Arguments: + + Device - The device. + + RipReserved - The ProtocolReserved section of the RIP packet. + +Return Value: + + None. + +--*/ + +{ + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + if (RipReserved->u.SR_RIP.RetryCount < 0xfe) { + + if (RipReserved->u.SR_RIP.Network != 0xffffffff) { + + IPX_DEBUG (RIP, ("Timing out RIP for network %lx\n", + REORDER_ULONG(RipReserved->u.SR_RIP.Network))); + + Segment = RipGetSegment ((PUCHAR)&RipReserved->u.SR_RIP.Network); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipReserved->u.SR_RIP.Network), + LockHandle, + FALSE, + NULL, + 0, + 0); + + } else { + + // + // This was the initial query looking for networks -- + // signal the init thread which is waiting. + // + + IPX_DEBUG (AUTO_DETECT, ("Signalling auto-detect event\n")); + KeSetEvent( + &Device->AutoDetectEvent, + 0L, + FALSE); + + } + + } else if (RipReserved->u.SR_RIP.RetryCount == 0xff) { + + // + // This is a DOWN message, set the device event that + // is waiting for it to complete. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + + // + // Put the RIP packet back in the pool. + // + + RipReserved->Identifier = IDENTIFIER_IPX; + +} /* RipCleanupPacket */ + + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ) + +/*++ + +Routine Description: + + This routine processes a RIP response from the specified + local target, indicating a route to the network in the RIP + header. + +Arguments: + + Device - The device. + + LocalTarget - The router that the frame was received from. + + RipPacket - The RIP response header. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED RipReserved; // ProtocolReserved of RIP packet + ULONG Segment; + PIPX_ROUTE_ENTRY RouteEntry, OldRouteEntry; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + // + // Since we have received a RIP response for this network. + // kill the waiting RIP packets for it if it exists. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + RipReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if (RipReserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (RipReserved->u.SR_RIP.Network == + RipPacket->NetworkEntry.NetworkNumber) { + break; + } + + } + + if (p == &Device->WaitingRipPackets) { + + // + // No packets pending on this, return. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Put the RIP packet back in the pool. + // + + IPX_DEBUG (RIP, ("Got RIP response for network %lx\n", + REORDER_ULONG(RipPacket->NetworkEntry.NetworkNumber))); + + RipReserved->u.SR_RIP.RouteFound = TRUE; + if (!RipReserved->SendInProgress) { + + // + // If the send is done destroy it now, otherwise + // when it pops up in RipShortTimeout it will get + // destroyed because RouteFound is TRUE. + // + + RemoveEntryList (p); + RipReserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &RipReserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + + + // + // Try to allocate and add a router segment unless the + // RIP router is active...if we don't that is fine, we'll + // just re-RIP later. + // + + Segment = RipGetSegment ((PUCHAR)&RipPacket->NetworkEntry.NetworkNumber); + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + *(UNALIGNED LONG *)RouteEntry->Network = RipPacket->NetworkEntry.NetworkNumber; +#ifdef _PNP_POWER + RouteEntry->NicId = NIC_FROM_LOCAL_TARGET(LocalTarget); + RouteEntry->NdisBindingContext = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)->Adapter->NdisBindingHandle; + // BUGBUG: What if this is NULL?? +#else + RouteEntry->NicId = LocalTarget->NicId; + RouteEntry->NdisBindingContext = Device->Bindings[LocalTarget->NicId]->Adapter->NdisBindingHandle; // BUGBUG: What if this is NULL?? +#endif + RouteEntry->Flags = 0; + RouteEntry->Timer = 0; + RouteEntry->PRIVATE.Reserved[0] = 0; + RouteEntry->Segment = Segment; + RouteEntry->HopCount = REORDER_USHORT(RipPacket->NetworkEntry.HopCount); + RouteEntry->TickCount = REORDER_USHORT(RipPacket->NetworkEntry.TickCount); + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + RtlCopyMemory (RouteEntry->NextRouter, LocalTarget->MacAddress, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Replace any existing routes. This is OK because once + // we get the first response to a RIP packet on a given + // route, we will take the packet out of the queue and + // ignore further responses. We will only get a bad route + // if we do two requests really quickly and there + // are two routes, and the second response to the first + // request is picked up as the first response to the second + // request. + // + + if ((OldRouteEntry = RipGetRoute (Segment, (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber))) != NULL) { + + // + // These are saved so timeouts etc. happen right. + // + + RouteEntry->Flags = OldRouteEntry->Flags; + RouteEntry->Timer = OldRouteEntry->Timer; + + RipDeleteRoute (Segment, OldRouteEntry); + IpxFreeMemory(OldRouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } + + RipAddRoute (Segment, RouteEntry); + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + // + // Complete all datagrams etc. that were waiting + // for this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber), + LockHandle, + TRUE, + LocalTarget, + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.HopCount)), + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.TickCount)) + ); + +} /* RipProcessResponse */ + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams, find route + requests, and GET_LOCAL_TARGET ioctls that were + waiting for a route to be found. + + THIS ROUTINE IS CALLED WITH THE SEGMENT LOCK HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + Network - The network in question. + + LockHandle - The handle used to acquire the lock. + + Success - TRUE if the route was successfully found. + + LocalTarget - If Success is TRUE, the local target for the route. + + HopCount - If Success is TRUE, the hop count for the route, + in machine order. + + TickCount - If Success is TRUE, the tick count for the route, + in machine order. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY FindRouteList; + LIST_ENTRY GetLocalTargetList; + LIST_ENTRY ReripNetnumList; + PIPX_SEND_RESERVED WaitReserved; // ProtocolReserved of waiting packet + PIPX_FIND_ROUTE_REQUEST FindRouteRequest; + PREQUEST GetLocalTargetRequest; + PREQUEST ReripNetnumRequest; + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PIPX_NETNUM_DATA NetnumData; + ULONG Segment; + PBINDING Binding, SendBinding; + PLIST_ENTRY p; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + ULONG HeaderSize; + NDIS_STATUS NdisStatus; + ULONG NetworkUlong = *(UNALIGNED ULONG *)Network; + + + InitializeListHead (&DatagramList); + InitializeListHead (&FindRouteList); + InitializeListHead (&GetLocalTargetList); + InitializeListHead (&ReripNetnumList); + + + // + // Put all packets that were waiting for a route to + // this network on DatagramList. They will be sent + // or failed later in the routine. + // + + Segment = RipGetSegment (Network); + + p = Device->Segments[Segment].WaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].WaitingForRoute) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; +#if 0 + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[Device->IncludedHeaderOffset]))->DestinationNetwork) == + NetworkUlong) { +#endif + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[MAC_HEADER_SIZE]))->DestinationNetwork) == + NetworkUlong) { + + RemoveEntryList (&WaitReserved->WaitLinkage); + InsertTailList (&DatagramList, &WaitReserved->WaitLinkage); + } + + } + + // + // Put all find route requests for this network on + // FindRouteList. They will be completed later in the + // routine. + // + + p = Device->Segments[Segment].FindWaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].FindWaitingForRoute) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + if (*(UNALIGNED ULONG *)(FindRouteRequest->Network) == + NetworkUlong) { + + RemoveEntryList (&FindRouteRequest->Linkage); + InsertTailList (&FindRouteList, &FindRouteRequest->Linkage); + } + + } + + // + // Put all get local target action requests for this + // network on GetLocalTargetList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingLocalTarget.Flink; + + while (p != &Device->Segments[Segment].WaitingLocalTarget) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + if (GetLocalTarget->IpxAddress.NetworkAddress == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(GetLocalTargetRequest)); + InsertTailList (&GetLocalTargetList, REQUEST_LINKAGE(GetLocalTargetRequest)); + } + + } + + // + // Put all MIPX_RERIPNETNUM action requests for this + // network on ReripNetnumList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingReripNetnum.Flink; + + while (p != &Device->Segments[Segment].WaitingReripNetnum) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + if (*(UNALIGNED ULONG *)NetnumData->netnum == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(ReripNetnumRequest)); + InsertTailList (&ReripNetnumList, REQUEST_LINKAGE(ReripNetnumRequest)); + } + + } + + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + // + // For sends we will use the master binding of a binding + // set, but we'll return the real NicId for people who + // want that. + // + + if (Success) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, SendBinding->NicId)); + } else { + SendBinding = Binding; + } +#else + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + LocalTarget->NicId = SendBinding->NicId; + } else { + SendBinding = Binding; + } +#endif + } + + + // + // Now that the lock is free, process all packets on + // DatagramList. + // + // NOTE: May misorder packets if they come in right now... + // + + for (p = DatagramList.Flink; p != &DatagramList ; ) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; + Packet = CONTAINING_RECORD (WaitReserved, NDIS_PACKET, ProtocolReserved[0]); + +#if DBG + CTEAssert (!WaitReserved->SendInProgress); + WaitReserved->SendInProgress = TRUE; +#endif + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued packet %lx\n", WaitReserved)); + + if (REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) > + SendBinding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Queued send %d bytes too large (%d)\n", + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request), + SendBinding->RealMaxDatagramSize)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_INVALID_BUFFER_SIZE); + + } else { + +#if 0 + if (WaitReserved->DestinationType == DESTINATION_DEF) { + HeaderSize = SendBinding->DefHeaderSize; + } else { + HeaderSize = SendBinding->BcMcHeaderSize; + } + + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[HeaderSize]); +#endif + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[MAC_HEADER_SIZE]); + + // + // Move the header to the correct location now that + // we know the NIC ID to send to. + // +#if 0 + if (HeaderSize != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + IpxHeader, + &WaitReserved->Header[Device->IncludedHeaderOffset], + sizeof(IPX_HEADER)); + + } +#endif + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // These frames need to look like they come from the + // local network, not the virtual one. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = SendBinding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, SendBinding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)SendBinding->Adapter, + Packet, + NdisStatus); + } + + } + + } else { + + IPX_DEBUG (RIP, ("Timing out packet %lx\n", WaitReserved)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_BAD_NETWORK_PATH); + + } + + } + + + // + // Since we round-robin outgoing rip packets, we just use the + // real NicId here for find route and get local target requests. + // We changed LocalTarget->NicId to be the master above. + // + + if (Success) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + } + + for (p = FindRouteList.Flink; p != &FindRouteList ; ) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + + if (Success) { + + PUSHORT Counts; + + IPX_DEBUG (RIP, ("Found queued find route %lx\n", FindRouteRequest)); + FindRouteRequest->LocalTarget = *LocalTarget; + + Counts = (PUSHORT)&FindRouteRequest->Reserved2; + Counts[0] = TickCount; + Counts[1] = HopCount; + + } else { + + IPX_DEBUG (RIP, ("Timing out find route %lx\n", FindRouteRequest)); + + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + Success); + + } + + for (p = GetLocalTargetList.Flink; p != &GetLocalTargetList ; ) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + GetLocalTarget->LocalTarget = *LocalTarget; + REQUEST_INFORMATION(GetLocalTargetRequest) = sizeof(ISN_ACTION_GET_LOCAL_TARGET); + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + REQUEST_INFORMATION(GetLocalTargetRequest) = 0; + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(GetLocalTargetRequest); + IpxFreeRequest(Device, GetLocalTargetRequest); + + } + + // + // NOTE: LocalTarget->NicId now points to the real binding + // not the master, so we use SendBinding->NicId below. + // + + for (p = ReripNetnumList.Flink; p != &ReripNetnumList ; ) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + NetnumData->hopcount = HopCount; + NetnumData->netdelay = TickCount; + NetnumData->cardnum = (INT)(MIN( Device->MaxBindings, SendBinding->NicId) - 1); + RtlMoveMemory (NetnumData->router, LocalTarget->MacAddress, 6); + + REQUEST_INFORMATION(ReripNetnumRequest) = + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_NETNUM_DATA); + REQUEST_STATUS(ReripNetnumRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + REQUEST_INFORMATION(ReripNetnumRequest) = 0; + REQUEST_STATUS(ReripNetnumRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(ReripNetnumRequest); + IpxFreeRequest(Device, ReripNetnumRequest); + + } + +} /* RipHandleRoutePending */ + + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ) + +/*++ + +Routine Description: + + This routine creates a router entry for a local network + and inserts it in the table. + +Arguments: + + Network - The network. + + NicId - The NIC ID used to route packets + + NdisBindingHandle - The binding handle used for NdisSend + + Count - The tick and hop count for this network (will be + 0 for the virtual net and 1 for attached nets) + +Return Value: + + The status of the operation. + +--*/ + +{ + PIPX_ROUTE_ENTRY RouteEntry; + PDEVICE Device = IpxDevice; + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // BUGBUG: We should allocate the memory in the binding/device + // structure itself. + // + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry == (PIPX_ROUTE_ENTRY)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Segment = RipGetSegment ((PUCHAR)&Network); + + *(UNALIGNED LONG *)RouteEntry->Network = Network; + RouteEntry->NicId = NicId; + RouteEntry->NdisBindingContext = NdisBindingContext; + + if (NicId == 0) { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY; + } else { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY | IPX_ROUTER_LOCAL_NET; + } + RouteEntry->Segment = Segment; + RouteEntry->TickCount = Count; + RouteEntry->HopCount = 1; + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + + // + // RouteEntry->NextRouter is not used for the virtual net or + // when LOCAL_NET is set (i.e. every net that we will add here). + // + + RtlZeroMemory (RouteEntry->NextRouter, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Make sure one doesn't exist. + // + + if (RipGetRoute(Segment, (PUCHAR)&Network) != NULL) { + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_DUPLICATE_NAME; + } + + // + // Add this new entry. + // + + if (RipAddRoute (Segment, RouteEntry)) { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + return STATUS_SUCCESS; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_INSUFFICIENT_RESOURCES; + + } + +} /* RipInsertLocalNetwork */ + + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ) + +/*++ + +Routine Description: + + This routine is called when an auto-detect binding is + deleted or moved, or a WAN line goes down. + + It scans the RIP database for routes equal to this NIC ID + and modifies them appropriately. If ChangeType is + IpxBindingDeleted it will subract one from any NIC IDs + in the database that are higher than NicId. It is assumed + that other code is readjusting the Device->Bindings + array. + +Arguments: + + NicId - The NIC ID of the deleted binding. + + NewNicId - The new NIC ID, for IpxBindingMoved changes. + + ChangeType - Either IpxBindingDeleted, IpxBindingMoved, + or IpxBindingDown. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry comparing the NIC ID. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->NicId == NicId) { + + if (ChangeType != IpxBindingMoved) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, binding deleted\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Changing NIC ID for route entry %lx\n", RouteEntry)); + RouteEntry->NicId = NewNicId; + + } +#ifdef _PNP_POWER + // + // If the NicId is 0, we dont adjust the other entries' NicId's - this is to support the removal + // of the Virtual Net # which resides at NicId=0. + // + } else if (NicId && (ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#else + } else if ((ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#endif + IPX_DEBUG (AUTO_DETECT, ("Decrementing NIC ID for route entry %lx\n", RouteEntry)); + --RouteEntry->NicId; + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipAdjustForBindingChange */ + + +UINT +RipGetSegment( + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the correct segment for the specified + network. + +Arguments: + + Network - The network. + +Return Value: + + The segment. + +--*/ + +{ + + ULONG Total; + + Total = Network[0] ^ Network[1] ^ Network[2] ^ Network[3]; + return (Total % IpxDevice->SegmentCount); + +} /* RipGetSegment */ + + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the router table entry for the given + network, which is in the specified segment of the table. + THE SEGMENT LOCK MUST BE HELD. The returned data is valid + until the segment lock is released or other operations + (add/delete) are performed on the segment. + +Arguments: + + Segment - The segment corresponding to the network. + + Network - The network. + +Return Value: + + The router table entry, or NULL if none exists for this network. + +--*/ + +{ + PLIST_ENTRY p; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + + RouterSegment = &IpxDevice->Segments[Segment]; + + for (p = RouterSegment->Entries.Flink; + p != &RouterSegment->Entries; + p = p->Flink) { + + RouteEntry = CONTAINING_RECORD( + p, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + if ((*(UNALIGNED LONG *)RouteEntry->Network) == + (*(UNALIGNED LONG *)Network)) { + return RouteEntry; + } + } + + return NULL; + +} /* RipGetRoute */ + + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine stores a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is allocated and filled in by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully inserted. + +--*/ + +{ + + IPX_DEBUG (RIP, ("Adding route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + InsertTailList( + &IpxDevice->Segments[Segment].Entries, + &RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipAddRoute */ + + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine deletes a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is freed by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully deleted. + +--*/ + +{ + + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + IPX_DEBUG (RIP, ("Deleting route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + + // + // If the current enumeration point for this segment is here, + // adjust the pointer before deleting the entry. We make it + // point to the previous entry so GetNextRoute will work. + // + + if (RouterSegment->EnumerateLocation == &RouteEntry->PRIVATE.Linkage) { + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Blink; + } + + RemoveEntryList (&RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipDeleteRoute */ + + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the first router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetNextRoute to enumerate all the + entries in a segment. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The first router table entry, or NULL if the segment is empty. + +--*/ + +{ + PIPX_ROUTE_ENTRY FirstEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->Entries.Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + FirstEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return FirstEntry; + + } + +} /* RipGetFirstRoute */ + + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the next router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetFirstRoute to enumerate all the + entries in a segment. + + It is illegal to call RipGetNextRoute on a segment + without first calling RipGetFirstRoute. The segment + lock must be held for the duration of the enumeration + of a single segment. It is legal to stop enumerating + the segment in the middle. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The next router table entry, or NULL if the end of the + segment is reached. + +--*/ + +{ + PIPX_ROUTE_ENTRY NextEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + NextEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return NextEntry; + + } + +} /* RipGetNextRoute */ + + +VOID +RipDropRemoteEntries( + VOID + ) + +/*++ + +Routine Description: + + This routine deletes all non-local entries from the + RIP database. It is called when the WAN line goes up + or down and we want to remove all existing entries. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through, deleting everything but local entries. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, dropping remote entries\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipDropRemoteEntries */ + diff --git a/private/ntos/tdi/isn/ipx/rt.c b/private/ntos/tdi/isn/ipx/rt.c new file mode 100644 index 000000000..8881f03fa --- /dev/null +++ b/private/ntos/tdi/isn/ipx/rt.c @@ -0,0 +1,1311 @@ +/*++ + +Copyright (c) 1989-1994 Microsoft Corporation + +Module Name; + + Rt.c + +Abstract; + + +Author; + + +Revision History; + +TODO: Get rid of ref/Deref since the RTINFO structure will not be destroyed + Use a common alloc/free function (with the rest of ipx) + Allocate tagged memory + Optimize code more +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// function prototypes +// + +VOID +RtIrpCancel( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ); + + +PVOID +RtAllocMem( + IN ULONG Size + ); + +VOID +RtFreeMem( + IN PVOID pBuffer, + IN ULONG Size + ); + +NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN PDEVICE pDevice + ); +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength); + +NTSTATUS +CleanupRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp); + +NTSTATUS +CloseRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp); + +NTSTATUS +SendIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ); + +NTSTATUS +RcvIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ); +NTSTATUS +PassDgToRt ( + IN PDEVICE pDevice, + IN PIPX_DATAGRAM_OPTIONS2 pContext, + IN ULONG Index, + IN VOID UNALIGNED *pDgrm, + IN ULONG uNumBytes + ); + +VOID +IpxDerefRt( + PRT_INFO pRt + ); + +VOID +IpxRefRt( + PRT_INFO pRt + ); + +VOID +IpxDestroyRt( + IN PRT_INFO pRt + ); + +#define ALLOC_PRAGMA 1 +#define CTEMakePageable(x, y) alloc_text(x,y) + +#define AllocMem(_BytesToAlloc) IpxAllocateMemory(_BytesToAlloc, MEMORY_PACKET, "RT MEMORY") + +#define FreeMem(_Memory, _BytesAllocated) IpxFreeMemory(_Memory, _BytesAllocated, MEMORY_PACKET, "RT MEMORY") + + +#define IpxVerifyRt(pRt) // \ + // if ((pRt->Type != IPX_RT_SIGNATURE) || (pRt->Size != sizeof(RT_INFO))) { return STATUS_INVALID_ADDRESS; } + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGERT, CloseRtAddress) +#pragma CTEMakePageable(PAGERT, CleanupRtAddress) +#pragma CTEMakePageable(PAGERT, RcvIrpFromRt) +#pragma CTEMakePageable(PAGERT, SendIrpFromRt) +#pragma CTEMakePageable(PAGERT, PassDgToRt) +#pragma CTEMakePageable(PAGERT, RtIrpCancel) +#pragma CTEMakePageable(PAGERT, NTCheckSetCancelRoutine) +#pragma CTEMakePageable(PAGERT, NTIoComplete) +#pragma CTEMakePageable(PAGERT, RtFreeMem) +#pragma CTEMakePageable(PAGERT, RtAllocMem) +#pragma CTEMakePageable(PAGERT, IpxRefRt) +#pragma CTEMakePageable(PAGERT, IpxDerefRt) +#pragma CTEMakePageable(PAGERT, IpxDestroyRt) +#endif +//******************* Pageable Routine Declarations **************** + + +HANDLE IpxRtDiscardableCodeHandle={0}; + +PRT_INFO pRtInfo; //contains info about all rt opened end points + + +NTSTATUS +OpenRtAddress( + IN PDEVICE pDevice, + IN PREQUEST pIrp + ) +{ + PRT_INFO pRt; + CTELockHandle OldIrq; + NTSTATUS status; + ULONG SaveReqCode; + + + IpxPrint0("OpenRtAddress - entered\n"); + + // + // if the RTINFO endpoint structure is not allocated, then allocate it + // and initialize it. But first get the device lock. This gurantees that + // we can not have two irps doing the creation at the same time + // + CTEGetLock(&pDevice->Lock, &OldIrq); + if (!pRtInfo) + { + + pRt = AllocMem(sizeof(RT_INFO)); + + // + // Do this after locking the pagable rtns. + // + // pRtInfo = pRt; //store it in pRtInfo. When irps come down from RM, + // we can compare pRt passed in them with pRtInfo + if (pRt) + { + RtlZeroMemory(pRt,sizeof(RT_INFO)); + IpxPrint1("OpenRtAddress: Initializing CompletedIrps for pRt=(%lx)\n", pRt); + pRt->RcvMemoryMax = RT_MAX_BUFF_MEM; // max. memory we can allocate + pRt->Type = IPX_RT_SIGNATURE; + pRt->Size = sizeof(RT_INFO); + pRt->pDevice = pDevice; + IpxPrint1("OpenRtAddress: pRtInfo=(%lx)\n", pRt); + IpxPrint1("Completed Irp list is (%lx)\n", IsListEmpty(&pRt->CompletedIrps)); + +#if DBG + RtlCopyMemory(pRt->Signature, "RTIF", sizeof("RTIF") - 1); +#endif + InitializeListHead(&pRt->CompletedIrps); + InitializeListHead(&pRt->HolderIrpsList); + } + CTEFreeLock(&pDevice->Lock, OldIrq); + } + else + { + pRt = pRtInfo; + CTEFreeLock(&pDevice->Lock, OldIrq); + IpxPrint1("OpenRtAddress: RTINFO found = (%lx)\n", pRtInfo); + } + + if (pRt) + { + + // Page in the Rt Code, if it hasn't already been paged in. + // + if (!IpxRtDiscardableCodeHandle) + { + IpxRtDiscardableCodeHandle = MmLockPagableCodeSection( CloseRtAddress ); + + pRtInfo = pRt; //store it in pRtInfo. When irps come down from RM, + // we can compare pRt passed in them with pRtInfo + } + + // + // it could fail to lock the pages so check for that + // + if (IpxRtDiscardableCodeHandle) + { + + ULONG i; + status = STATUS_SUCCESS; + + IpxReferenceRt(pRtInfo, RT_CREATE); + + // + // Find an empty slot and mark it open + // + CTEGetLock(&pRt->Lock, &OldIrq); + for (i=0; iAddFl[i].State == RT_EMPTY) + { + break; + } + } + if (i < IPX_RT_MAX_ADDRESSES) + { + pRt->AddFl[i].State = RT_OPEN; + pRt->NoOfAdds++; + InitializeListHead(&pRt->AddFl[i].RcvList); + InitializeListHead(&pRt->AddFl[i].RcvIrpList); + } + else + { + CTEFreeLock(&pRt->Lock, OldIrq); + IpxPrint1("OpenRtAddress; All %d slots used up\n", IPX_RT_MAX_ADDRESSES); + IpxDereferenceRt(pRtInfo, RT_CREATE); + status = STATUS_INSUFFICIENT_RESOURCES; + goto RET; + } + CTEFreeLock(&pRt->Lock, OldIrq); + + // + // Found an empty slot. Initialize all relevant info. and then + // open an address object. + // + SaveReqCode = REQUEST_CODE(pIrp); + REQUEST_CODE(pIrp) = MIPX_RT_CREATE; + status = IpxOpenAddressM(pDevice, pIrp, i); + REQUEST_CODE(pIrp) = SaveReqCode; + + IpxPrint1("After IpxOpenAddressM: Completed Irp list is (%lx)\n", IsListEmpty(&pRtInfo->CompletedIrps)); + if (status != STATUS_SUCCESS) + { + IpxPrint0("OpenRtAddress; Access Denied due to OpenAddress\n"); + IpxDereferenceRt(pRtInfo, RT_CREATE); + CTEGetLock(&pRt->Lock, &OldIrq); + pRt->AddFl[i].State = RT_EMPTY; + pRt->NoOfAdds--; + CTEFreeLock(&pRt->Lock, OldIrq); + } + else + { + CTEGetLock(&pRt->Lock, &OldIrq); + pRt->AddFl[i].AddressFile = REQUEST_OPEN_CONTEXT(pIrp); + CTEFreeLock(&pRt->Lock, OldIrq); + + // + // No need to put pRt since it is global. We stick with the addressfile here. + // + + // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)pRt; + REQUEST_OPEN_TYPE(pIrp) = (PVOID)(ROUTER_ADDRESS_FILE + i); + IpxPrint1("OpenRtAdd: Index = (%d)\n", RT_ADDRESS_INDEX(pIrp)); + } + } + else + { + IpxPrint1("OpenRtAddress; All %d slots used up\n", IPX_RT_MAX_ADDRESSES); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + IpxPrint0("OpenRtCreate; Couldn't allocate a RT_INFO structure\n"); + CTEAssert(FALSE); //should never happen unless system is running + //out of non-paged pool + status = STATUS_INSUFFICIENT_RESOURCES; + + } +RET: + IpxPrint1("OpenRtAddress status prior to return= %X\n",status); + return(status); +} + + +NTSTATUS +CleanupRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp) + +/*++ +Routine Description; + + This Routine handles closing the Rt Object that is used by + by RT to send and receive name service datagrams on port 137. + + +Arguments; + + pIrp - a ptr to an IRP + +Return Value; + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + PRT_INFO pRt; + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + ULONG Index; + PLIST_ENTRY pLE; + PIRP pTmpIrp; + + + + IpxPrint0("CleanupRtAddress - entered\n"); + + // + // if the endpoint structure is allocated, then deallocate it + // + // pRt = REQUEST_OPEN_CONTEXT(pIrp); + pRt = pRtInfo; + + Index = RT_ADDRESS_INDEX(pIrp); + IpxPrint1("CleanupRtAdd: Index = (%d)\n", Index); + + IpxVerifyRt(pRt); + CTEAssert(pRt && (pRt == pRtInfo)); + CTEAssert(Index < IPX_RT_MAX_ADDRESSES); + + do + { + PLIST_ENTRY pRcvEntry; + PRTRCV_BUFFER pRcv; + PRT_IRP pRtAddFl = &pRt->AddFl[Index]; + + CTEAssert(pRtAddFl->State == RT_OPEN); + IpxPrint1("CleanupRtAddress: Got AF handle = (%lx)\n", pRtAddFl); + IpxReferenceRt(pRt, RT_CLEANUP); + status = STATUS_SUCCESS; + + CTEGetLock (&pRt->Lock, &OldIrq); + + // + // prevent any more dgram getting queued up + // + pRtAddFl->State = RT_CLOSING; + CTEFreeLock (&pRt->Lock, OldIrq); + + // + // free any rcv buffers that may be queued up + // + pHead = &pRtAddFl->RcvList; + while (pRcvEntry = ExInterlockedRemoveHeadList(pHead, &pRt->Lock)) + { + pRcv = CONTAINING_RECORD(pRcvEntry,RTRCV_BUFFER,Linkage); + + CTEAssert(pRcv); + IpxPrint1("CleanupRtAddress:Freeing buffer = (%lx)\n", pRcv); + RtFreeMem(pRcv,pRcv->TotalAllocSize); + } + + // + // Complete all irps that are queued + // + while (pLE = ExInterlockedRemoveHeadList(&pRtAddFl->RcvIrpList, &pRt->Lock)) { + + // + // The recv irp is here so copy the data to its buffer and + // pass it up to RT + // + pTmpIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); + IpxPrint1("CleanupRtAddress: Completing Rt rcv Irp from AdFl queue pIrp=%X\n" ,pTmpIrp); + pTmpIrp->IoStatus.Information = 0; + pTmpIrp->IoStatus.Status = STATUS_CANCELLED; + + NTIoComplete(pTmpIrp, (NTSTATUS)-1, (ULONG)-1); + + } //end of while + + // + // dequeue and complete any irps on the complete queue. + // + + while (pLE = ExInterlockedRemoveHeadList(&pRt->CompletedIrps, &pRt->Lock)) + { + pTmpIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); + if (RT_ADDRESS_INDEX(pTmpIrp) == Index) + { + IpxPrint1("CleanupRtAddress:Completing Rt rcv Irp from CompleteIrps queue pIrp=%X\n" ,pTmpIrp); + + pTmpIrp->IoStatus.Information = 0; + pTmpIrp->IoStatus.Status = STATUS_CANCELLED; + NTIoComplete(pTmpIrp, (NTSTATUS)-1, (ULONG)-1); + } + else + { + ExInterlockedInsertHeadList(&pRt->HolderIrpsList, pLE, &pRt->Lock); + } + } + CTEGetLock(&pRt->Lock, &OldIrq); + while(!IsListEmpty(&pRt->HolderIrpsList)) + { + pLE = RemoveHeadList(&pRt->HolderIrpsList); + InsertHeadList(&pRt->CompletedIrps, pLE); + } + CTEFreeLock(&pRt->Lock, OldIrq); + + // + // Store AF pointer in Irp since we will now be freeing the address file + // (in driver.c). + // + + // + // We always have addressfile in the Irp + // + + // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRtAddFl->AddressFile); + + IpxDereferenceRt(pRt, RT_CLEANUP); + } while (FALSE); + + IpxPrint0("CleanupRtAddress: Return\n"); + return(status); +} + +NTSTATUS +CloseRtAddress( + IN PDEVICE pDevice, + IN PIRP pIrp) +{ + + NTSTATUS status; + PRT_INFO pRt; + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + ULONG Index; + + IpxPrint0("CloseRtAddress - entered\n"); + + // pRt = REQUEST_OPEN_CONTEXT(pIrp); + pRt = pRtInfo; + + Index = RT_ADDRESS_INDEX(pIrp); + IpxPrint1("CloseRtAdd: Index = (%d)\n", Index); + + IpxVerifyRt(pRt); + CTEAssert(pRt && (pRt == pRtInfo)); + CTEAssert(Index < IPX_RT_MAX_ADDRESSES); + CTEAssert(pRt->AddFl[Index].State == RT_CLOSING); + + // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRt->AddFl[Index].AddressFile); + //REQUEST_OPEN_TYPE(pIrp) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + CTEGetLock(&pRt->Lock, &OldIrq); + pRt->AddFl[Index].State = RT_EMPTY; + pRt->NoOfAdds--; + CTEFreeLock(&pRt->Lock, OldIrq); + + // + // THis is a counter to the RT_CREATE + // + IpxDereferenceRt(pRt, RT_CLOSE); + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- + NTSTATUS +SendIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ) +{ + CTELockHandle OldIrq; + NTSTATUS Status; + ULONG Index; + PRT_INFO pRt; + + IpxPrint0("SendIrpfromRt - entered\n"); + // pRt = REQUEST_OPEN_CONTEXT(pIrp); + pRt = pRtInfo; + + Index = RT_ADDRESS_INDEX(pIrp); + IpxVerifyRt(pRt); + CTEAssert(pRt && (pRt == pRtInfo)); + do { + // + // Check if the add. file slot indicates that it is OPEN. If it is + // not open, then we should return STATUS_INVALID_HANDLE. The + // reason why it may not be open is if we got a cleanup/close before + // this irp. + // + CTEGetLock(&pRt->Lock, &OldIrq); + if (pRt->AddFl[Index].State != RT_OPEN) + { + + // + // free the lock, set the status and break out + // + CTEFreeLock (&pRt->Lock, OldIrq); + Status = STATUS_INVALID_HANDLE; + break; + } + // + // Let us reference the RtInfo structure so that it does not dissapear + // and also for some accounting + // + IpxReferenceRt(pRt, RT_SEND); + + + IpxPrint1("SendIrpFromRt: Index = (%d)\n", Index); + + // + // Store the AF pointer since IpxTdiSendDatagram will use it. Free + // the device lock since we have nothing more to do with our structures + // here. + // + // REQUEST_OPEN_CONTEXT(pIrp) = (PVOID)(pRtInfo->AddFl[Index].AddressFile); + CTEFreeLock (&pRt->Lock, OldIrq); + + Status = IpxTdiSendDatagram(pDevice->DeviceObject, pIrp); + + // + // All done with this send. Derefernce the RtInfo structure. + // + IpxDereferenceRt(pRtInfo, RT_SEND); + } while(FALSE); + + IpxPrint0("SendIrpfromRt - leaving\n"); + return(Status); +} + + NTSTATUS +RcvIrpFromRt ( + IN PDEVICE pDevice, + IN PIRP pIrp + ) +/*++ + +Routine Description; + + This function takes the rcv irp posted by RT and decides if there are + any datagram queued waiting to go up to RT. If so then the datagram + is copied to the RT buffer and passed back up. Otherwise the irp is + held by Netbt until a datagram does come in. + +Arguments; + + pDevice - not used + pIrp - Rt Rcv Irp + +Return Value; + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes; + + +--*/ + +{ + NTSTATUS status; + PRTRCV_BUFFER pBuffer; + PLIST_ENTRY pEntry; + CTELockHandle OldIrq; + PRT_INFO pRt; + PIPX_DATAGRAM_OPTIONS2 pRtBuffer; + PRT_IRP pRtAF; + ULONG Index; +#if DBG + ULONG NoOfRcvIrp; +#endif + + IpxPrint0("RcvIrpfromRt - Entered\n"); + + // pRt = REQUEST_OPEN_CONTEXT(pIrp); + pRt = pRtInfo; + + Index = RT_ADDRESS_INDEX(pIrp); + + IpxPrint1("RcvIrpFromRt: Index = (%d)\n", Index); + + IpxVerifyRt(pRt); + CTEAssert(pRt && (pRt == pRtInfo)); + CTEAssert(Index < IPX_RT_MAX_ADDRESSES); + + CTEGetLock (&pRt->Lock, &OldIrq); + do + { + pRtAF = &pRt->AddFl[Index]; + if (pRtAF->State != RT_OPEN) + { + status = STATUS_INVALID_HANDLE; + CTEFreeLock (&pRt->Lock, OldIrq); + break; + } + IpxReferenceRt(pRt, RT_IRPIN); + + if (!IsListEmpty(&pRtAF->RcvList)) + { + PMDL pMdl; + ULONG CopyLength; + ULONG UserBufferLengthToPass; + ULONG MdlLength; + + // + // There is at least one datagram waiting to be received + // + pEntry = RemoveHeadList(&pRtAF->RcvList); + + pBuffer = (PRTRCV_BUFFER)CONTAINING_RECORD(pEntry,RTRCV_BUFFER, + Linkage); + + IpxPrint0("RcvIrpFromRt: Buffer dequeued\n"); + // + // Copy the datagram and the source address to RT buffer and + // return to RT + // + pMdl = pIrp->MdlAddress; + IpxPrint2("RcvIrpFromRt: Irp=(%lx); Mdl=(%lx)\n", pIrp, pMdl); + CTEAssert(pMdl); + if (!pMdl) + { + status = STATUS_BUFFER_TOO_SMALL; + CTEFreeLock (&pRt->Lock, OldIrq); + IpxDereferenceRt(pRtInfo, RT_IRPIN); + break; + + } + pRtBuffer = MmGetSystemAddressForMdl(pMdl); + MdlLength = MmGetMdlByteCount(pMdl); + + UserBufferLengthToPass = pBuffer->UserBufferLengthToPass; + + CopyLength = (UserBufferLengthToPass <= MdlLength) ? UserBufferLengthToPass : MdlLength; + IpxPrint0("RcvIrpFromRt: Copying Options\n"); + RtlCopyMemory((PVOID)pRtBuffer, + (PVOID)&pBuffer->Options, + CopyLength); + + // + // subtract from the total amount buffered for RT since we are + // passing a datagram up to RT now. + // + pRtInfo->RcvMemoryAllocated -= pBuffer->TotalAllocSize; + RtFreeMem(pBuffer, pBuffer->TotalAllocSize); + + CTEAssert(pRtBuffer->DgrmOptions.LocalTarget.NicId); + + // + // pass the irp up to RT + // + if (CopyLength < UserBufferLengthToPass) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + status = STATUS_SUCCESS; + } +#if DBG + NoOfRcvIrp = pRtAF->NoOfRcvIrps; +#endif + + CTEFreeLock (&pRt->Lock, OldIrq); + + + IpxPrint3("Returning Rt rcv Irp immediately with queued dgram, status=%X,pIrp=%X. NoOfRcvIrp=(%d)\n" ,status,pIrp, NoOfRcvIrp); + + pIrp->IoStatus.Information = CopyLength; + pIrp->IoStatus.Status = status; + } + else + { + + status = NTCheckSetCancelRoutine(pIrp,RtIrpCancel,pDevice); + + if (!NT_SUCCESS(status)) + { + CTEFreeLock (&pRt->Lock, OldIrq); + } + else + { + if (pRtAF->NoOfRcvIrps++ > RT_IRP_MAX) + { + IpxPrint1("RcvIrpFromRt; REACHED LIMIT OF IRPS. NoOfRcvIrp=(%d)\n", pRtAF->NoOfRcvIrps); + status = STATUS_INSUFFICIENT_RESOURCES; + pRtAF->NoOfRcvIrps--; + CTEFreeLock (&pRt->Lock, OldIrq); + + } + else + { + InsertTailList(&pRtAF->RcvIrpList,REQUEST_LINKAGE(pIrp)); + IpxPrint2("IpxRt;Holding onto Rt Rcv Irp, pIrp =%Xstatus=%X\n", status,pIrp); + + status = STATUS_PENDING; + CTEFreeLock(&pRt->Lock,OldIrq); + } + } + + + } + IpxDereferenceRt(pRtInfo, RT_IRPIN); + } while(FALSE); + + IpxPrint0("RcvIrpfromRt - Leaving\n"); + return(status); + +} + +//---------------------------------------------------------------------------- + NTSTATUS +PassDgToRt ( + IN PDEVICE pDevice, + IN PIPX_DATAGRAM_OPTIONS2 pContext, + IN ULONG Index, + IN VOID UNALIGNED *pDgrm, + IN ULONG uNumBytes + ) +/*++ + +Routine Description; + + This function is used to allow NBT to pass name query service Pdu's to + RT. Rt posts a Rcv irp to Netbt. If the Irp is here then simply + copy the data to the irp and return it, otherwise buffer the data up + to a maximum # of bytes. Beyond that limit the datagrams are discarded. + + If Retstatus is not success then the pdu will also be processed by + nbt. This allows nbt to process packets when wins pauses and + its list of queued buffers is exceeded. + +Arguments; + + pDevice - card that the request can in on + pSrcAddress - source address + pDgrm - ptr to the datagram + uNumBytes - length of datagram + +Return Value; + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes; + + +--*/ + +{ + NTSTATUS status; + PIPX_DATAGRAM_OPTIONS2 pRtBuffer; + PIRP pIrp; + CTELockHandle OldIrq; + + + IpxPrint0("PassDgToRt - Entered\n"); + + // + // Get the source port and ip address, since RT needs this information. + // + IpxPrint1("PassDgToRt: Index = (%d)\n", Index); + CTEGetLock(&pRtInfo->Lock,&OldIrq); + + do + { + PRT_IRP pRtAF = &pRtInfo->AddFl[Index]; + if (pRtAF->State != RT_OPEN) + { + CTEFreeLock(&pRtInfo->Lock,OldIrq); + break; + } + IpxReferenceRt(pRtInfo, RT_BUFF); + if (IsListEmpty(&pRtAF->RcvIrpList)) + { + IpxPrint0("PassDgToRt: No Rcv Irp\n"); + if (pRtInfo->RcvMemoryAllocated < pRtInfo->RcvMemoryMax) + { + PRTRCV_BUFFER pBuffer; + + pBuffer = RtAllocMem(uNumBytes + sizeof(RTRCV_BUFFER)); + if (pBuffer) + { + pBuffer->TotalAllocSize = uNumBytes + sizeof(RTRCV_BUFFER); + + // + // Copy the user data + // + RtlCopyMemory( + (PUCHAR)((PUCHAR)pBuffer + OFFSET_PKT_IN_RCVBUFF), + (PVOID)pDgrm,uNumBytes); + + + pBuffer->Options.DgrmOptions.LocalTarget.NicId = + pContext->DgrmOptions.LocalTarget.NicId; + pBuffer->Options.LengthOfExtraOpInfo = 0; + + // + // total amount allocated for user + // + pBuffer->UserBufferLengthToPass = uNumBytes + OFFSET_PKT_IN_OPTIONS; + + CTEAssert(pContext->DgrmOptions.LocalTarget.NicId); + IpxPrint2("PassDgToRt: Nic Id is (%d). BufferLength is (%lx)\n", pContext->DgrmOptions.LocalTarget.NicId, uNumBytes); + + + + // + // Keep track of the total amount buffered so that we don't + // eat up all non-paged pool buffering for RT + // + pRtInfo->RcvMemoryAllocated += pBuffer->TotalAllocSize; + + IpxPrint0("IpxRt;Buffering Rt Rcv - no Irp, status=%X\n"); + InsertTailList(&pRtAF->RcvList,&pBuffer->Linkage); + IpxPrint0("PassDgToRt: Buffer Queued\n"); + status = STATUS_SUCCESS; + } + else + { + IpxPrint0("PassDgToRt; Could not allocate buffer\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + // this ret status will allow netbt to process the packet. + // + IpxPrint0("PassDgToRt; Dropping Pkt\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + CTEFreeLock(&pRtInfo->Lock,OldIrq); + } + else + { + PMDL pMdl; + ULONG CopyLength; + ULONG DgrmLength; + ULONG MdlBufferLength; + ULONG BytesToCopy; + PLIST_ENTRY pLE; + + // + // The recv irp is here so copy the data to its buffer and + // pass it up to RT + // + pLE = RemoveHeadList(&pRtAF->RcvIrpList); + pIrp = CONTAINING_RECORD(pLE, IRP, Tail.Overlay.ListEntry); + + (*(REQUEST_LINKAGE(pIrp))).Flink = NULL; + (*(REQUEST_LINKAGE(pIrp))).Blink = NULL; + + // + // Copy the datagram and the source address to RT buffer and + // return to RT + // + pMdl = pIrp->MdlAddress; + IpxPrint2("PassDgToRt: Irp=(%lx); Mdl=(%lx)\n", pIrp, pMdl); + CTEAssert(pMdl); + + pRtBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + MdlBufferLength = MmGetMdlByteCount(pMdl); + DgrmLength = uNumBytes; + BytesToCopy = DgrmLength + OFFSET_PKT_IN_OPTIONS; + + CopyLength = (BytesToCopy <= MdlBufferLength) ? BytesToCopy : MdlBufferLength; + IpxPrint2("PassDgToRt: Copy Length = (%d); Mdl Buffer Length is (%d)\n", CopyLength, MdlBufferLength); + + // + // Copy user datagram into pRtBuffer + // + RtlCopyMemory((PVOID)((PUCHAR)pRtBuffer + OFFSET_PKT_IN_OPTIONS), + (PVOID)pDgrm, + CopyLength-OFFSET_PKT_IN_OPTIONS); + + IpxPrint1("Data copied is (%.12s)\n", (PUCHAR)((PUCHAR)pRtBuffer + OFFSET_PKT_IN_OPTIONS + sizeof(IPX_HEADER))); + + pRtBuffer->DgrmOptions.LocalTarget.NicId = pContext->DgrmOptions.LocalTarget.NicId; + pRtBuffer->LengthOfExtraOpInfo = 0; + + IpxPrint3("PassDgToRt: Copy to RcvIrp;Nic Id is (%d/%d). BufferLength is (%lx)\n", pContext->DgrmOptions.LocalTarget.NicId, pRtBuffer->DgrmOptions.LocalTarget.NicId, uNumBytes); + + + CTEAssert(pContext->DgrmOptions.LocalTarget.NicId); + + // + // pass the irp up to RT + // + if (CopyLength < BytesToCopy) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + status = STATUS_SUCCESS; + } + + InsertTailList(&pRtInfo->CompletedIrps, REQUEST_LINKAGE(pIrp)); + pRtAF->NoOfRcvIrps--; + IpxPrint4("PassDgToRt;Returning Rt Rcv Irp - data from net, Length=%X,pIrp=%X; status = (%d). NoOfRcvIrp = (%d)\n" ,uNumBytes,pIrp, status, pRtAF->NoOfRcvIrps); + + pIrp->IoStatus.Status = status; + pIrp->IoStatus.Information = CopyLength; + CTEFreeLock(&pRtInfo->Lock,OldIrq); + + } + IpxDereferenceRt(pRtInfo, RT_BUFF); + } while (FALSE); + + + IpxPrint0("PassDgToRt - Entered\n"); + return(status); + +} + +//---------------------------------------------------------------------------- + VOID +RtIrpCancel( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description; + + This routine handles the cancelling a RtRcv Irp. It must release the + cancel spin lock before returning re; IoCancelIrp(). + +Arguments; + + +Return Value; + + The final status from the operation. + +--*/ +{ + KIRQL OldIrq; + PRT_INFO pRt; + PDEVICE pDevice = IpxDevice; + ULONG Index; + PIRP pTmpIrp; + + IpxPrint0("RtIrpCancel;Got a Rt Irp Cancel !!! *****************\n"); + + Index = RT_ADDRESS_INDEX(pIrp); + IpxPrint1("RtIrpCancel: Index = (%d)\n", Index); + // pRt = (PRT_INFO)REQUEST_OPEN_CONTEXT(pIrp); + pRt = pRtInfo; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + if ((pRt->Type != IPX_RT_SIGNATURE) || (pRt->Size != sizeof(RT_INFO))) { + return; + } + + + // + // Be sure that PassNamePduToRt has not taken the RcvIrp for a + // Rcv just now. + // + CTEGetLock(&pRt->Lock,&OldIrq); + if (pRt && (pRt == pRtInfo) && (*(REQUEST_LINKAGE(pIrp))).Flink != NULL) + { + + PRT_IRP pRtAF = &pRt->AddFl[Index]; + + RemoveEntryList(REQUEST_LINKAGE(pIrp)); + + pIrp->IoStatus.Status = STATUS_CANCELLED; + pRtAF->NoOfRcvIrps--; + CTEFreeLock(&pRt->Lock,OldIrq); + IpxPrint1("RtIrpCancel;Completing Request. NoOfRcvIrp = (%d)\n", pRtAF->NoOfRcvIrps); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } else { + CTEFreeLock(&pRt->Lock,OldIrq); + } +} +//---------------------------------------------------------------------------- + PVOID +RtAllocMem( + IN ULONG Size + ) + +/*++ +Routine Description; + + This Routine handles allocating memory and keeping track of how + much has been allocated. + +Arguments; + + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value; + + ptr to the memory allocated + +--*/ + +{ + if (pRtInfo->RcvMemoryAllocated > pRtInfo->RcvMemoryMax) + { + return NULL; + } + else + { + pRtInfo->RcvMemoryAllocated += Size; + return (AllocMem(Size)); + } +} +//---------------------------------------------------------------------------- + VOID +RtFreeMem( + IN PVOID pBuffer, + IN ULONG Size + ) + +/*++ +Routine Description; + + This Routine handles freeing memory and keeping track of how + much has been allocated. + +Arguments; + + pBuffer - buffer to free + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value; + + none + +--*/ + +{ + if (pRtInfo) + { + pRtInfo->RcvMemoryAllocated -= Size; + } + + FreeMem(pBuffer, Size); +} + + + +//---------------------------------------------------------------------------- + +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength) + +/*++ +Routine Description; + + This Routine handles calling the NT I/O system to complete an I/O. + +Arguments; + + status - a completion status for the Irp + +Return Value; + + NTSTATUS - status of the request + +--*/ + +{ + KIRQL OldIrq; + + if (Status != -1) + { + pIrp->IoStatus.Status = Status; + } + // use -1 as a flag to mean do not adjust the sent length since it is + // already set + if (SentLength != -1) + { + pIrp->IoStatus.Information = SentLength; + } + +#if DBG + if (SentLength != -1) + { + if ( (Status != STATUS_SUCCESS) && + (Status != STATUS_PENDING) && + (Status != STATUS_INVALID_DEVICE_REQUEST) && + (Status != STATUS_INVALID_PARAMETER) && + (Status != STATUS_IO_TIMEOUT) && + (Status != STATUS_BUFFER_OVERFLOW) && + (Status != STATUS_BUFFER_TOO_SMALL) && + (Status != STATUS_INVALID_HANDLE) && + (Status != STATUS_INSUFFICIENT_RESOURCES) && + (Status != STATUS_CANCELLED) && + (Status != STATUS_DUPLICATE_NAME) && + (Status != STATUS_TOO_MANY_NAMES) && + (Status != STATUS_TOO_MANY_SESSIONS) && + (Status != STATUS_REMOTE_NOT_LISTENING) && + (Status != STATUS_BAD_NETWORK_PATH) && + (Status != STATUS_HOST_UNREACHABLE) && + (Status != STATUS_CONNECTION_REFUSED) && + (Status != STATUS_WORKING_SET_QUOTA) && + (Status != STATUS_REMOTE_DISCONNECT) && + (Status != STATUS_LOCAL_DISCONNECT) && + (Status != STATUS_LINK_FAILED) && + (Status != STATUS_SHARING_VIOLATION) && + (Status != STATUS_UNSUCCESSFUL) && + (Status != STATUS_ACCESS_VIOLATION) && + (Status != STATUS_NONEXISTENT_EA_ENTRY) ) + { + IpxPrint1("returning unusual status = %X\n",Status); + } + } +#endif + IpxPrint1("Irp Status is %d\n", pIrp->IoStatus.Status); + + // + // set the Irps cancel routine to null or the system may bugcheck + // with a bug code of CANCEL_STATE_IN_COMPLETED_IRP + // + // refer to IoCancelIrp() ..\ntos\io\iosubs.c + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT); +} + + +//---------------------------------------------------------------------------- + NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN PDEVICE pDevice + ) + +/*++ +Routine Description; + + This Routine sets the cancel routine for an Irp. + +Arguments; + + status - a completion status for the Irp + +Return Value; + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + + IpxPrint1("CheckSetCancelRoutine: Entered. Irp = (%lx)\n", pIrp); + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + if (pIrp->Cancel) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + status = STATUS_CANCELLED; + + } + else + { + // setup the cancel routine + IoMarkIrpPending(pIrp); + IoSetCancelRoutine(pIrp,CancelRoutine); + status = STATUS_SUCCESS; + } + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return(status); + +} + + + + +VOID +IpxRefRt( + PRT_INFO pRt + ) + +/*++ + +Routine Description; + + This routine increments the reference count on a device context. + +Arguments; + + Binding - Pointer to a transport device context object. + +Return Value; + + none. + +--*/ + +{ + + (VOID)InterlockedIncrement (&pRt->ReferenceCount); +// CTEAssert (pRt->ReferenceCount > 0); // not perfect, but... +// IpxPrint1("RefRt: RefCount is (%d)\n", pRt->ReferenceCount); + +} /* IpxRefRt */ + + +VOID +IpxDerefRt( + PRT_INFO pRt + ) + +/*++ + +Routine Description; + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments; + + Binding - Pointer to a transport device context object. + +Return Value; + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&pRt->ReferenceCount); +// IpxPrint1("DerefRt: RefCount is (%d)\n", pRt->ReferenceCount); + +// CTEAssert (result >= 0); + +#if 0 + if (result == 0) { + IpxDestroyRt (pRt); + } +#endif + +} /* IpxDerefRt */ + + + + +VOID +IpxDestroyRt( + IN PRT_INFO pRt + ) + +/*++ + +Routine Description; + + This routine destroys a binding structure. + +Arguments; + + Binding - Pointer to a transport binding structure. + +Return Value; + + None. + +--*/ + +{ + IpxPrint0("Destroying Rt\n"); + FreeMem (pRt, sizeof(RT_INFO)); + pRtInfo = NULL; + return; +} /* IpxDestroyRt */ + diff --git a/private/ntos/tdi/isn/ipx/send.c b/private/ntos/tdi/isn/ipx/send.c new file mode 100644 index 000000000..b4e885c48 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/send.c @@ -0,0 +1,2364 @@ + +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code that implements the send engine for the + IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - August-25-1995 + Bug Fixes - tagged [SA] + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// BUGBUG Using the macro for performance reasons. Should be taken out +// when NdisQueryPacket is optimized. In the near future (after PPC release) +// move this to a header file and use it at other places. +// +#define IPX_PACKET_HEAD(Pkt) (Pkt)->Private.Head + +#if 0 +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} +#endif + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(NdisPacket->ProtocolReserved); + PADAPTER Adapter = (PADAPTER)ProtocolBindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PDEVICE Device = IpxDevice; + PBINDING Binding; + USHORT NewId, OldId; + ULONG NewOffset, OldOffset; + PIPX_HEADER IpxHeader; + IPX_LOCAL_TARGET LocalTarget; + PIO_STACK_LOCATION irpSp; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + +#if DBG + if (Adapter != NULL) { + ASSERT_ADAPTER(Adapter); + } +#endif + + // + // See if this send was padded. + // +RealFunctionStart:; + if (Reserved->PaddingBuffer) { + + UINT Offset; + // + // Check if we simply need to re-adjust the buffer length. This will + // happen if we incremented the buffer length in MAC.C. + // + + if (Reserved->PreviousTail) { + CTEAssert (NDIS_BUFFER_LINKAGE(Reserved->PaddingBuffer->NdisBuffer) == NULL); + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT BufferLength; + + NdisQueryBufferOffset( LastBuffer, &Offset, &BufferLength ); + NdisAdjustBufferLength( LastBuffer, (BufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (NdisPacket); + } + } + +FunctionStart:; + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + + // + // Check if this packet should be sent to all + // networks. + // + + if (Reserved->u.SR_DG.CurrentNicId) { + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->u.SR_DG.Net0SendSucceeded = TRUE; + } + + OldId = Reserved->u.SR_DG.CurrentNicId; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if ((Binding = NIC_ID_TO_BINDING(Device, NewId)) +#else + for (NewId = OldId+1; NewId <= Device->HighestExternalNicId; NewId++) { + if ((Binding = Device->Bindings[NewId]) +#endif _PNP_POWER + && + ((!Device->SingleNetworkActive) || + (Device->ActiveNetworkWan == Binding->Adapter->MacInfo.MediumAsync)) + && + (Device->ForwarderBound || + (!Device->DisableDialoutSap) || + (!Binding->DialOutAsync) || + (!Reserved->u.SR_DG.OutgoingSap))) { + + // + // The binding exists, and we either are not configured + // for "SingleNetworkActive", or we are and this binding + // is the right type (i.e. the active network is wan and + // this is a wan binding, or the active network is not + // wan and this is not a wan binding), and if the FWD is + // not bound; and this is not an outgoing sap that we are + // trying to send with "DisableDialoutSap" set. + // + + break; + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + + Reserved->u.SR_DG.CurrentNicId = NewId; + CTEAssert ((Reserved->DestinationType == DESTINATION_BCAST) || + (Reserved->DestinationType == DESTINATION_MCAST)); + +#if 0 + NewOffset = Binding->BcMcHeaderSize; + OldOffset = Device->Bindings[OldId]->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER)); + + } + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); +#endif + + + +#if BACK_FILL + // This should be a normal packet. Backfill packet is never used for + // reserved other than IPX type + + CTEAssert(!Reserved->BackFill); +#endif + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&LocalTarget, NewId); +#else + LocalTarget.NicId = NewId; +#endif + RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual net. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + + // + // [FW] Call the InternalSendHandler of the Forwarder + // + + if (Device->ForwarderBound) { + + // + // Call the InternalSend to filter the packet and get to know + // the correct adapter context + // + + NTSTATUS ret; + PUCHAR IpxHeader; + PUCHAR Data; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + UINT DataLength; + ULONG FwdAdapterCtx = INVALID_CONTEXT_VALUE; + + if (GET_VALUE(Binding->ReferenceCount) == 2) { + FwdAdapterCtx = Binding->FwdAdapterContext; + } + + // + // Figure out the IpxHeader - it is always at the top of the second MDL. + // + NdisQueryPacket (NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer (NDIS_BUFFER_LINKAGE(HeaderBuffer), &IpxHeader, &TempHeaderBufferLength); + + // + // Data is always at the top of the third MDL. + // + NdisQueryBuffer (NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(HeaderBuffer)), &Data, &DataLength); + + ret = (*Device->UpperDrivers[IDENTIFIER_RIP].InternalSendHandler)( + &LocalTarget, + FwdAdapterCtx, + NdisPacket, + IpxHeader, + Data, + REQUEST_INFORMATION(Reserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + FALSE); + + // + // The return shd not be a silent drop - we dont broadcast keepalives. + // + CTEAssert(ret != STATUS_DROP_SILENTLY); + + if (ret == STATUS_SUCCESS) { + // + // The adapter could have gone away and we have indicated to the Forwarder + // but the Forwarder has not yet closed the adapter. + // [ZZ] adapters do not go away now. + // + // BUGBUG: what if the binding is NULL here? Can we trust the Forwarder to + // give us a non-NULL binding? + // + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&LocalTarget)); + + if (GET_VALUE(Binding->ReferenceCount) == 1) { + Adapter = Binding->Adapter; +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + goto FunctionStart; + } else { + goto send_packet; + } + + } else if (ret == STATUS_PENDING) { + // + // LocalTarget will get filled up in InternalSendComplete + // + return; + } + // + // else DISCARD + // + Adapter = Binding->Adapter; +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + goto FunctionStart; + + } else { +send_packet: + // + // [FW] Use the frametype specific send handler + // + + // if ((NdisStatus = IpxSendFrame( + // &LocalTarget, + // NdisPacket, + // REQUEST_INFORMATION(Reserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + // sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + // + // Adapter = Binding->Adapter; + // goto FunctionStart; + // } + // + // return; + + if ((NdisStatus = (*Binding->SendFrameHandler)( + Binding->Adapter, + &LocalTarget, + NdisPacket, + REQUEST_INFORMATION(Reserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + goto RealFunctionStart; + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + return; + } + } else { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + + if (Reserved->u.SR_DG.Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + + } + + +#if 0 + // + // NOTE: We don't NULL out the linkage field of the + // HeaderBuffer, which will leave the old buffer chain + // hanging off it; but that is OK because if we reuse + // this packet we will replace that chain with the new + // one, and before we free it we NULL it out. + // + // I.e. we don't do this: + // + + NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer) = NULL; + NdisRecalculatePacketCounts (NdisPacket); +#endif + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer), &ActualLength); + if (ActualLength != REQUEST_INFORMATION(Reserved->u.SR_DG.Request)) { + DbgPrint ("IPX: At completion, IRP %lx has parameter length %d, buffer chain length %d\n", + Reserved->u.SR_DG.Request, REQUEST_INFORMATION(Reserved->u.SR_DG.Request), ActualLength); + DbgBreakPoint(); + } + } +#endif + + // + // Save these so we can free the packet. + // + + Request = Reserved->u.SR_DG.Request; + AddressFile = Reserved->u.SR_DG.AddressFile; + + +#if BACK_FILL + // Check if this is backfilled. If so restore users Mdl back to its original shape + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + + if (Reserved->BackFill) { + + Reserved->HeaderBuffer->MappedSystemVa = Reserved->MappedSystemVa; + Reserved->HeaderBuffer->ByteCount = Reserved->UserLength; + Reserved->HeaderBuffer->StartVa = (PCHAR)((ULONG)Reserved->HeaderBuffer->MappedSystemVa & ~(PAGE_SIZE-1)); + Reserved->HeaderBuffer->ByteOffset = (ULONG)Reserved->HeaderBuffer->MappedSystemVa & (PAGE_SIZE-1); + + IPX_DEBUG(SEND, ("completeing back filled userMdl %x\n",Reserved->HeaderBuffer)); + + NdisPacket->Private.ValidCounts = FALSE; + + NdisPacket->Private.Head = NULL; + NdisPacket->Private.Tail = NULL; + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } + // not a back fill packet. Push it on sendpacket pool + else { + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + + } + +#else + + if (Reserved->OwnedByAddress) { + + + Reserved->Address->SendPacketInUse = FALSE; + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } +#endif + + ++Device->Statistics.PacketsSent; + + // + // If this is a fast send irp, we bypass the file system and + // call the completion routine directly. + // + + REQUEST_STATUS(Request) = NdisStatus; + irpSp = IoGetCurrentIrpStackLocation( Request ); + + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + } else { + IpxCompleteRequest (Request); + } + + IpxFreeRequest(Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + + break; + + case IDENTIFIER_RIP_INTERNAL: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + break; + + case IDENTIFIER_RIP_RESPONSE: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + break; + +#ifdef _PNP_POWER + case IDENTIFIER_NB: + case IDENTIFIER_SPX: + + // + // See if this is an iterative send + // + if (OldId = Reserved->CurrentNicId) { + + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + PUCHAR Header; + PIPX_HEADER IpxHeader; + BOOLEAN fFwdDecides=FALSE; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->Net0SendSucceeded = TRUE; + } + + // + // Figure out the IpxHeader - it is always at the top of the second MDL. + // + NdisQueryPacket (NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer (NDIS_BUFFER_LINKAGE(HeaderBuffer), &IpxHeader, &TempHeaderBufferLength); + + // + // For Type 20 pkts, we let the Fwd decide the next Nic to send on, so we pass + // the old Nic itself and let the Fwd change it for us. + // + if ((Device->ForwarderBound) && + (IpxHeader->PacketType == 0x14)) { + NewId = NIC_FROM_LOCAL_TARGET(&Reserved->LocalTarget); + fFwdDecides=TRUE; + Binding = NIC_ID_TO_BINDING(Device, NewId); + IPX_DEBUG(SEND, ("SendComplete: IpxHeader has Type20: %lx\n", IpxHeader)); + } else { + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if (Binding = NIC_ID_TO_BINDING(Device, NewId)) { + // + // Found next NIC to send on + // + break; + } + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + IPX_DEBUG(SEND, ("ISN iteration: OldId: %lx, NewId: %lx\n", OldId, NewId)); + Reserved->CurrentNicId = NewId; + +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&LocalTarget, NewId); +#else + LocalTarget.NicId = NewId; +#endif + RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + // + // [FW] Call the InternalSendHandler of the Forwarder + // + + if (Device->ForwarderBound) { + + // + // Call the InternalSend to filter the packet and get to know + // the correct adapter context + // + + NTSTATUS ret; + PUCHAR Data; + UINT DataLength; + ULONG FwdAdapterCtx = INVALID_CONTEXT_VALUE; + + if (GET_VALUE(Binding->ReferenceCount) == 2) { + FwdAdapterCtx = Binding->FwdAdapterContext; + } + + ret = (*Device->UpperDrivers[IDENTIFIER_RIP].InternalSendHandler)( + &LocalTarget, + FwdAdapterCtx, + NdisPacket, + (PUCHAR)IpxHeader, + ((PUCHAR)IpxHeader)+sizeof(IPX_HEADER), // the data starts after the IPX Header. + Reserved->PacketLength, + TRUE); // iterate is true + + // + // The return shd not be a silent drop - we dont broadcast keepalives. + // + CTEAssert(ret != STATUS_DROP_SILENTLY); + + if (ret == STATUS_SUCCESS) { + // + // The adapter could have gone away and we have indicated to the Forwarder + // but the Forwarder has not yet closed the adapter. + // [ZZ] adapters do not go away now. + // + // BUGBUG: what if the binding is NULL here? Can we trust the Forwarder to + // give us a non-NULL binding? + // + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&LocalTarget)); + + if (GET_VALUE(Binding->ReferenceCount) == 1) { + Adapter = Binding->Adapter; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + goto FunctionStart; + } else { + NewId = NIC_FROM_LOCAL_TARGET(&LocalTarget); + goto send_packet1; + } + + } else if (ret == STATUS_PENDING) { + // + // LocalTarget will get filled up in InternalSendComplete + // + return; + } + // + // else DISCARD + // + Adapter = Binding->Adapter; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + // + // If Fwd decides, then this is end of Nic list - complete the send. + // + if (fFwdDecides) { + goto NoMoreSends; + } else { + goto FunctionStart; + } + + } else { +#if DBG + NdisQueryPacket (NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + + IpxHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + IPX_DEBUG(SEND, ("SendComplete: IpxHeader: %lx\n", IpxHeader)); +#endif + +send_packet1: + + FILL_LOCAL_TARGET(&Reserved->LocalTarget, NewId); + + // + // We don't need to so this since the macaddress is replaced in + // IpxSendFrame anyway. The LocalTarget is the same as the one on + // the original send - this is passed down for further sends. + // + // RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + &Reserved->LocalTarget, + NdisPacket, + Reserved->PacketLength, + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + goto FunctionStart; + } + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + return; + } + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +NoMoreSends: + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + if (Reserved->Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + } + + // + // fall thru' + // +#endif + default: + + (*Device->UpperDrivers[Reserved->Identifier].SendCompleteHandler)( + NdisPacket, + NdisStatus); + break; + } + +} /* IpxSendComplete */ + + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS_FILE AddressFile; + PADDRESS Address; + PNDIS_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + TDI_ADDRESS_IPX UNALIGNED * RemoteAddress; + TDI_ADDRESS_IPX TempAddress; + TA_ADDRESS UNALIGNED * AddressName; + PTDI_CONNECTION_INFORMATION Information; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PBINDING Binding; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = IpxDevice; + UCHAR PacketType; + NTSTATUS Status; + PIPX_HEADER IpxHeader; + NDIS_STATUS NdisStatus; + USHORT LengthIncludingHeader; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PIO_STACK_LOCATION irpSp; \ + BOOLEAN IsLoopback = FALSE; + IPX_FIND_ROUTE_REQUEST routeEntry; + PIPX_DATAGRAM_OPTIONS2 Options; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutRequests); +#endif SNMP + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + IPX_BEGIN_SYNC (&SyncContext); + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) && + ((Address = AddressFile->Address) != NULL)) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_CLOSING) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + Information = Parameters->SendDatagramInformation; + + // + // Do a quick check if this address has only one entry. + // + + if (!REQUEST_SPECIAL_SEND(Request)) { + AddressName = &((TRANSPORT_ADDRESS UNALIGNED *)(Information->RemoteAddress))->Address[0]; + + if ((AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) && + (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX))) { + + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)(AddressName->Address); + + } else if ((RemoteAddress = IpxParseTdiAddress (Information->RemoteAddress)) == NULL) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_ADDRESS; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_no_packet; + } + } else { + ASSERT(OPEN_REQUEST_EA_LENGTH(Request) == sizeof(IPX_DATAGRAM_OPTIONS2)); + Options = ((PIPX_DATAGRAM_OPTIONS2)(OPEN_REQUEST_EA_INFORMATION(Request))); + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)(&Options->RemoteAddress); + IPX_DEBUG(SEND, ("IpxTdiSendDatagram: Options buffer supplied as input buffer\n")); + } + + IPX_DEBUG (SEND, ("Send on %lx, network %lx socket %lx\n", + Address, RemoteAddress->NetworkAddress, RemoteAddress->Socket)); + +#if 0 + if (Parameters->SendLength > IpxDevice->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + IpxDevice->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_no_packet; + } +#endif + // + // Every address has one packet committed to it, use that + // if possible, otherwise take one out of the pool. + // + + +#if BACK_FILL + + // If the request is coming from the server, which resrves transport header space + // build the header in its space. Allocate a special packet to which does not contain + // mac and ipx headers in its reserved space. + + if ((PMDL)REQUEST_NDIS_BUFFER(Request) && + (((PMDL)REQUEST_NDIS_BUFFER(Request))->MdlFlags & MDL_NETWORK_HEADER) && + (!(Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS))) && + (RemoteAddress->NodeAddress[0] != 0xff)) { + + //if (!Address->BackFillPacketInUse) { + if (InterlockedExchangeAdd(&Address->BackFillPacketInUse, 0) == 0) { + //Address->BackFillPacketInUse = TRUE; + InterlockedIncrement(&Address->BackFillPacketInUse); + + Packet = PACKET(&Address->BackFillPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEBUG(SEND, ("Getting owned backfill %x %x \n", Packet,Reserved)); + + }else { + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBackFillPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBackFillPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_no_packet; + } + +GotBackFillPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + IPX_DEBUG(SEND, ("getting backfill packet %x %x %x\n", s, Reserved, RemoteAddress->NodeAddress)); + if(!Reserved->BackFill)DbgBreakPoint(); + + } + + }else { + + // if (!Address->SendPacketInUse) { + if (InterlockedExchangeAdd(&Address->SendPacketInUse, 0) == 0) { + // Address->SendPacketInUse = TRUE; + InterlockedIncrement(&Address->SendPacketInUse); + + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + Reserved->BackFill = FALSE; + + } + + } + + +#else + + if (!Address->SendPacketInUse) { + + Address->SendPacketInUse = TRUE; + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + +#endif + + IpxReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + // + // Save this now while we have Parameters available. + // + + REQUEST_INFORMATION(Request) = Parameters->SendLength; + LengthIncludingHeader = (USHORT)(Parameters->SendLength + sizeof(IPX_HEADER)); + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(REQUEST_NDIS_BUFFER(Request), &ActualLength); + if (ActualLength != Parameters->SendLength) { + DbgPrint ("IPX: IRP %lx has parameter length %d, buffer chain length %d\n", + Request, Parameters->SendLength, ActualLength); + DbgBreakPoint(); + } + } +#endif + + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.Request = Request; + CTEAssert (Reserved->Identifier == IDENTIFIER_IPX); + + + // + // Set this to 0; this means the packet is not one that + // should be broadcast on all nets. We will change it + // later if it turns out this is the case. + // + + Reserved->u.SR_DG.CurrentNicId = 0; + + // + // We need this to track these packets specially. + // + + Reserved->u.SR_DG.OutgoingSap = AddressFile->IsSapSocket; + + // + // Add the MDL chain after the pre-allocated header buffer. + // NOTE: THIS WILL ONLY WORK IF WE EVENTUALLY CALL + // NDISRECALCULATEPACKETCOUNTS (which we do in IpxSendFrame). + // + // +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->HeaderBuffer = REQUEST_NDIS_BUFFER(Request); + + //remove the ipx mdl from the packet. + Reserved->UserLength = Reserved->HeaderBuffer->ByteCount; + + IPX_DEBUG(SEND, ("back filling userMdl Reserved %x %x\n", Reserved->HeaderBuffer, Reserved)); + } else { + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); + } +#else + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); +#endif + + + // + // If IrpSp does not have a buffer for the right size for + // datagram options and there is no input buffer + // + if (!REQUEST_SPECIAL_SEND(Request) && + (Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS))) { + + // + // The caller did not supply the local target for this + // send, so we look it up ourselves. + // + + UINT Segment; + + // + // We calculate this now since we need to know + // if it is directed below. + // + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send + } + + // + // If there are no options, then check if the + // caller is passing the packet type as a final byte + // in the remote address; if not use the default. + // + + if (Information->OptionsLength == 0) { + if (AddressFile->ExtendedAddressing) { + PacketType = ((PUCHAR)(RemoteAddress+1))[0]; + } else { + PacketType = AddressFile->DefaultPacketType; + } + } else { + PacketType = ((PUCHAR)(Information->Options))[0]; + } + + if ((Reserved->DestinationType != DESTINATION_DEF) && + ((RemoteAddress->NetworkAddress == 0) || + (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)))) { + + // + // This packet needs to be broadcast to all networks. + // Make sure it is not too big for any of them. + // + + if (Parameters->SendLength > Device->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, Device->RealMaxDatagramSize)); + Status = STATUS_INVALID_BUFFER_SIZE; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutMalformedRequests); +#endif SNMP + goto error_send_with_packet; + } + + // + // If this is a broadcast to the virtual net, we + // need to construct a fake remote address which + // has network 0 in there instead. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + RtlCopyMemory (&TempAddress, (PVOID)RemoteAddress, sizeof(TDI_ADDRESS_IPX)); + TempAddress.NetworkAddress = 0; + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)&TempAddress; + } + + // + // If someone is sending to the SAP socket and + // we are running with multiple cards without a + // virtual network, AND this packet is a SAP response, + // then we log an error to warn them that the + // system may not work as they like (since there + // is no virtual network to advertise, we use + // the first card's net/node as our local address). + // We only do this once per boot, using the + // SapWarningLogged variable to control that. + // + + if ((RemoteAddress->Socket == SAP_SOCKET) && + (!Device->SapWarningLogged) && + (Device->MultiCardZeroVirtual)) { + + PNDIS_BUFFER FirstBuffer; + UINT FirstBufferLength; + USHORT UNALIGNED * FirstBufferData; + + if ((FirstBuffer = REQUEST_NDIS_BUFFER(Request)) != NULL) { + + NdisQueryBuffer( + FirstBuffer, + (PVOID *)&FirstBufferData, + &FirstBufferLength); + + // + // The first two bytes of a SAP packet are the + // operation, 0x2 (in network order) is response. + // + + if ((FirstBufferLength >= sizeof(USHORT)) && + (*FirstBufferData == 0x0200)) { + + Device->SapWarningLogged = TRUE; + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_IPX_SAP_ANNOUNCE, + 777, + STATUS_NOT_SUPPORTED, + NULL, + 0, + NULL); + } + } + } + + + // + // In this case we do not RIP but instead set the + // packet up so it is sent to each network in turn. + // + // Special case: If this packet is from the SAP + // socket and we are running with multiple cards + // without a virtual network, we only send this + // on the card with NIC ID 1, so we leave + // CurrentNicId set to 0. + // + + // + // BUGBUG: What if NicId 1 is invalid? Should scan + // for first valid one, fail send if none. + // + + if ((Address->Socket != SAP_SOCKET) || + (!Device->MultiCardZeroVirtual)) { + + if (Device->SingleNetworkActive) { + + if (Device->ActiveNetworkWan) { + Reserved->u.SR_DG.CurrentNicId = Device->FirstWanNicId; + } else { + Reserved->u.SR_DG.CurrentNicId = Device->FirstLanNicId; + } + + } else { + + Reserved->u.SR_DG.CurrentNicId = 1; + + } + + Reserved->u.SR_DG.Net0SendSucceeded = FALSE; + + // + // In this case, we need to scan for the first + // non-dialout wan socket. + // + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET)) { + + PBINDING TempBinding; + + CTEAssert (Reserved->u.SR_DG.CurrentNicId <= Device->ValidBindings); + while (Reserved->u.SR_DG.CurrentNicId <= MIN (Device->MaxBindings, Device->ValidBindings)) { +#ifdef _PNP_POWER +// No need to lock the access path since he just looks at it +// + TempBinding = NIC_ID_TO_BINDING(Device, Reserved->u.SR_DG.CurrentNicId); +#else + TempBinding = Device->Bindings[Reserved->u.SR_DG.CurrentNicId]; +#endif _PNP_POWER + if ((TempBinding != NULL) && + (!TempBinding->DialOutAsync)) { + break; + } + ++Reserved->u.SR_DG.CurrentNicId; + } + if (Reserved->u.SR_DG.CurrentNicId > MIN (Device->MaxBindings, Device->ValidBindings)) { + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + + goto error_send_with_packet; + } + } +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, Reserved->u.SR_DG.CurrentNicId); +#else + TempLocalTarget.NicId = Reserved->u.SR_DG.CurrentNicId; +#endif + + } else { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, 1); +#else + TempLocalTarget.NicId = 1; +#endif + } + + RtlCopyMemory(TempLocalTarget.MacAddress, RemoteAddress->NodeAddress, 6); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + // + // [FW] the localtarget shd be in the packet's reserved section + // + LocalTarget = &Reserved->LocalTarget; + Reserved->LocalTarget = TempLocalTarget; + } else { + + // + // [FW] If router installed, call the Forwarder's FindRouteHandler. + // This returns a STATUS_SUCCESS if a route is available + // + if (Device->ForwarderBound) { + + Status = (*Device->UpperDrivers[IDENTIFIER_RIP].FindRouteHandler) ( + (PUCHAR)&RemoteAddress->NetworkAddress, + RemoteAddress->NodeAddress, + &routeEntry); + + if (Status != STATUS_SUCCESS) { + + IPX_DEBUG (SEND, ("RouteHandler failed, network: %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RemoteAddress->NetworkAddress))); + goto error_send_with_packet; + + } else { + + // + // Fill in the LocalTarget from the RouteEntry + // + + LocalTarget = &Reserved->LocalTarget; + + Reserved->LocalTarget = routeEntry.LocalTarget; + + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: LocalTarget is: %lx\n", Reserved->LocalTarget)); + + if (GET_VALUE(NIC_ID_TO_BINDING(Device, LocalTarget->NicId)->ReferenceCount) == 1) { + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: FWD returned SUCCESS, Ref count is 1\n")); + Status = NDIS_STATUS_SUCCESS; + goto error_send_with_packet; + } + + if (Parameters->SendLength > + NIC_ID_TO_BINDING(Device, LocalTarget->NicId)->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + NIC_ID_TO_BINDING(Device, LocalTarget->NicId)->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + + goto error_send_with_packet; + } + + // + // [FW] we dont need to check this since the FWD does it for us. + // + + /* + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (NIC_ID_TO_BINDING(Device, LocalTarget->NicId)->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_NETWORK_UNREACHABLE; + goto error_send_with_packet; + } + */ + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + IPX_DEBUG(SEND, ("FindRoute for %02x-%02x-%02x-%02x-%02x-%02x returned %lx\n", + LocalTarget->MacAddress[0], + LocalTarget->MacAddress[1], + LocalTarget->MacAddress[2], + LocalTarget->MacAddress[3], + LocalTarget->MacAddress[4], + LocalTarget->MacAddress[5], + Status)); + + } + + } else { + Segment = RipGetSegment((PUCHAR)&RemoteAddress->NetworkAddress); + + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + Status = RipGetLocalTarget( + Segment, + RemoteAddress, + IPX_FIND_ROUTE_RIP_IF_NEEDED, + &TempLocalTarget, + NULL); + + if (Status == STATUS_SUCCESS) { + + // + // We found the route, TempLocalTarget is filled in. + // + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + #ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (NIC_FROM_LOCAL_TARGET(&TempLocalTarget) == (USHORT)LOOPBACK_NIC_ID) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + FILL_LOCAL_TARGET(&TempLocalTarget, 1); + } + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + if (Parameters->SendLength > + Binding->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutMalformedRequests); +#endif SNMP + goto error_send_with_packet; + } + + if (!Device->ForwarderBound && + (Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Binding->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_with_packet; + } + #else + if (TempLocalTarget.NicId == 0) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + TempLocalTarget.NicId = 1; + } + + if (Parameters->SendLength > + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Device->Bindings[TempLocalTarget.NicId]->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + goto error_send_with_packet; + } + #endif _PNP_POWER + + } else if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this packet for transmission when the RIP + // response arrives. First we fill in the IPX + // header; the only thing we don't know is where + // exactly to fill it in, so we choose + // the most common location. + // + + IpxConstructHeader( + &Reserved->Header[Device->IncludedHeaderOffset], + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + // + // Adjust the 2nd mdl's size + // + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + + IPX_DEBUG (RIP, ("Queueing packet %lx\n", Reserved)); + + InsertTailList( + &Device->Segments[Segment].WaitingForRoute, + &Reserved->WaitLinkage); + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_with_packet; + + } + + // + // [FW] The localtarget shd be in the reserved section. + // + LocalTarget = &Reserved->LocalTarget; + Reserved->LocalTarget = TempLocalTarget; + } + } + + // + // [FW] moved to the conditions above so we save a copy in the RIP case + // + + // LocalTarget = &TempLocalTarget; + + // + // Now we know the local target, we can figure out + // the offset for the IPX header. + // +#ifdef _PNP_POWER +// Remember that we have got the binding with ref above.... + +#else + Binding = Device->Bindings[LocalTarget->NicId]; +#endif + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; +#if 0 + if (Reserved->DestinationType == DESTINATION_DEF) { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } else { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } +#endif + + } else { + + if (!REQUEST_SPECIAL_SEND(Request)) { + PacketType = ((PUCHAR)(Information->Options))[0]; + LocalTarget = &((PIPX_DATAGRAM_OPTIONS)(Information->Options))->LocalTarget; + } else { + ASSERT(OPEN_REQUEST_EA_LENGTH(Request) == sizeof(IPX_DATAGRAM_OPTIONS2)); + if (OPEN_REQUEST_EA_LENGTH(Request) == sizeof(IPX_DATAGRAM_OPTIONS2)) { + //IpxPrint0("IpxTdiSendDatagram: We have an input buffer of the right size\n"); + } else { + //IpxPrint1("IpxTdiSendDatagram: Wrong sized buffer. Buff size is =(%d)\n", OPEN_REQUEST_EA_LENGTH(Request)); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + PacketType = Options->DgrmOptions.PacketType; + LocalTarget = &Options->DgrmOptions.LocalTarget; + if (NIC_ID_TO_BINDING(Device, LocalTarget->NicId) == NULL) { + Status = STATUS_NOT_FOUND; + goto error_send_with_packet; + } + } + + // + // Calculate the binding and the correct location + // for the IPX header. We can do this at the same + // time as we calculate the DestinationType which + // saves an if like the one 15 lines up. + // + +#ifdef _PNP_POWER +// Get lock to ref. + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // If a loopback packet, use the first binding as place holder + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)LOOPBACK_NIC_ID) { + Binding = NIC_ID_TO_BINDING(Device, 1); + IsLoopback = TRUE; + } else { + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (LocalTarget->NicId == 0) { + Binding = Device->Bindings[1]; + IsLoopback = TRUE; + } else { + Binding = Device->Bindings[LocalTarget->NicId]; + } +#endif _PNP_POWER + if (Parameters->SendLength > Binding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutMalformedRequests); +#endif SNMP + goto error_send_with_packet; + } + +#if 0 + // + // This shouldn't be needed because even WAN bindings + // don't go away once they are added. + // + + if (Binding == NULL) { + Status = STATUS_DEVICE_DOES_NOT_EXIST; + goto error_send_with_packet; + } +#endif + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; + + } + + + ++Device->TempDatagramsSent; + Device->TempDatagramBytesSent += Parameters->SendLength; + + +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->MappedSystemVa = Reserved->HeaderBuffer->MappedSystemVa; + IpxHeader = (PIPX_HEADER)((PCHAR)Reserved->HeaderBuffer->MappedSystemVa - sizeof(IPX_HEADER)); + Reserved->HeaderBuffer->ByteOffset -= sizeof(IPX_HEADER); + (ULONG)Reserved->HeaderBuffer->MappedSystemVa-= sizeof(IPX_HEADER); + IPX_DEBUG(SEND, ("Adjusting backfill userMdl Ipxheader %x %x \n",Reserved->HeaderBuffer,IpxHeader)); + } +#endif + + // + // In case the packet is being sent to a SAP socket or + // we have multiple cards and a zero virtual net or + // it is a special send (on a nic), we need to use + // the binding's address instead of the virtual address. + // + if (Device->MultiCardZeroVirtual || + (Address->LocalAddress.Socket == SAP_SOCKET) || + (RemoteAddress->Socket == SAP_SOCKET) || + (REQUEST_SPECIAL_SEND(Request))) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual network number. + // + // If this is a binding set member and a local target + // was provided we will send using the real node of + // the binding, even if it was a slave. This is + // intentional. If no local target was provided then + // this will not be a binding slave. + // + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Binding->LocalAddress); + + IpxHeader->SourceSocket = Address->SendSourceSocket; + + } else { + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + } + + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + // + // Adjust the 2nd mdl's size + // +#if BACK_FILL + if (Reserved->BackFill) { + NdisAdjustBufferLength(Reserved->HeaderBuffer, (Reserved->HeaderBuffer->ByteCount+sizeof(IPX_HEADER))); + } else { + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + } +#else + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); +#endif + + IPX_DEBUG(SEND, ("Packet Head %x\n",IPX_PACKET_HEAD(Packet))); + + /* + if (Address->RtAdd) + { + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(pRtInfo); + } + */ + + // + // [FW] If Forwarder installed, send the packet out for filtering + // + // STEFAN: 3/28/96: + // Dont filter IPXWAN config packets since the FWD does not have this adapter opened yet. + // + + IPX_DEBUG(SEND, ("LocalAddress.Socket %x, IPXWAN_SOCKET\n", Address->LocalAddress.Socket, IPXWAN_SOCKET)); + if (Address->LocalAddress.Socket != IPXWAN_SOCKET && + Device->ForwarderBound) { + + // + // Call the InternalSend to filter the packet and get to know + // the correct adapter context + // + + NTSTATUS ret; + ULONG FwdAdapterCtx = INVALID_CONTEXT_VALUE; + PUCHAR Data; + UINT DataLength; + + if (GET_VALUE(Binding->ReferenceCount) == 2) { + FwdAdapterCtx = Binding->FwdAdapterContext; + } + + // + // Figure out the location of the data in the packet + // For BackFill packets, the data is in the first (and only) MDL. + // For others, it is in the third MDL. + // +#if BACK_FILL + if (Reserved->BackFill) { + Data = (PUCHAR)(IpxHeader+sizeof(IPX_HEADER)); + } else { + NdisQueryBuffer(NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)), &Data, &DataLength); + } +#else + NdisQueryBuffer(NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)), &Data, &DataLength); +#endif + + ret = (*Device->UpperDrivers[IDENTIFIER_RIP].InternalSendHandler)( + LocalTarget, + FwdAdapterCtx, + Packet, + (PUCHAR)IpxHeader, + Data, + LengthIncludingHeader, + FALSE); + + if (ret == STATUS_SUCCESS) { + // + // The adapter could have gone away and we have indicated to the Forwarder + // but the Forwarder has not yet closed the adapter. + // + // BUGBUG: what if the binding is NULL here? Can we trust the Forwarder to + // give us a non-NULL binding? + // + + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + + if (GET_VALUE(Binding->ReferenceCount) == 1) { + Status = NDIS_STATUS_SUCCESS; +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + goto error_send_with_packet; + } else { + IsLoopback = (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)LOOPBACK_NIC_ID); + goto send_packet; + } + } else if (ret == STATUS_PENDING) { + // + // LocalTarget will get filled up in InternalSendComplete + // + + // + // BUGBUG: this is a NULL macro - include this? + // + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + } else if (ret == STATUS_DROP_SILENTLY) { + IPX_DEBUG(SEND, ("IPX: SendFramePreFwd: FWD returned STATUS_DROP_SILENTLY - dropping pkt.\n")); + Status = NDIS_STATUS_SUCCESS; + +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + goto error_send_with_packet; + } + + // + // else DISCARD + // + +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + Status = STATUS_NETWORK_UNREACHABLE; + goto error_send_with_packet; + + } else { + + // + // [FW] Jump here if the Forwarder gave us the go ahead on this send. + // We also come here to send if the Forwarder is not installed. + // + +send_packet: + if (IsLoopback) { + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Packet: %lx, Addr: %lx, Addr->SendPacket: %lx\n", Packet, Address, Address->SendPacket)); + + // + // Recalculate packet counts here. + // + // NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#if BACK_FILL + + if (Reserved->BackFill) { + // + // Set the Header pointer and chain the first MDL + // + Reserved->Header = (PCHAR)Reserved->HeaderBuffer->MappedSystemVa; + NdisChainBufferAtFront(Packet,(PNDIS_BUFFER)Reserved->HeaderBuffer); + } +#endif + NdisRecalculatePacketCounts (Packet); +#ifdef _PNP_POWER + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); +#else + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); +#endif + + } else { + if ((NdisStatus = (*Binding->SendFrameHandler)( + Binding->Adapter, + LocalTarget, + Packet, + Parameters->SendLength + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + } + + IPX_END_SYNC (&SyncContext); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_PENDING; + } + + } else { + + // + // The address file state was closing. + // + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_HANDLE; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_no_packet; + + } + + } else { + + // + // The address file didn't look like one. + // + + Status = STATUS_INVALID_HANDLE; +#ifdef SNMP + ++IPX_MIB_ENTRY(Device, SysOutDiscards); +#endif SNMP + goto error_send_no_packet; + } + + // + // Jump here if we want to fail the send and we have already + // allocated the packet and ref'ed the address file. + // + +error_send_with_packet: + +#if BACK_FILL + // + // Check if this is backfilled. If so, set the headerbuffer to NULL. Note that we dont need + // restore to restore the user's MDL since it was never touched when this error occurred. + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + // + if (Reserved->BackFill) { + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + } else { + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } else { + // not a back fill packet. Push it on sendpacket pool + if (Reserved->OwnedByAddress) { + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + } +#else + if (Reserved->OwnedByAddress) { + Reserved->Address->SendPacketInUse = FALSE; + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } +#endif + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + +error_send_no_packet: + + // + // Jump here if we fail before doing any of that. + // + + IPX_END_SYNC (&SyncContext); + + irpSp = IoGetCurrentIrpStackLocation( Request ); + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + REQUEST_STATUS(Request) = Status; + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + IpxFreeRequest (DeviceObject, Request); + } + + return Status; + +} /* IpxTdiSendDatagram */ + + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ) + +/*++ + +Routine Description: + + This routine constructs an IPX header in a packet. + +Arguments: + + Header - The location at which the header should be built. + + PacketLength - The length of the packet, including the IPX header. + + PacketType - The packet type of the frame. + + RemoteAddress - The remote IPX address. + + LocalAddress - The local IPX address. + +Return Value: + + None. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)Header; + + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = (UCHAR)(PacketLength / 256); + IpxHeader->PacketLength[1] = (UCHAR)(PacketLength % 256); + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = PacketType; + + // + // These copies depend on the fact that the destination + // network is the first field in the 12-byte address. + // + + RtlCopyMemory(IpxHeader->DestinationNetwork, (PVOID)RemoteAddress, 12); + RtlCopyMemory(IpxHeader->SourceNetwork, LocalAddress, 12); + +} /* IpxConstructHeader */ +#endif + + + +// +// [FW] +// + +VOID +IpxInternalSendComplete( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN NTSTATUS Status + ) +/*++ + +Routine Description: + + This routine is called by the Kernel Forwarder to indicate that a pending + internal send to it has completed. + +Arguments: + + LocalTarget - if Status is OK, this has the local target for the send. + + Packet - A pointer to the NDIS_PACKET that we sent. + + PacketLength - length of the packet (including the IPX header) + + BUGBUG: Can IpxSendFrame use the local var. PktLength instead? What about IpxSendFrameXXX (frame specific) + + Status - the completion status of the send - STATUS_SUCCESS or STATUS_NETWORK_UNREACHABLE + +Return Value: + + none. + +--*/ +{ + PDEVICE Device=IpxDevice; + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding; + NDIS_STATUS NdisStatus; + PIO_STACK_LOCATION irpSp; + PREQUEST Request; + PADDRESS_FILE AddressFile; + + switch (Reserved->Identifier) + { + case IDENTIFIER_IPX: + + // + // datagrams can be sent to the frame-specific handlers directly + // + // BUGBUG: Make this change in SendComplete too + // + + if ((Status == STATUS_SUCCESS) && + (Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget))) && + (GET_VALUE(Binding->ReferenceCount) == 2)) { + + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)LOOPBACK_NIC_ID) { + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Packet: %lx \n", Packet)); + + // + // Recalculate packet counts here. + // + // NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#if BACK_FILL + + if (Reserved->BackFill) { + // + // Set the Header pointer and chain the first MDL + // + Reserved->Header = (PCHAR)Reserved->HeaderBuffer->MappedSystemVa; + NdisChainBufferAtFront(Packet,(PNDIS_BUFFER)Reserved->HeaderBuffer); + } +#endif + NdisRecalculatePacketCounts (Packet); +#ifdef _PNP_POWER + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); +#else + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); +#endif + + } else { + if ((NdisStatus = (*Binding->SendFrameHandler)( + Binding->Adapter, + LocalTarget, + Packet, + PacketLength, + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + // + // Call SendComplete here so it can send broadcasts over other + // Nic's and remove any padding if used. + // + + IpxSendComplete((NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + } + } else { + // + // DISCARD was returned - complete the IRP + // + NdisStatus = STATUS_NETWORK_UNREACHABLE; + + + // + // We need to free the packet and deref the addressfile... + // + + // #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + // #endif + + if (Reserved->OwnedByAddress) { + Reserved->Address->SendPacketInUse = FALSE; + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->Lock); + } + + AddressFile = Reserved->u.SR_DG.AddressFile; + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + + Request = Reserved->u.SR_DG.Request; + REQUEST_STATUS(Request) = NdisStatus; + irpSp = IoGetCurrentIrpStackLocation( Request ); + + // + // If this is a fast send irp, we bypass the file system and + // call the completion routine directly. + // + + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + } else { + IpxCompleteRequest (Request); + } + IpxFreeRequest(Device, Request); + } + + break; + + default: + // + // for all other packet types + // + + if ((Status == STATUS_SUCCESS) && + (Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget))) && + (GET_VALUE(Binding->ReferenceCount) == 2)) { + // + // BUGBUG: IncludedHeaderLength is only used to check for RIP packets (==0) + // so IPX_HEADER size is OK. Should finally remove this parameter. + // + + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == (USHORT)LOOPBACK_NIC_ID) { + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Packet: %lx\n", Packet)); + + // + // Recalculate packet counts here. + // + // NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#if BACK_FILL + + if (Reserved->BackFill) { + // + // Set the Header pointer and chain the first MDL + // + Reserved->Header = (PCHAR)Reserved->HeaderBuffer->MappedSystemVa; + NdisChainBufferAtFront(Packet,(PNDIS_BUFFER)Reserved->HeaderBuffer); + } +#endif + NdisRecalculatePacketCounts (Packet); +#ifdef _PNP_POWER + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); +#else + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); +#endif + + } else { + NdisStatus = IpxSendFrame(LocalTarget, Packet, PacketLength, sizeof(IPX_HEADER)); + + if (NdisStatus != NDIS_STATUS_PENDING) { + IPX_DEBUG (SEND, ("IpxSendFrame status %lx on NICid %lx, packet %lx \n", + NdisStatus, LocalTarget->NicId, Packet)); + goto error_complete; + } + } + } else { + // + // DISCARD was returned - call the upper driver's sendcomplete with error + // + + // + // Else return STATUS_NETWORK_UNREACHABLE + // + + NdisStatus = STATUS_NETWORK_UNREACHABLE; + + error_complete: + + IPX_DEBUG (SEND, ("Calling the SendCompleteHandler of tightly bound driver with status: %lx\n", NdisStatus)); + (*Device->UpperDrivers[Reserved->Identifier].SendCompleteHandler)( + Packet, + NdisStatus); + } + } +} + + diff --git a/private/ntos/tdi/isn/ipx/sources.inc b/private/ntos/tdi/isn/ipx/sources.inc new file mode 100644 index 000000000..9e78c5493 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/sources.inc @@ -0,0 +1,75 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkipx + +TARGETNAME=nwlnkipx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +NTPROFILEINPUT=yes + + + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -DBACK_FILL=1 -DNDIS40=1 -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\adapter.c \ + ..\address.c \ + ..\config.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\ind.c \ + ..\internal.c \ + ..\nwlnkipx.rc \ + ..\mac.c \ + ..\ndis.c \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\rip.c \ + ..\send.c \ + ..\loopback.c \ + ..\rt.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj + +SOURCES_USED=..\sources.inc + + diff --git a/private/ntos/tdi/isn/ipx/up/makefile b/private/ntos/tdi/isn/ipx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/ipx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/ipx/up/nwlnkipx.prf b/private/ntos/tdi/isn/ipx/up/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/up/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isn/ipx/up/sources b/private/ntos/tdi/isn/ipx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isn/ipx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/ipxroute/ipxroute.c b/private/ntos/tdi/isn/ipxroute/ipxroute.c new file mode 100644 index 000000000..d4e327f12 --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/ipxroute.c @@ -0,0 +1,1822 @@ +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Compatible Source Routing Daemon for Windows NT +* +* Module: ipx/route/ipxroute.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +* 02-14-95 RamC Added command line options to support displaying +* Router Table, Router Statistics and SAP information. +* Basically a merge of ipxroute and Stefan's rttest. +***************************************************************************** +* +* Functional Description: +* +* +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "errno.h" +#include "tdi.h" +#include "isnkrnl.h" +#include "ipxrtmsg.h" +#include "..\rip\driver.h" +#include "utils.h" +#include "nwsap.h" + +typedef struct _IPX_ROUTE_ENTRY { + UCHAR Network[4]; + USHORT NicId; + UCHAR NextRouter[6]; + PVOID NdisBindingContext; + USHORT Flags; + USHORT Timer; + UINT Segment; + USHORT TickCount; + USHORT HopCount; + PVOID AlternateRoute[2]; + PVOID NicLinkage[2]; + struct { + PVOID Linkage[2]; + ULONG Reserved[1]; + } PRIVATE; +} IPX_ROUTE_ENTRY, * PIPX_ROUTE_ENTRY; + + + + +IPX_ROUTE_ENTRY rte; + +/** Global Variables **/ + +int sr_def = 0; +int sr_bcast = 0; +int sr_multi = 0; +int boardnum = 0; +int clear = 0; +int config = 0; +int showtable = 0; +int showservers = 0; +int showstats = 0; +int clearstats = 0; +int servertype; +char nodeaddr[6]; /* Node address to remove */ +HANDLE nwlinkfd; +HANDLE isnipxfd; +HANDLE isnripfd; +char ebuffer[128]; + +char nwlinkname[] = "\\Device\\Streams\\NWLinkIpx"; +wchar_t isnipxname[] = L"\\Device\\NwlnkIpx"; +wchar_t isnripname[] = L"\\Device\\Ipxroute"; +char pgmname[] = "IPXROUTE"; +#define SHOW_ALL_SERVERS 0XFFFF + +/** **/ + +#define INVALID_HANDLE (HANDLE)(-1) + +/** Structure to send REMOVE with **/ + +typedef struct rterem { + int rterem_bnum; /* Board number */ + char rterem_node[6]; /* Node to remove */ +} rterem; + +typedef int (_CRTAPI1 * PQSORT_COMPARE)(const void * p0, const void * p1); + +/** Internal Function Prototypes **/ + +extern void print_table(int); +extern void usage(void); +extern void print_version(void); +extern char *print_type(int); +extern int my_strncmp(char *, char *, int); +extern int get_board_num(char *, int *); +extern int get_node_num(char *, char *); +extern int get_server_type(char *, int *); +extern unsigned char get_hex_byte(char *); +extern int get_driver_parms(void); +extern int set_driver_parms(void); +extern int do_strioctl(HANDLE, int, char *, int, int); +extern void remove_address(char *); +extern void clear_table(void); +extern void print_config(void); +extern unsigned long get_emsg(int); +int do_isnipxioctl(HANDLE fd, int cmd, char *datap, int dlen); +unsigned long put_msg(BOOLEAN error, unsigned long MsgNum, ... ); +char *load_msg( unsigned long MsgNum, ... ); +extern void show_router_table(PHANDLE, PIO_STATUS_BLOCK); +extern void show_stats(HANDLE, PIO_STATUS_BLOCK); +extern void clear_stats(HANDLE, PIO_STATUS_BLOCK); +extern void show_servers(int); +extern int _CRTAPI1 CompareServerNames( void * p0, void * p1); +extern int _CRTAPI1 CompareNetNumber( void * p0, void * p1); + +/*page************************************************************* + m a i n + + This is the main routine that gets executed when a NET START + happens. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void _CRTAPI1 main(int argc, char **argv) +{ + char *p; + int todo; + int remove_flag; + UNICODE_STRING FileString; + OBJECT_ATTRIBUTES ObjectAttributes, RouterObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock, RouterIoStatusBlock; + NTSTATUS Status; + + /** **/ + + print_version(); + + /** Open the nwlink driver **/ + + nwlinkfd = s_open(nwlinkname, 0, 0); + + /** Open the isnipx driver **/ + + RtlInitUnicodeString (&FileString, isnipxname); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnipxfd, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + isnipxfd = INVALID_HANDLE; + put_msg (TRUE, MSG_OPEN_FAILED, "\\Device\\NwlnkIpx"); + } + + /** Open the isnrip driver **/ + + RtlInitUnicodeString (&FileString, isnripname); + + InitializeObjectAttributes( + &RouterObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnripfd, + SYNCHRONIZE | GENERIC_READ, + &RouterObjectAttributes, + &RouterIoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT); + + if (!NT_SUCCESS(Status)) { + isnripfd = INVALID_HANDLE; + // don't display any error message, but display the + // message that the IPX router is not started when + // the user actually tries to look at the router. + } + + if ((nwlinkfd == INVALID_HANDLE) && + (isnipxfd == INVALID_HANDLE) && + (isnripfd == INVALID_HANDLE)) + { + exit(1); + } + + + /** Go thru the command line and set it up **/ + + argc--; + argv++; + + /** Parse the command line **/ + + todo = 0; + remove_flag = 0; + while (argc--) { + + /** Uppercase the arg **/ + + p = *argv; + _strupr(p); + + /** Parse the argument **/ + + if (!strcmp(p, "CLEAR")) { + todo = 1; + clear = 1; + } + else if (!strcmp(p, "DEF")) { + todo = 1; + sr_def = 1; + } + else if (!strcmp(p, "GBR")) { + todo = 1; + sr_bcast = 1; + } + else if (!strcmp(p, "MBR")) { + todo = 1; + sr_multi = 1; + } + else if (!strcmp(p, "CONFIG")) { + todo = 1; + config = 1; + } + else if (!my_strncmp(p, "BOARD=", 6)) + get_board_num(p + 6, &boardnum); + else if (!my_strncmp(p, "REMOVE=", 7)) { + remove_flag = 1; + get_node_num(p + 7, nodeaddr); + } + else if (!strcmp(p, "TABLE")) { + todo = 1; + showtable = 1; + } + else if (!strcmp(p, "SERVERS")) { + todo = 1; + showservers = 1; + /** default is to show all server types **/ + servertype = SHOW_ALL_SERVERS; + argv++; + if(argc--) { + p = *argv; + _strupr(p); + if (!my_strncmp(p, "/TYPE=", 6)) { + get_server_type(p + 6, &servertype); + } + else + usage(); + } + /** no more arguments - break out of while lop **/ + else + break; + } + else if (!strcmp(p, "STATS")) { + todo = 1; + /** default is to show the router statistics **/ + showstats = 1; + argv++; + if(argc--) { + p = *argv; + _strupr(p); + if (!strcmp(p, "/CLEAR")) { + clearstats = 1; + showstats = 0; + } + else if (!strcmp(p, "/SHOW")) { + showstats = 1; + } + else + usage; + } + /** no more arguments - break out of while lop **/ + else + break; + } + else + usage(); + + /** Goto the next entry **/ + + argv++; + } + + /** Go update the driver **/ + +#if 0 + printf("todo = %d\n", todo); + printf("remove_flag= %d\n", remove_flag); + printf("Clear flag = %d\n", clear); + printf("Config flag = %d\n", config); + printf("SR_DEF = %d\n", sr_def); + printf("SR_BCAST = %d\n", sr_bcast); + printf("SR_MULTI = %d\n", sr_multi); + printf("Board = %d\n", boardnum); + printf("Node = %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned char)nodeaddr[0], + (unsigned char)nodeaddr[1], + (unsigned char)nodeaddr[2], + (unsigned char)nodeaddr[3], + (unsigned char)nodeaddr[4], + (unsigned char)nodeaddr[5]); +#endif + + /** If we have a remove - go remove it and leave **/ + + if (remove_flag) + remove_address(nodeaddr); /* Does not return */ + + /** If clear - go clear the source routing table **/ + + if (clear) + clear_table(); /* Does not return */ + + /** If config - print out config **/ + + if (config) + print_config(); /* Does not return */ + + /** If showtable - print out routing table **/ + + if (showtable) + show_router_table(&isnripfd, &RouterIoStatusBlock); /* Does not return */ + + /** If showservers - print out selected servers list **/ + + if (showservers) + show_servers(servertype); /* Does not return */ + + /** If showstats - print out statistics **/ + + if (showstats) + show_stats(&isnripfd, &RouterIoStatusBlock); /* Does not return */ + + /** If clearstats - go clear statistics **/ + + if (clearstats) + clear_stats(&isnripfd, &RouterIoStatusBlock); /* Does not return */ + + /** If there is nothing to do - just print out everything **/ + + if (!todo) { + + /** Get the driver parms **/ + + if (get_driver_parms()) { + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(1); + } + + /** Print out the table (Never comes back) **/ + + print_table(1); + } + + /** Go set the parameters **/ + + set_driver_parms(); + + /** Print the table out **/ + + print_table(0); + + /** All Done **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page************************************************************* + p r i n t _ t a b l e + + Print out the status of the source routing. + + Arguments - flag = 0 - Do NOT print the table + 1 - Do print the table (Never returns) + + Returns - Nothing +********************************************************************/ +void print_table(int flag) +{ + /** Print the information **/ + + char * ptype; + + printf("\n"); + ptype = print_type(sr_def); + put_msg (FALSE, MSG_DEFAULT_NODE, ptype); + LocalFree (ptype); + printf("\n"); + + ptype = print_type(sr_bcast); + put_msg (FALSE, MSG_BROADCAST, ptype); + LocalFree (ptype); + printf("\n"); + + ptype = print_type(sr_multi); + put_msg (FALSE, MSG_MULTICAST, ptype); + LocalFree (ptype); + printf("\n"); + + if (!flag) + return; + +#if 0 + printf("\n"); + printf(" Node Address Source Route\n"); + printf("\n"); +#endif + + /** All Done **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page************************************************************* + u s a g e + + Print the usage message. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void usage(void) +{ + put_msg( FALSE, MSG_USAGE, pgmname ); + + /** All Done **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page************************************************************* + p r i n t _ v e r s i o n + + Print the version number + + Arguments - None + + Returns - Nothing +********************************************************************/ +void print_version(void) +{ + printf("\n"); + put_msg (FALSE, MSG_VERSION); + return; +} + +/*page************************************************************* + p r i n t _ t y p e + + Returns the broadcast type given in a string, the caller + must free the string. + + Arguments - 0 = SINGLE ROUTE + Else = ALL ROUTES + + Returns - Nothing +********************************************************************/ +char *print_type(int flag) +{ + if (flag) + return load_msg (MSG_ALL_ROUTE); + else + return load_msg (MSG_SINGLE_ROUTE); + +} + +/*page************************************************************* + m y _ s t r n c m p + + Given a string (p), see if the first len chars are the same + as those of the 2nd string (s). + + Arguments - p = Ptr to first string + s = Ptr to 2nd string + len = Length to check + + Returns - 0 = Matched + Else = Not matched +********************************************************************/ +int my_strncmp(char *p, char *s, int len) +{ + /** **/ + + while (len--) { + if (*p++ != *s++) + return 1; + } + + /** They matched **/ + + return 0; +} + +/*page************************************************************* + g e t _ b o a r d _ n u m + + Get the decimal number from the command line + + Arguments - p = Ptr to the ASCII number + nump = Store the number here + + Returns - 0 = Got the number OK + Else = Bad number +********************************************************************/ +int get_board_num(char *p, int *nump) +{ + *nump = atoi(p); + return 0; +} + +/*page************************************************************* + g e t _ n o d e _ n u m + + Get a node address from the command line + + Arguments - p = Ptr to the ASCII number + nodep = Store the node number here + + Returns - 0 = Got the number OK + Else = Bad number +********************************************************************/ +int get_node_num(char *p, char *nodep) +{ + int i; + unsigned char c1; + unsigned char c2; + + /** **/ + + if (strlen(p) != 12) { + put_msg (TRUE, MSG_INVALID_REMOVE); + exit(1); + } + + /** Get the number **/ + + for (i = 0 ; i < 6 ; i++) { + + /** Get the next 2 digits **/ + + c1 = get_hex_byte(p++); + c2 = get_hex_byte(p++); + + /** If we got a bad number - return error **/ + + if ((c1 == 0xFF) || (c2 == 0xFF)) { + put_msg (TRUE, MSG_INVALID_REMOVE); + exit(1); + } + + /** Set the next byte **/ + + *nodep++ = (c1 << 4) + c2; + } + + /** Return OK **/ + + return 0; +} + +/*page************************************************************* + g e t _ s e r v e r _ t y p e + + Get the decimal number from the command line + + Arguments - p = Ptr to the ASCII number + nump = Store the number here + + Returns - 0 = Got the number OK + Else = Bad number +********************************************************************/ +int get_server_type(char *p, int *nump) +{ + *nump = atoi(p); + return 0; +} + +/*page************************************************************* + g e t _ h e x _ b y t e + + Take 1 ascii hex chars and convert to a hex byte + + Arguments - p = Ptr to the ASCII number + + Returns - The number + (0xFF = Error) +********************************************************************/ +unsigned char get_hex_byte(char *p) +{ + unsigned char c; + + /** Get the char **/ + + c = *(unsigned char *)p; + + /** If 0-9 handle it **/ + + if ((c >= '0') && (c <= '9')) { + c -= '0'; + return c; + } + + /** If A-F handle it **/ + + if ((c >= 'A') && (c <= 'F')) { + c -= ('A' - 10); + return c; + } + + /** This is a bad number **/ + + return 0xFF; +} + + +/*page*************************************************************** + g e t _ d r i v e r _ p a r m s + + Get the parameters from the driver + + Arguments - None + + Returns - 0 = OK + else = Error +********************************************************************/ +int get_driver_parms() +{ + int rc; + int buffer[4]; + + /** Set the board number **/ + + buffer[0] = boardnum; + + /** Get the parms **/ + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRGETPARMS, (char *)buffer, 4*sizeof(int), 0); + if (rc) { + + /** Get the error code **/ + + rc = GetLastError(); + put_msg (TRUE, MSG_BAD_PARAMETERS, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + + /** Return the error **/ + + return rc; + } + } + + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRGETPARMS, (char *)buffer, 4*sizeof(int)); + if (rc) { + + put_msg (TRUE, MSG_BAD_PARAMETERS, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + + /** Return the error **/ + + return rc; + } + } + + /** Get the variables **/ + + sr_def = buffer[1]; + sr_bcast = buffer[2]; + sr_multi = buffer[3]; + + /** Return OK **/ + + return 0; +} + +/*page*************************************************************** + s e t _ d r i v e r _ p a r m s + + Set the parameters for the driver + + Arguments - None + + Returns - 0 = OK + else = Error +********************************************************************/ +int set_driver_parms() +{ + int rc; + int buffer[2]; + + /** Set the DEFAULT parm **/ + + buffer[0] = boardnum; + buffer[1] = sr_def; + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRDEF, (char *)buffer, 2 * sizeof(int), 0); + if (rc) { + rc = GetLastError(); + put_msg (TRUE, MSG_SET_DEFAULT_ERROR, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRDEF, (char *)buffer, 2 * sizeof(int)); + if (rc) { + put_msg (TRUE, MSG_SET_DEFAULT_ERROR, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + + /** Set the BROADCAST parm **/ + + buffer[0] = boardnum; + buffer[1] = sr_bcast; + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRBCAST, (char *)buffer, 2 * sizeof(int), 0); + if (rc) { + rc = GetLastError(); + put_msg (TRUE, MSG_SET_BROADCAST_ERROR, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRBCAST, (char *)buffer, 2 * sizeof(int)); + if (rc) { + put_msg (TRUE, MSG_SET_BROADCAST_ERROR, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + + /** Set the MULTICAST parm **/ + + buffer[0] = boardnum; + buffer[1] = sr_multi; + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRMULTI, (char *)buffer, 2 * sizeof(int), 0); + if (rc) { + rc = GetLastError(); + put_msg (TRUE, MSG_SET_MULTICAST_ERROR, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRMULTI, (char *)buffer, 2 * sizeof(int)); + if (rc) { + put_msg (TRUE, MSG_SET_MULTICAST_ERROR, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + return rc; + } + } + + /** Return OK **/ + + return 0; +} + +/*page*************************************************************** + d o _ s t r i o c t l + + Do a stream ioctl + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + timout = Timeout value + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_strioctl(HANDLE fd, int cmd, char *datap, int dlen, int timout) +{ + int rc; + struct strioctl io; + + /** Fill out the structure **/ + + io.ic_cmd = cmd; + io.ic_dp = datap; + io.ic_len = dlen; + io.ic_timout = timout; + + /** Issue the ioctl **/ + + rc = s_ioctl(fd, I_STR, &io); + + /** All Done **/ + + return rc; +} + +/*page*************************************************************** + r e m o v e _ a d d r e s s + + Remove an address from the source routing table. + + Arguments - nodep = Ptr to node address to remove + + Returns - Does not return +********************************************************************/ +void remove_address(char *nodep) +{ + int rc; + int len; + rterem buf; + + /** Build the area to send down to the driver **/ + + buf.rterem_bnum = boardnum; + memcpy(buf.rterem_node, nodep, 6); + len = sizeof(int) + 6; + + /** Send the ioctl to remove the address **/ + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRREMOVE, (char *)&buf, len, 0); + if (rc) { + rc = GetLastError(); + put_msg (TRUE, MSG_REMOVE_ADDRESS_ERROR, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + } + } + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRREMOVE, (char *)&buf, len); + if (rc) { + put_msg (TRUE, MSG_REMOVE_ADDRESS_ERROR, "nwlnkipx"); + if (rc == EINVAL) { + put_msg (TRUE, MSG_ADDRESS_NOT_FOUND); + } else { + put_msg (TRUE, get_emsg(rc)); + } + } + } + + /** Close up and exit **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page*************************************************************** + c l e a r _ t a b l e + + Clear out the routing table + + Arguments - None + + Returns - Does not return +********************************************************************/ +void clear_table(void) +{ + int rc; + + /** Send the ioctl to clear the table **/ + + if (nwlinkfd != INVALID_HANDLE) { + rc = do_strioctl(nwlinkfd, MIPX_SRCLEAR, (char *)&boardnum, sizeof(int), 0); + if (rc) { + rc= GetLastError(); + put_msg (TRUE, MSG_CLEAR_TABLE_ERROR, "nwlink"); + put_msg (TRUE, get_emsg(rc)); + } + } + if (isnipxfd != INVALID_HANDLE) { + rc = do_isnipxioctl(isnipxfd, MIPX_SRCLEAR, (char *)&boardnum, sizeof(int)); + if (rc) { + put_msg (TRUE, MSG_CLEAR_TABLE_ERROR, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + } + } + + /** Close up and exit **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +typedef struct _ISN_ACTION_GET_DETAILS { + USHORT NicId; // passed by caller + BOOLEAN BindingSet; // returns TRUE if in set + UCHAR Type; // 1 = lan, 2 = up wan, 3 = down wan + ULONG FrameType; // returns 0 through 3 + ULONG NetworkNumber; // returns virtual net if NicId is 0 + UCHAR Node[6]; // adapter's MAC address. + WCHAR AdapterName[64]; // terminated with Unicode NULL +} ISN_ACTION_GET_DETAILS, *PISN_ACTION_GET_DETAILS; + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + +/*page*************************************************************** + p r i n t _ c o n f i g + + Prints out the current config + + Arguments - None + + Returns - Does not return +********************************************************************/ +void print_config(void) +{ + int rc; + int nicid, nicidcount; + int showlegend = 0; + char nicidbuf[6]; + char network[9]; + char * frametype; + char node[13]; + char special[2]; + char adaptername[64]; + ISN_ACTION_GET_DETAILS getdetails; + + + if (isnipxfd != INVALID_HANDLE) { + + /** First query nicid 0 **/ + + getdetails.NicId = 0; + + rc = do_isnipxioctl(isnipxfd, MIPX_CONFIG, (char *)&getdetails, sizeof(getdetails)); + if (rc) { + put_msg (TRUE, MSG_QUERY_CONFIG_ERROR, "nwlnkipx"); + put_msg (TRUE, get_emsg(rc)); + goto errorexit; + } + + printf("\n"); + + if (getdetails.NetworkNumber != 0) { + sprintf (network, "%.8x", REORDER_ULONG(getdetails.NetworkNumber)); + put_msg (FALSE, MSG_SHOW_INTERNAL_NET, network); + } + + // + // The NicId 0 query returns the total number. + // + + nicidcount = getdetails.NicId; + + for (nicid = 1; nicid <= nicidcount; nicid++) { + + getdetails.NicId = nicid; + + rc = do_isnipxioctl(isnipxfd, MIPX_CONFIG, (char *)&getdetails, sizeof(getdetails)); + if (rc) { + continue; + } + + sprintf (nicidbuf, "%d", nicid); + sprintf (network, "%.8x", REORDER_ULONG(getdetails.NetworkNumber)); + + switch (getdetails.FrameType) { + case 0: frametype = load_msg (MSG_ETHERNET_II); break; + case 1: frametype = load_msg (MSG_802_3); break; + case 2: frametype = load_msg (MSG_802_2); break; + case 3: frametype = load_msg (MSG_SNAP); break; + case 4: frametype = load_msg (MSG_ARCNET); break; + default: frametype = load_msg (MSG_UNKNOWN); break; + } + sprintf (adaptername, "%ws", getdetails.AdapterName); + + sprintf (node, "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + getdetails.Node[0], + getdetails.Node[1], + getdetails.Node[2], + getdetails.Node[3], + getdetails.Node[4], + getdetails.Node[5]); + + special[1] = '\0'; + if (getdetails.BindingSet) { + special[0] = '*'; + showlegend |= 1; + } else if (getdetails.Type == 2) { + special[0] = '+'; + showlegend |= 2; + } else if (getdetails.Type == 3) { + special[0] = '-'; + showlegend |= 4; + } else { + special[0] = '\0'; + } + + put_msg (FALSE, MSG_SHOW_NET_NUMBER, + nicidbuf, + network, + frametype, + adaptername, + node, + special); + + LocalFree (frametype); + + } + + if (showlegend) { + if (showlegend & 1) { + put_msg (FALSE, MSG_LEGEND_BINDING_SET); + } + if (showlegend & 2) { + put_msg (FALSE, MSG_LEGEND_ACTIVE_WAN); + } + if (showlegend & 4) { + put_msg (FALSE, MSG_LEGEND_DOWN_WAN); + } + printf("\n"); + } + + } + +errorexit: + + /** Close up and exit **/ + + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page*************************************************************** + g e t _ e m s g + + Get an error message for an error + + Arguments - None + + Returns - Does not return +********************************************************************/ +unsigned long get_emsg(int rc) +{ + /** + We have 3 defined error codes that can come back. + + 1 - EINVAL means that we sent down parameters wrong + (SHOULD NEVER HAPPEN) + + 2 - ERANGE means that the board number is invalid + (CAN HAPPEN IF USER ENTERS BAD BOARD) + + 3 - ENOENT means that on remove - the address given + is not in the source routing table. + **/ + + switch (rc) { + + case EINVAL: + return MSG_INTERNAL_ERROR; + + case ERANGE: + return MSG_INVALID_BOARD; + + case ENOENT: + return MSG_ADDRESS_NOT_FOUND; + + default: + return MSG_UNKNOWN_ERROR; + } + +} + +/*page*************************************************************** + d o _ i s n i p x i o c t l + + Do the equivalent of a stream ioctl to isnipx + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_isnipxioctl(HANDLE fd, int cmd, char *datap, int dlen) +{ + NTSTATUS Status; + UCHAR buffer[sizeof(NWLINK_ACTION) + sizeof(ISN_ACTION_GET_DETAILS) - 1]; + PNWLINK_ACTION action; + IO_STATUS_BLOCK IoStatusBlock; + int rc; + + /** Fill out the structure **/ + + action = (PNWLINK_ACTION)buffer; + + action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; + action->OptionType = NWLINK_OPTION_CONTROL; + action->BufferLength = sizeof(ULONG) + dlen; + action->Option = cmd; + RtlMoveMemory(action->Data, datap, dlen); + + /** Issue the ioctl **/ + + Status = NtDeviceIoControlFile( + fd, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_TDI_ACTION, + NULL, + 0, + action, + FIELD_OFFSET(NWLINK_ACTION,Data) + dlen); + + if (Status != STATUS_SUCCESS) { + if (Status == STATUS_INVALID_PARAMETER) { + rc = ERANGE; + } else { + rc = EINVAL; + } + } else { + if (dlen > 0) { + RtlMoveMemory (datap, action->Data, dlen); + } + rc = 0; + } + + return rc; + +} + + +//***************************************************************************** +// +// Name: put_msg +// +// Description: Reads a message resource, formats it in the current language +// and displays the message. +// +// NOTE: This routine was stolen from net\sockets\tcpcmd\common2\util.c. +// +// Parameters: error - TRUE if this is an error message. +// unsigned long MsgNum: ID of the message resource. +// +// Returns: unsigned long: number of characters displayed. +// +// History: +// 01/05/93 JayPh Created. +// +//***************************************************************************** + +unsigned long put_msg(BOOLEAN error, unsigned long MsgNum, ... ) +{ + unsigned long msglen; + char *vp; + va_list arglist; + + va_start( arglist, MsgNum ); + msglen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_HMODULE, + NULL, + MsgNum, + 0L, // Default country ID. + (LPTSTR)&vp, + 0, + &arglist ); + if ( msglen == 0 ) + { + return ( 0 ); + } + + fprintf( error ? stderr : stdout, "%s", vp ); + LocalFree( vp ); + + return ( msglen ); +} + + +//***************************************************************************** +// +// Name: load_msg +// +// Description: Reads and formats a message resource and returns a pointer +// to the buffer containing the formatted message. It is the +// responsibility of the caller to free the buffer. +// +// NOTE: This routine was stolen from net\sockets\tcpcmd\common2\util.c. +// +// Parameters: unsigned long MsgNum: ID of the message resource. +// +// Returns: char *: pointer to the message buffer, NULL if error. +// +// History: +// 01/05/93 JayPh Created. +// +//***************************************************************************** + +char *load_msg( unsigned long MsgNum, ... ) +{ + unsigned long msglen; + char *vp; + va_list arglist; + + va_start( arglist, MsgNum ); + msglen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_HMODULE, + NULL, + MsgNum, + 0L, // Default country ID. + (LPTSTR)&vp, + 0, + &arglist ); + if ( msglen == 0 ) + { + return(0); + } + + return ( vp ); +} + + +#define MAX_NETWORK_INTERFACES 255 + +typedef struct router_info +{ + ULONG NetNumber; + USHORT TickCount; + USHORT HopCount; + USHORT NicId; + UCHAR InterfaceNumber[10]; +} ROUTER_INFO, *PROUTER_INFO; + +/*page*************************************************************** + s h o w _ r o u t e r _ t a b l e + + Display the IPX routing table + + Arguments - FileHandle = Router File Handle + IoStatusBlock = Device IO Status Block + + Returns - Does not return +********************************************************************/ +VOID +show_router_table( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock +) +{ + SHOW_NIC_INFO nis[MAX_NETWORK_INTERFACES]; + ULONG NetNumber; + char InterfaceNumber[10]; + NTSTATUS Status; + USHORT index, i, NumEntries, count; + char router_entry[128]; + char buffer[32]; + PROUTER_INFO RouterInfo = NULL; + + if (*FileHandle == INVALID_HANDLE) { + put_msg(TRUE, MSG_IPXROUTER_NOT_STARTED ); + goto exit_show_table; + } + /** First get the Network numbers for all interfaces **/ + + index = 0; + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SHOWNICINFO, // IoControlCode + &index, // Input Buffer + sizeof(USHORT), // Input Buffer Length + &nis[index], // Output Buffer + sizeof(SHOW_NIC_INFO)); // Output Buffer Length + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + break; + } + + index ++; + + if(Status != STATUS_SUCCESS) { + sprintf(buffer, "%x", Status); + put_msg(TRUE, MSG_SHOWSTATS_FAILED, buffer); + goto exit_show_table; + } + + if (index >= MAX_NETWORK_INTERFACES) { + // break out of this loop if there are more than 255 network + // interfaces because we only have storage for 255. + + break; + } + + } + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SNAPROUTES, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + if (IoStatusBlock->Status != STATUS_SUCCESS) { + sprintf(buffer, "%x", Status); + put_msg(TRUE, MSG_SNAPROUTES_FAILED, buffer); + goto exit_show_table; + } + + // first determine the number of router table entries to + // allocate sufficient storage + + NumEntries = 0; + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_GETNEXTROUTE, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + &rte, // Output Buffer + sizeof(IPX_ROUTE_ENTRY)); // Output Buffer Length + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + break; + } + + if(Status != STATUS_SUCCESS) { + sprintf(buffer,"%x",Status); + put_msg(TRUE, MSG_GETNEXTROUTE_FAILED, buffer); + goto exit_show_table; + } + + NumEntries ++; + } + + RouterInfo = (PROUTER_INFO) LocalAlloc(LPTR, sizeof(ROUTER_INFO) * NumEntries); + if(!RouterInfo) { + put_msg(FALSE, MSG_INSUFFICIENT_MEMORY); + goto exit_show_table; + } + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SNAPROUTES, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + if (IoStatusBlock->Status != STATUS_SUCCESS) { + sprintf(buffer, "%x", Status); + put_msg(TRUE, MSG_SNAPROUTES_FAILED, buffer); + goto exit_show_table; + } + + index = 0; + + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_GETNEXTROUTE, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + &rte, // Output Buffer + sizeof(IPX_ROUTE_ENTRY)); // Output Buffer Length + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + break; + } + + if(Status != STATUS_SUCCESS) { + sprintf(buffer,"%x",Status); + put_msg(TRUE, MSG_GETNEXTROUTE_FAILED, buffer); + goto exit_show_table; + } + + // make sure we don't exceed the number of entries + if (index > NumEntries) { + break; + } + + // get net nr in "on the wire" order + + GETLONG2ULONG(&(RouterInfo[index].NetNumber), rte.Network); + + // find out the matching Network number based on NIC ID + for(i=0; i < MAX_NETWORK_INTERFACES; i++) { + if(rte.NicId == nis[i].NicId) { + sprintf(RouterInfo[index].InterfaceNumber, "%.2x%.2x%.2x%.2x", + nis[i].Network[0], + nis[i].Network[1], + nis[i].Network[2], + nis[i].Network[3]); + break; + } + } + RouterInfo[index].TickCount = rte.TickCount; + RouterInfo[index].HopCount = rte.HopCount; + RouterInfo[index].NicId = rte.NicId; + + index++; + } + + // Now sort the entries by net number + qsort( (void*) RouterInfo, + NumEntries, + sizeof(ROUTER_INFO), + (PQSORT_COMPARE)CompareNetNumber ); + + put_msg(FALSE, MSG_ROUTER_TABLE_HEADER); + for(index =0, count = 0; index < NumEntries; index++, count++) + { + if (count > 50) { + count = 0; + // display router table header every 25 entries + // to make reading the table easier. + put_msg(FALSE, MSG_ROUTER_TABLE_HEADER); + } + printf("%.8x %6d %2d %-16s %d\n", + RouterInfo[index].NetNumber, + RouterInfo[index].TickCount, + RouterInfo[index].HopCount, + RouterInfo[index].InterfaceNumber, + RouterInfo[index].NicId ); + } + /** Close up and exit **/ +exit_show_table: + if (RouterInfo) LocalFree(RouterInfo); + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +int _CRTAPI1 CompareNetNumber( void * p0, void * p1) +{ + PROUTER_INFO pLeft = (PROUTER_INFO) p0; + PROUTER_INFO pRight = (PROUTER_INFO) p1; + + if(pLeft->NetNumber == pRight->NetNumber) + return(0); + if(pLeft->NetNumber > pRight->NetNumber) + return(1); + else + return(-1); +} + +PUCHAR DeviceType[2] = { "LAN", "WAN" }; +PUCHAR NicState[4] = { "CLOSED", "CLOSING", "ACTIVE", "PENDING_OPEN" }; + +/*page*************************************************************** + s h o w _ s t a t s + + Displays IPX internal routing statistics + + Arguments - FileHandle = Router File Handle + IoStatusBlock = Device IO Status Block + + Returns - Does not return +********************************************************************/ +VOID +show_stats( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock +) +{ + SHOW_NIC_INFO nis; + USHORT index, i; + char NicId[4]; + char NetworkNumber[10]; + char RipRcvd[32], RipSent[32]; + char RoutedRcvd[32], RoutedSent[32]; + char Type20Rcvd[32], Type20Sent[32]; + char BadRcvd[32]; + char buffer[32]; + + NTSTATUS Status; + + if (*FileHandle == INVALID_HANDLE) { + put_msg(TRUE, MSG_IPXROUTER_NOT_STARTED ); + goto end_stats; + } + + index = 0; + + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SHOWNICINFO, // IoControlCode + &index, // Input Buffer + sizeof(USHORT), // Input Buffer Length + &nis, // Output Buffer + sizeof(nis)); // Output Buffer Length + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + goto end_stats; + } + + index ++; + + if(Status != STATUS_SUCCESS) { + sprintf(buffer, "%x", Status); + put_msg(TRUE, MSG_SHOWSTATS_FAILED, buffer); + goto end_stats; + } + + sprintf(NicId, "%d", nis.NicId); + + sprintf(NetworkNumber, + "%.2x%.2x%.2x%.2x", + nis.Network[0], + nis.Network[1], + nis.Network[2], + nis.Network[3]); + + sprintf(RipRcvd, "%-8d", nis.StatRipReceived); + + sprintf(RipSent, "%-8d", nis.StatRipSent); + + sprintf(RoutedRcvd, "%-8d", nis.StatRoutedReceived); + + sprintf(RoutedSent, "%-8d", nis.StatRoutedSent); + + sprintf(Type20Rcvd, "%-8d", nis.StatType20Received); + + sprintf(Type20Sent, "%-8d", nis.StatType20Sent); + + sprintf(BadRcvd, "%-8d", nis.StatBadReceived); + + put_msg(FALSE, + MSG_SHOW_STATISTICS, + NicId, + NetworkNumber, + RipRcvd, + RipSent, + Type20Rcvd, + Type20Sent, + RoutedRcvd, + RoutedSent, + BadRcvd); + + } + + /** Close up and exit **/ + +end_stats: + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +/*page*************************************************************** + c l e a r _ s t a t s + + Clears the IPX internal routing statistics + + Arguments - FileHandle = Router File Handle + IoStatusBlock = Device IO Status Block + + Returns - Does not return +********************************************************************/ +VOID +clear_stats( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock +) +{ + NTSTATUS Status; + char buffer[32]; + + if (*FileHandle == INVALID_HANDLE) { + put_msg(TRUE, MSG_IPXROUTER_NOT_STARTED ); + goto end_clearstats; + } + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_ZERONICSTATISTICS, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + if(Status != STATUS_SUCCESS) { + sprintf(buffer, "%x", Status); + put_msg(TRUE, MSG_CLEAR_STATS_FAILED, buffer); + } + /** Close up and exit **/ +end_clearstats: + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +typedef struct server_info +{ + USHORT ObjectType; + UCHAR ObjectName[100]; + UCHAR IpxAddress[12]; +} SERVER_INFO, *PSERVER_INFO; + +/*page*************************************************************** + s h o w _ s e r v e r s + + Display the servers from the SAP table + + Arguments - servertype = Type of servers to display + Defaults to show all server types + + Returns - Does not return +********************************************************************/ +VOID +show_servers(int servertype) +{ + INT rc; + ULONG ObjectID = 0xFFFFFFFF; + UCHAR ObjectName[100]; + USHORT ObjectType; + USHORT ScanType = servertype; + UCHAR IpxAddress[12]; + USHORT i; + USHORT index, count, NumServers; + + PSERVER_INFO ServerInfo = NULL; + + if(rc = SapLibInit() != SAPRETURN_SUCCESS) { + put_msg(TRUE, MSG_SAP_NOT_STARTED); + goto show_servers_end; + } + + memset(&ObjectName, 0, 100); + + // find out how many servers are there so that we can allocate + // sufficient storage + NumServers = 0; + + while((rc = SapScanObject(&ObjectID, + ObjectName, + &ObjectType, + ScanType)) == SAPRETURN_SUCCESS) + { + NumServers++; + } + + ServerInfo = (PSERVER_INFO) LocalAlloc(LPTR, sizeof(SERVER_INFO) * NumServers); + if(!ServerInfo) + { + put_msg(FALSE, MSG_INSUFFICIENT_MEMORY); + goto show_servers_end; + } + + index = 0; + ObjectID = 0xFFFFFFFF; + + while((rc = SapScanObject(&ObjectID, + ObjectName, + &ObjectType, + ScanType)) == SAPRETURN_SUCCESS) + { + if (index > NumServers) { + break; + } + + // get object address + SapGetObjectName(ObjectID, + ObjectName, + &ObjectType, + IpxAddress); + + ServerInfo[index].ObjectType = ObjectType; + strcpy(ServerInfo[index].ObjectName, ObjectName); + CopyMemory(ServerInfo[index].IpxAddress, IpxAddress, 12); + + index++; + } + + // Now sort the entries by server name + qsort( (void*) ServerInfo, + NumServers, + sizeof(SERVER_INFO), + (PQSORT_COMPARE)CompareServerNames ); + + if(servertype == SHOW_ALL_SERVERS) + put_msg(FALSE, MSG_SHOW_ALL_SERVERS_HEADER); + else + put_msg(FALSE, MSG_SHOW_SPECIFIC_SERVER_HEADER); + + for(index = 0, count = 0; index < NumServers; index++, count++) + { + if (count > 50) { + // write the table header for every 50 entries + // to make this more readable. + count = 0; + + if(servertype == SHOW_ALL_SERVERS) + put_msg(FALSE, MSG_SHOW_ALL_SERVERS_HEADER); + else + put_msg(FALSE, MSG_SHOW_SPECIFIC_SERVER_HEADER); + } + + for(i=0; i<4; i++) { + printf("%.2x", ServerInfo[index].IpxAddress[i]); + } + printf("."); + for(i=4; i<10; i++) { + printf("%.2x", ServerInfo[index].IpxAddress[i]); + } + + if(servertype == SHOW_ALL_SERVERS) { + printf(" %-6d", ServerInfo[index].ObjectType); + } + + printf(" %s\n", ServerInfo[index].ObjectName); + } + + /** Close up and exit **/ +show_servers_end: + if (ServerInfo) LocalFree(ServerInfo); + if (isnipxfd != INVALID_HANDLE) NtClose(isnipxfd); + if (nwlinkfd != INVALID_HANDLE) NtClose(nwlinkfd); + if (isnripfd != INVALID_HANDLE) NtClose(isnripfd); + exit(0); +} + +int _CRTAPI1 CompareServerNames( void * p0, void * p1) +{ + PSERVER_INFO pLeft = (PSERVER_INFO) p0; + PSERVER_INFO pRight = (PSERVER_INFO) p1; + + return(strcmp(pLeft->ObjectName, pRight->ObjectName)); +} + diff --git a/private/ntos/tdi/isn/ipxroute/ipxroute.rc b/private/ntos/tdi/isn/ipxroute/ipxroute.rc new file mode 100644 index 000000000..e56b3d767 --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/ipxroute.rc @@ -0,0 +1,13 @@ +#include + +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLink Source Routing Application" +#define VER_INTERNALNAME_STR "ipxroute.exe" +#define VER_ORIGINALFILENAME_STR "ipxroute.exe" + +#include "common.ver" + +1 11 MSG00001.BIN diff --git a/private/ntos/tdi/isn/ipxroute/ipxrtmsg.mc b/private/ntos/tdi/isn/ipxroute/ipxrtmsg.mc new file mode 100644 index 000000000..47203d60e --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/ipxrtmsg.mc @@ -0,0 +1,252 @@ +; //*************************************************************************** +; // +; // Name: ipxroute.mc +; // +; // Description: Message file for ipxroute.exe +; // +; // History: +; // 07/14/94 AdamBa Created. +; // +; //*************************************************************************** +; +; //*************************************************************************** +; // +; // Copyright (c) 1994 by Microsoft Corp. All rights reserved. +; // +; //*************************************************************************** + +MessageId=10000 SymbolicName=MSG_USAGE +Language=English + +Display and modify information about the routing tables +used by IPX. + +IPX Routing Options +------------------- + +%1 servers [/type=xxxx] +%1 stats [/show] [/clear] +%1 table + + servers Displays the SAP table for the specified + server type. Server type is an integer value. + For example use %1 servers /type=4 to display + all file servers. If no type is specified, + servers of all types are shown. The displayed + list is sorted by server name. + + stats Displays or clears IPX router interface statistics. + If no option is specified, statistics are shown. + To clear the statistics specify /clear. + + table Displays the IPX routing table. The displayed + list is sorted by network number. + +Source Routing Options +---------------------- + +%1 board=n clear def gbr mbr remove=xxxxxxxxxxxx +%1 config + + board=n Specify the board number to check. + clear Clear the source routing table. + def Send packets that are destined for an + unknown address to the ALL ROUTES broadcast + (Default is SINGLE ROUTE broadcast). + gbr Send packets that are destined for the + broadcast address (FFFF FFFF FFFF) to the + ALL ROUTES broadcast + (Default is SINGLE ROUTE broadcast). + mbr Send packets that are destined for a + multicast address (C000 xxxx xxxx) to the + ALL ROUTES broadcast + (Default is SINGLE ROUTE broadcast). + remove=xxxx Remove the given mac address from the + source routing table. + + config Displays information on all the bindings + that IPX is configured for. + +All parameters should be separated by spaces. +. +MessageId=10001 SymbolicName=MSG_INTERNAL_ERROR +Language=English +Invalid parameters (internal error). +. +MessageId=10002 SymbolicName=MSG_INVALID_BOARD +Language=English +Invalid board number. +. +MessageId=10003 SymbolicName=MSG_ADDRESS_NOT_FOUND +Language=English +Address not in table. +. +MessageId=10004 SymbolicName=MSG_UNKNOWN_ERROR +Language=English +Unknown error. +. +MessageId=10005 SymbolicName=MSG_OPEN_FAILED +Language=English +Unable to open transport %1. +. +MessageId=10006 SymbolicName=MSG_VERSION +Language=English +NWLink IPX Routing and Source Routing Control Program v2.00 +. +MessageId=10007 SymbolicName=MSG_DEFAULT_NODE +Language=English + DEFault Node (Unknown) Addresses are sent %1 +. +MessageId=10008 SymbolicName=MSG_BROADCAST +Language=English + Broadcast (FFFF FFFF FFFF) Addresses are sent %1 +. +MessageId=10009 SymbolicName=MSG_MULTICAST +Language=English + Multicast (C000 xxxx xxxx) Addresses are sent %1 +. +MessageId=10010 SymbolicName=MSG_ALL_ROUTE +Language=English +ALL ROUTE BROADCAST%0 +. +MessageId=10011 SymbolicName=MSG_SINGLE_ROUTE +Language=English +SINGLE ROUTE BROADCAST%0 +. +MessageId=10012 SymbolicName=MSG_INVALID_REMOVE +Language=English +Invalid value for the remove node number. +. +MessageId=10013 SymbolicName=MSG_BAD_PARAMETERS +Language=English +Error getting parameters from IPX (%1): %0 +. +MessageId=10014 SymbolicName=MSG_SET_DEFAULT_ERROR +Language=English +Error setting DEFAULT flag to IPX (%1): %0 +. +MessageId=10015 SymbolicName=MSG_SET_BROADCAST_ERROR +Language=English +Error setting BROADCAST flag to IPX (%1): %0 +. +MessageId=10016 SymbolicName=MSG_SET_MULTICAST_ERROR +Language=English +Error setting MULTICAST flag to IPX (%1): %0 +. +MessageId=10017 SymbolicName=MSG_REMOVE_ADDRESS_ERROR +Language=English +Error removing address from source routing table (%1): %0 +. +MessageId=10018 SymbolicName=MSG_CLEAR_TABLE_ERROR +Language=English +Error clearing source routing table (%1): %0 +. +MessageId=10019 SymbolicName=MSG_QUERY_CONFIG_ERROR +Language=English +Error querying config (%1): %0 +. +MessageId=10020 SymbolicName=MSG_SHOW_INTERNAL_NET +Language=English +IPX internal network number %1 +. +MessageId=10021 SymbolicName=MSG_SHOW_NET_NUMBER +Language=English +net %1: network number %2, frame type %3, device %4 (%5)%6 +. +MessageId=10022 SymbolicName=MSG_ETHERNET_II +Language=English +ethernet ii%0 +. +MessageId=10023 SymbolicName=MSG_802_3 +Language=English +802.3%0 +. +MessageId=10024 SymbolicName=MSG_802_2 +Language=English +802.2%0 +. +MessageId=10025 SymbolicName=MSG_SNAP +Language=English +snap%0 +. +MessageId=10026 SymbolicName=MSG_ARCNET +Language=English +arcnet%0 +. +MessageId=10027 SymbolicName=MSG_UNKNOWN +Language=English +unknown%0 +. +MessageId=10028 SymbolicName=MSG_LEGEND_BINDING_SET +Language=English +* binding set member %0 +. +MessageId=10029 SymbolicName=MSG_LEGEND_ACTIVE_WAN +Language=English ++ active wan line %0 +. +MessageId=10030 SymbolicName=MSG_LEGEND_DOWN_WAN +Language=English +- down wan line %0 +. +MessageId=10031 SymbolicName=MSG_ROUTER_TABLE_HEADER +Language=English + +Net Number Ticks Hops Interface Net Number Interface ID +------------------------------------------------------------------------- +. +MessageId=10032 SymbolicName=MSG_SNAPROUTES_FAILED +Language=English +Ioctl snap routes to IPX router failed with error %1. +. +MessageId=10033 SymbolicName=MSG_GETNEXTROUTE_FAILED +Language=English +Failed to get the next route from the IPX router with error %1. +. +MessageId=10034 SymbolicName=MSG_SHOWSTATS_FAILED +Language=English +Failed to get the internal router statistics with error %1. +. +MessageId=10035 SymbolicName=MSG_SHOW_STATISTICS +Language=English + +Network Interface ID = %1 +Network Interface Number = %2 +RIP packets: received = %3 sent = %4 +Type 20 packets: received = %5 sent = %6 +Forwarded packets: received = %7 sent = %8 +Discarded packets: received = %9 +. +MessageId=10036 SymbolicName=MSG_CLEAR_STATS_FAILED +Language=English +Ioctl to clear statistics failed with error %1. +. +MessageId=10037 SymbolicName=MSG_SHOW_ALL_SERVERS_HEADER +Language=English + +IPX Address Server Type Server Name +------------------------------------------------------- +. +MessageId=10038 SymbolicName=MSG_SHOW_SPECIFIC_SERVER_HEADER +Language=English + +IPX Address Server Name +---------------------------------------- +. +MessageId=10039 SymbolicName=MSG_IPXROUTER_NOT_STARTED +Language=English + +Unable to set/get information from the IPX router. +Make sure that the IPX router service (NWLNKRIP) is started. +. +MessageId=10040 SymbolicName=MSG_SAP_NOT_STARTED +Language=English + +Unable to get information from the SAP agent. +Make sure that the SAP agent service (NWSAPAGENT) is started. +. +MessageId=10041 SymbolicName=MSG_INSUFFICIENT_MEMORY +Language=English +Cannot allocate sufficient memory. Close other applications and try this operation. +. + \ No newline at end of file diff --git a/private/ntos/tdi/isn/ipxroute/makefile b/private/ntos/tdi/isn/ipxroute/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/ipxroute/makefile.inc b/private/ntos/tdi/isn/ipxroute/makefile.inc new file mode 100644 index 000000000..ce99ed09d --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/makefile.inc @@ -0,0 +1,13 @@ +IPX_BASENAME = $(TARGETNAME).exe + +NLINKEXENAME = obj\$(TARGET_DIRECTORY)\$(IPX_BASENAME) + +NLINKBINNAME = $(BASEDIR)\public\sdk\lib\$(TARGET_DIRECTORY)\$(IPX_BASENAME) + +$(NLINKBINNAME): $(NLINKEXENAME) + copy $(NLINKEXENAME) $(NLINKBINNAME) + +ipxroute.rc: ipxrtmsg.rc msg00001.bin + +ipxrtmsg.h ipxrtmsg.rc msg00001.bin: ipxrtmsg.mc + mc -v ipxrtmsg.mc diff --git a/private/ntos/tdi/isn/ipxroute/sources b/private/ntos/tdi/isn/ipxroute/sources new file mode 100644 index 000000000..de1fcda6b --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/sources @@ -0,0 +1,36 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc. + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=ipxroute + +TARGETNAME=ipxroute +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=UMAPPL_NOLIB + +USE_CRTDLL=1 + +C_DEFINES=$(C_DEFINES) + +!IF 0 +INCLUDES=..\h;..\..\..\..\..\inc;..\..\..\..\inc;..\..\..\inc +!ELSE +INCLUDES=..\h;$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;$(BASEDIR)\private\ntos\streams\inc;$(BASEDIR)\private\net\inc +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= ipxroute.rc + +UMTYPE=console +UMAPPL=$(TARGETNAME) +UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\public\sdk\lib\*\winstrm.lib \ + $(BASEDIR)\public\sdk\lib\*\nwsaplib.lib + +UMRES=obj\*\ipxroute.res + +NTTARGETFILE0=ipxrtmsg.h ipxrtmsg.mc ipxroute.rc diff --git a/private/ntos/tdi/isn/ipxroute/utils.h b/private/ntos/tdi/isn/ipxroute/utils.h new file mode 100644 index 000000000..afacd2a35 --- /dev/null +++ b/private/ntos/tdi/isn/ipxroute/utils.h @@ -0,0 +1,55 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: utils.h +// +// Description: Contains miscellaneous utilities +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _UTILS_ +#define _UTILS_ + +/* + * The following macros deal with on-the-wire short and long values + * + * On the wire format is big-endian i.e. a long value of 0x01020304 is + * represented as 01 02 03 04. + * Similarly a short value of 0x0102 is represented as 01 02. + * + * The host format is not assumed since it will vary from processor to + * processor. + */ + +// Get a short from on-the-wire format to a USHORT in the host format +#define GETSHORT2USHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 8) + \ + (*((PUCHAR)(SrcPtr)+1) )) + +// Get a long from on-the-wire format to a ULONG in the host format +#define GETLONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 24) + \ + (*((PUCHAR)(SrcPtr)+1) << 16) + \ + (*((PUCHAR)(SrcPtr)+2) << 8) + \ + (*((PUCHAR)(SrcPtr)+3) )) + +// Put a USHORT from the host format to a short to on-the-wire format +#define PUTUSHORT2SHORT(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((USHORT)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR)(Src) + +// Put a ULONG from the host format to an array of 4 UCHARs on-the-wire format +#define PUTULONG2LONG(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((ULONG)(Src) >> 24), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR) ((ULONG)(Src) >> 16), \ + *((PUCHAR)(DstPtr)+2) = (UCHAR) ((ULONG)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+3) = (UCHAR) (Src) + +#endif // _UTILS_ diff --git a/private/ntos/tdi/isn/isnext/cteext.c b/private/ntos/tdi/isn/isnext/cteext.c new file mode 100644 index 000000000..6a790e89c --- /dev/null +++ b/private/ntos/tdi/isn/isnext/cteext.c @@ -0,0 +1,155 @@ +#include "precomp.h" +#pragma hdrstop + + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Mdl +#define _objAddr MdlToDump +#define _objType MDL + +VOID +DumpMdlChain +( + ULONG _objAddr, + VERBOSITY Verbosity +) +{ + _objType _obj; + ULONG result; + + if ( !ReadMemory( _objAddr, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read MDL structure\n", _objAddr ); + return; + } + + PrintStartStruct(); + PrintPtr( Next ); + PrintUShort( Size ); + PrintXUShort( MdlFlags ); + PrintPtr( Process ); + PrintPtr( MappedSystemVa ); + PrintPtr( StartVa ); + PrintULong( ByteCount ); + PrintULong( ByteOffset ); + return; +} + +VOID +DumpCTELock +( + ULONG LockToDump, + VERBOSITY Verbosity +) +{ + CTELock Lock; + CTELock *pLock; + ULONG result; + + pLock = ( CTELock * )LockToDump; + + if ( !ReadMemory( LockToDump, + &Lock, + sizeof( Lock ), + &result )) + { + dprintf("%08lx: Could not read CTELock structure\n", LockToDump ); + return; + } + + dprintf( "{ Lock = %d }", Lock ); + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Timer +#define _objAddr pItem +#define _objType CTETimer + +VOID +DumpCTETimer +( + ULONG TimerToDump, + VERBOSITY Verbosity +) +{ + CTETimer Timer; + CTETimer *prTimer; + ULONG result; + + prTimer = ( CTETimer * )TimerToDump; + + if ( !ReadMemory( TimerToDump, + &Timer, + sizeof( Timer ), + &result )) + { + dprintf("%08lx: Could not read CTETimer structure\n", TimerToDump ); + return; + } + + PrintStart; + PrintULong( t_running ); + PrintLock( t_lock ); + PrintSymbolPtr( t_handler ); + PrintXULong( t_arg ); + // DPC + // KTIMER + PrintEnd; + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj QItem +#define _objAddr prQItem +#define _objType WORK_QUEUE_ITEM + +VOID +DumpWorkQueueItem +( + ULONG ItemToDump, + VERBOSITY Verbosity +) +{ + _objType _obj; + _objType *_objAddr; + ULONG result; + + _objAddr = ( _objType * )ItemToDump; + + if ( !ReadMemory( ItemToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + ItemToDump, + "WORK_QUEUE_ITEM" ); + return; + } + + PrintStart; + PrintLL( List ); + PrintSymbolPtr( WorkerRoutine ); + PrintXULong( Parameter ); + PrintEnd; + return; +} diff --git a/private/ntos/tdi/isn/isnext/cteext.h b/private/ntos/tdi/isn/isnext/cteext.h new file mode 100644 index 000000000..dce46b350 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/cteext.h @@ -0,0 +1,33 @@ +#if !defined( _INCLUDED_CTEEXT_H_ ) +#define _INCLUDED_CTEEXT_H_ + +VOID +DumpCTELock +( + ULONG LockToDump, + VERBOSITY Verbosity +); + +VOID +DumpCTETimer +( + ULONG TimerToDump, + VERBOSITY Verbosity +); + +VOID +DumpWorkQueueItem +( + ULONG ItemToDump, + VERBOSITY Verbosity +); + + +VOID +DumpMdlChain +( + ULONG MdlToDump, + VERBOSITY Verbosity +); + +#endif diff --git a/private/ntos/tdi/isn/isnext/daytona/makefile b/private/ntos/tdi/isn/isnext/daytona/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/isnext/daytona/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/isnext/daytona/sources b/private/ntos/tdi/isn/isnext/daytona/sources new file mode 100644 index 000000000..032c146bb --- /dev/null +++ b/private/ntos/tdi/isn/isnext/daytona/sources @@ -0,0 +1,45 @@ +MAJORCOMP=isn +MINORCOMP=isnext + +TARGETNAME=isnext +TARGETPATH=obj +TARGETTYPE=DYNLINK + +DLLDEF=obj\*\isnext.def + +SOURCES=\ + ..\isnext.c \ + ..\nbext.c \ + ..\isnext.rc \ + ..\ipxext.c \ + ..\traverse.c \ + ..\spxext.c \ + ..\cteext.c + +UMTYPE=windows + +INCLUDES=\ + ..\;\ + $(BASEDIR)\private\ntos\tdi\isn\inc;\ + $(BASEDIR)\private\ntos\tdi\isn\nb;\ + $(BASEDIR)\private\ntos\tdi\isn\spx;\ + $(BASEDIR)\private\ntos\tdi\isn\spx\h;\ + $(BASEDIR)\private\ntos\tdi\isn\ipx;\ + $(BASEDIR)\private\ntos\inc;\ + $(BASEDIR)\private\inc + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_PNP_POWER=1 -DBACK_FILL=1 +#-DRSRC_TIMEOUT_DBG + +#C_DEFINES=$(C_DEFINES) + +TARGETLIBS=\ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib + +USE_NTDLL=1 + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=..\precomp.pch +PRECOMPILED_OBJ=..\precomp.obj diff --git a/private/ntos/tdi/isn/isnext/dirs b/private/ntos/tdi/isn/isnext/dirs new file mode 100644 index 000000000..ae926322e --- /dev/null +++ b/private/ntos/tdi/isn/isnext/dirs @@ -0,0 +1,25 @@ +!IF 0 + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. This was necessitated by the need to + build a cairo and nt srv. + +Author: + + Isaac Heizer + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=daytona + +OPTIONAL_DIRS=cairo diff --git a/private/ntos/tdi/isn/isnext/ipxext.c b/private/ntos/tdi/isn/isnext/ipxext.c new file mode 100644 index 000000000..6a6e5e0e1 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/ipxext.c @@ -0,0 +1,1989 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + ipxext.c + +Abstract: + + This file contains kernel debugger extensions for examining the + IPX structures. + +Author: + + Heath Hunnicutt (T-HeathH) 3-Aug-1995 + +Environment: + + User Mode + +--*/ +#include "precomp.h" +#pragma hdrstop + +#include "isnipx.h" +#include "config.h" +#include "mac.h" +#include "ipxtypes.h" +//#include "ipxprocs.h" + +#define LIMIT_BINDINGS 10 + +// +// Local function prototypes +// +VOID DumpDeviceObject +( + ULONG DevObjToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxDevice +( + ULONG DeviceToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxSend +( + ULONG IpxSendToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxReceive +( + ULONG IpxReceiveToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxAddress +( + ULONG AddressToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxAddressFile +( + ULONG AddressFileToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxBinding +( + ULONG BindingToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxRequest +( + ULONG RequestToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxAdapter +( + ULONG AdapterToDump, + VERBOSITY Verbosity +); + +VOID +DumpIpxIrpStack +( + PREQUEST pRequest, + VERBOSITY Verbosity +); + + +ENUM_INFO EnumIrpMajorFunction[] = +{ + EnumString( IRP_MJ_CREATE ), + EnumString( IRP_MJ_CREATE_NAMED_PIPE ), + EnumString( IRP_MJ_CLOSE ), + EnumString( IRP_MJ_READ ), + EnumString( IRP_MJ_WRITE ), + EnumString( IRP_MJ_QUERY_INFORMATION ), + EnumString( IRP_MJ_SET_INFORMATION ), + EnumString( IRP_MJ_QUERY_EA ), + EnumString( IRP_MJ_SET_EA ), + EnumString( IRP_MJ_FLUSH_BUFFERS ), + EnumString( IRP_MJ_QUERY_VOLUME_INFORMATION ), + EnumString( IRP_MJ_SET_VOLUME_INFORMATION ), + EnumString( IRP_MJ_DIRECTORY_CONTROL ), + EnumString( IRP_MJ_FILE_SYSTEM_CONTROL ), + EnumString( IRP_MJ_DEVICE_CONTROL ), + EnumString( IRP_MJ_INTERNAL_DEVICE_CONTROL ), + EnumString( IRP_MJ_SHUTDOWN ), + EnumString( IRP_MJ_LOCK_CONTROL ), + EnumString( IRP_MJ_CLEANUP ), + EnumString( IRP_MJ_CREATE_MAILSLOT ), + EnumString( IRP_MJ_QUERY_SECURITY ), + EnumString( IRP_MJ_SET_SECURITY ), + EnumString( IRP_MJ_QUERY_POWER ), + EnumString( IRP_MJ_NOT_DEFINED ), + EnumString( IRP_MJ_DEVICE_CHANGE ), + EnumString( IRP_MJ_QUERY_QUOTA ), + EnumString( IRP_MJ_SET_QUOTA ), + { 0, NULL } +}; + +ENUM_INFO EnumIrpMinorFunction[ IRP_MJ_MAXIMUM_FUNCTION + 1 ][ 18 ] = +{ + {{ 0, NULL}}, // IRP_MJ_CREATE + {{ 0, NULL}}, // IRP_MJ_CREATE_NAMED_PIPE + {{ 0, NULL}}, // IRP_MJ_CLOSE + {{ 0, NULL}}, // IRP_MJ_READ + {{ 0, NULL}}, // IRP_MJ_WRITE + {{ 0, NULL}}, // IRP_MJ_QUERY_INFORMATION + {{ 0, NULL}}, // IRP_MJ_SET_INFORMATION + {{ 0, NULL}}, // IRP_MJ_QUERY_EA + {{ 0, NULL}}, // IRP_MJ_SET_EA + {{ 0, NULL}}, // IRP_MJ_FLUSH_BUFFERS + {{ 0, NULL}}, // IRP_MJ_QUERY_VOLUME_INFORMATION + {{ 0, NULL}}, // IRP_MJ_SET_VOLUME_INFORMATION + {{ 0, NULL}}, // IRP_MJ_DIRECTORY_CONTROL + {{ 0, NULL}}, // IRP_MJ_FILE_SYSTEM_CONTROL + {{ 0, NULL}}, // IRP_MJ_DEVICE_CONTROL + { // IRP_MJ_INTERNAL_DEVICE_CONTROL + EnumString( TDI_ASSOCIATE_ADDRESS ), + EnumString( TDI_DISASSOCIATE_ADDRESS ), + EnumString( TDI_CONNECT ), + EnumString( TDI_LISTEN ), + EnumString( TDI_ACCEPT ), + EnumString( TDI_DISCONNECT ), + EnumString( TDI_SEND ), + EnumString( TDI_RECEIVE ), + EnumString( TDI_SEND_DATAGRAM ), + EnumString( TDI_RECEIVE_DATAGRAM ), + EnumString( TDI_SET_EVENT_HANDLER ), + EnumString( TDI_QUERY_INFORMATION ), + EnumString( TDI_SET_INFORMATION ), + EnumString( TDI_ACTION ), + EnumString( TDI_DIRECT_SEND ), + EnumString( TDI_DIRECT_SEND_DATAGRAM ), + { 0, NULL } + }, + {{ 0, NULL}}, // IRP_MJ_SHUTDOWN + {{ 0, NULL}}, // IRP_MJ_LOCK_CONTROL + {{ 0, NULL}}, // IRP_MJ_CLEANUP + {{ 0, NULL}}, // IRP_MJ_CREATE_MAILSLOT + {{ 0, NULL}}, // IRP_MJ_QUERY_SECURITY + {{ 0, NULL}}, // IRP_MJ_SET_SECURITY + {{ 0, NULL}}, // IRP_MJ_QUERY_POWER + {{ 0, NULL}}, // IRP_MJ_SET_POWER + {{ 0, NULL}}, // IRP_MJ_DEVICE_CHANGE + {{ 0, NULL}}, // IRP_MJ_QUERY_QUOTA + {{ 0, NULL}}, // IRP_MJ_SET_QUOTA +}; + + +ENUM_INFO EnumAddressFileState[] = +{ + EnumString( ADDRESSFILE_STATE_OPENING ), + EnumString( ADDRESSFILE_STATE_OPEN ), + EnumString( ADDRESSFILE_STATE_CLOSING ), + { 0, NULL } +}; + + +ENUM_INFO EnumBindingFrameType[] = +{ + EnumString( ISN_FRAME_TYPE_802_2 ), + EnumString( ISN_FRAME_TYPE_802_3 ), + EnumString( ISN_FRAME_TYPE_ETHERNET_II ), + EnumString( ISN_FRAME_TYPE_SNAP ), + { 0, NULL } +}; + +ENUM_INFO EnumSendReservedIdentifier[] = +{ + EnumString( IDENTIFIER_NB ), + EnumString( IDENTIFIER_SPX ), + EnumString( IDENTIFIER_RIP ), + EnumString( IDENTIFIER_IPX ), + EnumString( IDENTIFIER_RIP_INTERNAL ), + EnumString( IDENTIFIER_RIP_RESPONSE ), + + { 0, NULL } +}; + +ENUM_INFO EnumSendReservedDestinationType[] = +{ + EnumString( DESTINATION_DEF ), + EnumString( DESTINATION_BCAST ), + EnumString( DESTINATION_MCAST ), + { 0, NULL } +}; + +MEMBER_TABLE IpxDeviceMembers[] = +{ + { "GlobalSendPacketList", + FIELD_OFFSET( DEVICE, GlobalSendPacketList ), + DumpIpxSend, + NextListEntry, + PrevListEntry, + FIELD_OFFSET( NDIS_PACKET, ProtocolReserved ) + FIELD_OFFSET( IPX_SEND_RESERVED, GlobalLinkage ) + }, + + { "GlobalReceivePacketList", + FIELD_OFFSET( DEVICE, GlobalReceivePacketList ), + DumpIpxReceive, + NextListEntry, + PrevListEntry, + FIELD_OFFSET( NDIS_PACKET, ProtocolReserved ) + FIELD_OFFSET( IPX_RECEIVE_RESERVED, GlobalLinkage ) + }, + + { NULL } +}; + + +/////////////////////////////////////////////////////////////////////// +// DEVICE +////////////////////////////////////////////////////////////////////// + + +// +// Exported functions +// + + + +VOID ipxdev_usage( VOID ) +{ + dprintf( "Use me!\n" ); +} + +DECLARE_API( ipxdev ) + +/*++ + +Routine Description: + + Dumps the most important fields of the specified DEVICE_CONTEXT object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ + +{ + ULONG deviceToDump = 0; + ULONG pDevice = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &deviceToDump); + } + + if ( deviceToDump == 0 ) { + + pDevice = GetExpression( "nwlnkipx!IpxDevice" ); + + if ( !pDevice ) { + dprintf("Could not get nwlnkipx!IpxDevice, Try !reload\n"); + return; + } else { + + if (!ReadMemory(pDevice, + &deviceToDump, + sizeof(deviceToDump), + &result + ) + ) + { + dprintf("%08lx: Could not read device address\n", pDevice); + return; + } + } + + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "IpxDevice", VarName, ( PVOID )deviceToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpIpxDevice(deviceToDump, VERBOSITY_NORMAL ); + + return; +} + + + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Device +#define _objAddr DeviceToDump +#define _objType DEVICE +// +// Local functions +// + +VOID +DumpIpxDevice +( + ULONG DeviceToDump, + VERBOSITY Verbosity +) + +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + DeviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ + +{ + DEVICE Device; + ULONG result; + unsigned int index; + BIND_ARRAY_ELEM Bindings[ LIMIT_BINDINGS ]; + WCHAR Buffer[ 1000 ]; + PWCHAR pDeviceName = NULL; + + if (!ReadMemory( + DeviceToDump, + &Device, + sizeof(Device), + &result + ) + ) + { + dprintf("%08lx: Could not read device context\n", DeviceToDump); + return; + } + if (Device.Type != IPX_DEVICE_SIGNATURE) + { + dprintf( "Signature does not match, probably not a device object %lx\n", DeviceToDump); + dprintf( "Device.Type == %04X, and I think it should be %04X\n", Device.Type, IPX_DEVICE_SIGNATURE ); + dprintf( "DeviceToDump = %08X\n", DeviceToDump ); + dprintf( "Offset to Device.Type = %d\n", FIELD_OFFSET( DEVICE, Type ) ); + return; + } + + if ( !ReadMemory( ( ULONG )Device.DeviceName, + Buffer, + sizeof( WCHAR ) * Device.DeviceNameLength, + &result )) + { + dprintf("%08lx: Could not read device name buffer\n", Device.DeviceName ); + } + else + { + pDeviceName = Buffer; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprintf( "\"%S\"", pDeviceName ); + return; + } + + dprintf("Device General Info "); + PrintStartStruct(); + +#if DBG +# if DREF_TOTAL != 12 +# error The DREF_TOTAL constant has changed, and so must ipxext.c +# endif + + PrintULong( RefTypes[ DREF_CREATE ] ); + PrintULong( RefTypes[ DREF_LOADED ] ); + PrintULong( RefTypes[ DREF_ADAPTER ] ); + PrintULong( RefTypes[ DREF_ADDRESS ] ); + PrintULong( RefTypes[ DREF_SR_TIMER ] ); + PrintULong( RefTypes[ DREF_RIP_TIMER ] ); + PrintULong( RefTypes[ DREF_LONG_TIMER ] ); + PrintULong( RefTypes[ DREF_RIP_PACKET ] ); + PrintULong( RefTypes[ DREF_ADDRESS_NOTIFY ] ); + PrintULong( RefTypes[ DREF_LINE_CHANGE ] ); + PrintULong( RefTypes[ 10 ] ); + PrintULong( RefTypes[ 11 ] ); +#endif + + PrintEnum( Type, EnumStructureType ); + PrintUShort( Size ); + +#if DBG + PrintNChar( Signature1, sizeof( Device.Signature1 )); +#endif + + PrintLock( Interlock ); + + PrintULong( TempDatagramBytesSent ); + PrintULong( TempDatagramsSent ); + PrintULong( TempDatagramBytesReceived ); + PrintULong( TempDatagramsReceived ); + + PrintBool( EthernetPadToEven ); + PrintBool( SingleNetworkActive ); + PrintBool( DisableDialoutSap ); + PrintBool( MultiCardZeroVirtual ); + + PrintLock( Lock ); + + PrintULong( ReferenceCount ); + + PrintStartStruct(); + dprintf( " ( We are assumed not to be at init time )\n" ); + PrintULong( BindingCount ); + + if ( Device.BindingCount > LIMIT_BINDINGS ) + { + dprintf(" isnext can only display the first %d bindings, because malloc() always crashed under the kernel debugger.\n", LIMIT_BINDINGS ); + dprintf(" This device struct has more bindings than that, but you're only going to see some of them.\n" ); + dprintf(" You could always change LIMIT_BINDINGS in ipxext.c and recompile isnext\n" ); + + Device.BindingCount = LIMIT_BINDINGS; + } + + if ( !ReadMemory( ( ULONG )Device.Bindings, + Bindings, + sizeof( PBIND_ARRAY_ELEM ) * Device.BindingCount, + &result ) ) + { + dprintf( "Could not read Bindings array.\n" ); + } + else + { + for( index = 0; index <= Device.ValidBindings; index ++ ) + { + dprintf( " Bindings[ %d ] = %-10X", index, Bindings[ index ].Binding ); + if ( Bindings[ index ].Binding != NULL ) + { + DumpIpxBinding( ( ULONG )Bindings[ index ].Binding, VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + } + } + PrintEndStruct(); + + PrintUShort( ValidBindings ); + PrintUShort( HighestExternalNicId ); + PrintUShort( SapNicCount ); + PrintUShort( HighestType20NicId ); + + PrintLL( GlobalSendPacketList ); + PrintLL( GlobalReceivePacketList ); + PrintLL( GlobalReceiveBufferList ); + + PrintLL( AddressNotifyQueue ); + + PrintLL( LineChangeQueue ); + PrintLL( SendPoolList ); + PrintLL( ReceivePoolList ); + + PrintL( SendPacketList ); + PrintL( ReceivePacketList ); + + PrintUChar( State ); + PrintUChar( FrameTypeDefault ); + + PrintBool( ActiveNetworkWan ); + PrintBool( VirtualNetwork ); + PrintUShort( FirstLanNicId ); + PrintUShort( FirstWanNicId ); + PrintULong( MemoryUsage ); + PrintULong( MemoryLimit ); + PrintULong( AllocatedDatagrams ); + PrintULong( AllocatedReceivePackets ); + PrintULong( AllocatedPaddingBuffers ); + PrintULong( InitDatagrams ); + PrintULong( MaxDatagrams ); + PrintULong( RipAgeTime ); + PrintULong( RipCount ); + PrintULong( RipTimeout ); + PrintULong( RipUsageTime ); + PrintULong( SourceRouteUsageTime ); + PrintUShort( SocketStart ); + PrintUShort( SocketEnd ); + PrintULong( SocketUniqueness ); + PrintULong( VirtualNetworkNumber ); + PrintULong( EthernetExtraPadding ); + PrintBool( DedicatedRouter ); + PrintBool( VirtualNetworkOptional ); + PrintUChar( DisableDialinNetbios ); + PrintULong( InitReceivePackets ); + PrintULong( InitReceiveBuffers ); + PrintULong( MaxReceivePackets ); + PrintULong( MaxReceiveBuffers ); + PrintUShort( ControlChannelIdentifier ); + PrintUShort( CurrentSocket ); + PrintULong( SegmentCount ); + + // MORE - dump actual locks + PrintPtr( SegmentLocks ); + + PrintLL( WaitingRipPackets ); + PrintULong( RipPacketCount ); + PrintBool( RipShortTimerActive ); + PrintUShort( RipSendTime ); + + PrintCTETimer( RipShortTimer ); + PrintCTETimer( RipLongTimer ); + + PrintBool( SourceRoutingUsed ); // TRUE if any 802.5 bindings exist. + PrintUChar( SourceRoutingTime ); // incremented each time timer fires. + PrintCTETimer( SourceRoutingTimer ); + PrintULong( LinkSpeed ); + PrintULong( MacOptions ); + PrintULong( IncludedHeaderOffset ); + PrintTDIAddress( SourceAddress ); + +#if IPX_ADDRESS_HASH_COUNT != 16 +# error An assumption is made here concerning the value of IPX_ADDRESS_HASH_COUNT +#endif + + PrintLL( AddressDatabases[ 0 ] ); + PrintLL( AddressDatabases[ 1 ] ); + PrintLL( AddressDatabases[ 2 ] ); + PrintLL( AddressDatabases[ 3 ] ); + PrintLL( AddressDatabases[ 4 ] ); + PrintLL( AddressDatabases[ 5 ] ); + PrintLL( AddressDatabases[ 6 ] ); + PrintLL( AddressDatabases[ 7 ] ); + PrintLL( AddressDatabases[ 8 ] ); + PrintLL( AddressDatabases[ 9 ] ); + PrintLL( AddressDatabases[ 10 ] ); + PrintLL( AddressDatabases[ 11 ] ); + PrintLL( AddressDatabases[ 12 ] ); + PrintLL( AddressDatabases[ 13 ] ); + PrintLL( AddressDatabases[ 14 ] ); + PrintLL( AddressDatabases[ 15 ] ); + + PrintPtr( LastAddress ); + + PrintPtr( NdisBufferPoolHandle ); + PrintAddr( Information ); + PrintULong( RealMaxDatagramSize ); + +#if DBG + PrintNChar( Signature2, sizeof( Device.Signature2 )); +#endif + + PrintFlushLeft(); + PrintBool( AnyUpperDriverBound ); + PrintBool( ForwarderBound ); + + if ( Device.AnyUpperDriverBound ) + { + for ( index = 0; index < UPPER_DRIVER_COUNT; index ++ ) + { + PrintFlushLeft(); + + dprintf( "UpperDriver...[ %d ] = ", index ); + PrintStartStruct(); + + PrintFieldName( "UpperDriverBound" ); + PrintRawBool( UpperDriverBound[ index ] ); + + if ( !Device.UpperDriverBound[ index ] ) + { + PrintEndStruct(); + continue; + } + + PrintFieldName( "ReceiveCompletePending" ); + PrintRawBool( ReceiveCompletePending[ index ] ); + PrintPtr( UpperDriverControlChannel[ index ] ); + + PrintSymbolPtr( UpperDrivers[ index ].ReceiveHandler ); + + PrintEndStruct(); + } + } + + PrintFlushLeft(); + + PrintULong( EnableBroadcastCount ); + PrintBool( EnableBroadcastPending ); + PrintBool( DisableBroadcastPending ); + PrintBool( ReverseBroadcastOperation ); + PrintBool( WanGlobalNetworkNumber ); + PrintULong( GlobalWanNetwork ); + PrintBool( GlobalNetworkIndicated ); + PrintBool( RipResponder ); + PrintBool( SapWarningLogged ); + + PrintWorkQueueItem( BroadcastOperationQueueItem ); + PrintAddr( UnloadEvent ); + PrintBool( UnloadWaiting ); + PrintAddr( Statistics ); + PrintBool( AutoDetect ); + PrintBool( DefaultAutoDetected ); + PrintUChar( AutoDetectState ); + + PrintAddr( AutoDetectEvent ); + PrintAddr( IpxStartTime ); + PrintAddr( AddressResource ); + PrintPtr( DeviceObject ); + + PrintJoin(); + PrintPtr( DeviceName ); + dprintf( "\"%S\"\n", pDeviceName ); + + PrintULong( DeviceNameLength ); + + + + PrintEndStruct(); + PrintEnd + + return; +} + + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Reserved +#define _objAddr pReserved +#define _objType IPX_SEND_RESERVED + +VOID +DumpIpxSend +( + ULONG IpxSendToDump, + VERBOSITY Verbosity +) +{ + PIPX_SEND_RESERVED pReserved; + IPX_SEND_RESERVED Reserved; + ULONG result; + + + pReserved = SEND_RESERVED(( PIPX_SEND_PACKET )IpxSendToDump ); + + if ( !ReadMemory( ( ULONG )pReserved, + &Reserved, + sizeof( Reserved ), + &result )) + { + dprintf("%08lx: Could not read IPX_SEND_RESERVED structure\n", pReserved ); + return; + } + + dprintf( "NDIS_PACKET @(0x%08X) ", IpxSendToDump ); + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprintf( "{ " ); + if ( Reserved.Address != NULL ) + { + DumpIpxAddress( ( ULONG )( Reserved.Address ), VERBOSITY_ONE_LINER ); + } + else + { + dprintf( "(NULL Address)" ); + } + + dprintf( " (" ); + dprint_enum_name( (ULONG) _obj.Identifier, EnumSendReservedIdentifier ); + dprintf( ") }" ); + return; + } + + + PrintStart; + PrintStartStruct(); + + PrintEnum( Identifier, EnumSendReservedIdentifier ); + + PrintBool( SendInProgress ); + PrintBool( OwnedByAddress ); + + PrintEnum( DestinationType, EnumSendReservedDestinationType ); + + PrintPtr( PaddingBuffer ); + PrintPtr( PreviousTail ); + PrintL( PoolLinkage ); + + PrintLL( GlobalLinkage ); + PrintLL( WaitLinkage ); +#ifdef IPX_TRACK_POOL + PrintPtr( Pool ); +#endif + PrintJoin(); + PrintPtr( Address ); + + if ( Reserved.Address != NULL ) + { + DumpIpxAddress( ( ULONG )( Reserved.Address ), VERBOSITY_ONE_LINER ); + } + + dprintf( "\n" ); + + PrintFlushLeft(); + + switch ( Reserved.Identifier ) + { + case IDENTIFIER_NB: + case IDENTIFIER_IPX: + case IDENTIFIER_SPX: + dprintf( " --- SR_DG part of union ---------------------------\n"); + PrintPtr( u.SR_DG.Request ); + PrintJoin(); + PrintPtr( u.SR_DG.AddressFile ); + if ( Reserved.u.SR_DG.AddressFile != NULL ) + { + DumpIpxAddressFile( ( ULONG )( Reserved.u.SR_DG.AddressFile ), VERBOSITY_ONE_LINER ); + } + + dprintf( "\n" ); + + PrintUShort( u.SR_DG.CurrentNicId ); + PrintBool( u.SR_DG.Net0SendSucceeded ); + PrintBool( u.SR_DG.OutgoingSap ); + break; + case IDENTIFIER_RIP: + case IDENTIFIER_RIP_INTERNAL: + case IDENTIFIER_RIP_RESPONSE: + PrintStartStruct(); + dprintf( " --- SR_RIP part of union ---------------------------\n"); + PrintULong( u.SR_RIP.Network ); + PrintUShort( u.SR_RIP.CurrentNicId ); + PrintUChar( u.SR_RIP.RetryCount ); + PrintBool( u.SR_RIP.RouteFound ); + PrintUShort( u.SR_RIP.SendTime ); + PrintBool( u.SR_RIP.NoIdAdvance ); + break; + default: + dprintf( "*** Couldn't determine which part of union to display.\n" ); + } + + PrintFlushLeft(); + + PrintPtr( Header ); + + PrintJoin(); + PrintPtr( HeaderBuffer ); + DumpMdlChain( ( ULONG )_obj.HeaderBuffer, VERBOSITY_ONE_LINER ); + dprintf( "\n" ); + + PrintEndStruct(); + + PrintEnd; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Reserved +#define _objAddr pReserved +#define _objType IPX_RECEIVE_RESERVED + +VOID +DumpIpxReceive +( + ULONG IpxReceiveToDump, + VERBOSITY Verbosity +) +{ + _objType *_objAddr; + _objType _obj; + ULONG result; + + dprintf( "NDIS_PACKET at 0x%08X\n", IpxReceiveToDump ); + + pReserved = RECEIVE_RESERVED(( PIPX_SEND_PACKET )IpxReceiveToDump ); + + if ( !ReadMemory( IpxReceiveToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read IPX_SEND_RESERVED structure\n", _objAddr ); + return; + } + + PrintStartStruct(); + + PrintEnum( Identifier, EnumSendReservedIdentifier ); + + PrintBool( TransferInProgress ); + PrintBool( OwnedByAddress ); + +#ifdef IPX_TRACK_POOL + PrintPtr( Pool ); +#endif + PrintJoin(); + PrintPtr( Address ); + if ( Reserved.Address != NULL ) + { + DumpIpxAddress( ( ULONG )( Reserved.Address ), VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintPtr( SingleRequest ); + PrintPtr( ReceiveBuffer ); + + PrintL( PoolLinkage ); + + PrintLL( GlobalLinkage ); + PrintLL( Requests ); + + PrintEndStruct(); +} + + +DECLARE_API( ipxaddrfile ) +{ + ULONG AddressFileToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &AddressFileToDump); + } + + if ( AddressFileToDump == 0 ) + { + dprintf( "Please specify an address.\n" ); + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "IpxAddressFile", VarName, ( PVOID )AddressFileToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpIpxAddressFile( AddressFileToDump, VERBOSITY_FULL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj AddressFile +#define _objAddr prAddressFile +#define _objType ADDRESS_FILE + +VOID +DumpIpxAddressFile +( + ULONG AddressFileToDump, + VERBOSITY Verbosity +) +{ + _objType _obj; + _objType *_objAddr; + ULONG result; + + _objAddr = ( _objType * )AddressFileToDump; + + if ( !ReadMemory( AddressFileToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + AddressFileToDump, + "ADDRESS_FILE" ); + return; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + switch ( _obj.State ) + { + case ADDRESSFILE_STATE_OPENING: + dprintf( "OPENING " ); + break; + case ADDRESSFILE_STATE_OPEN: + dprintf( "OPEN " ); + break; + case ADDRESSFILE_STATE_CLOSING: + dprintf( "CLOSING " ); + break; + default: + dprintf( "Bogus state " ); + break; + } + DumpIpxAddress( ( ULONG )( AddressFile.Address ), VERBOSITY_ONE_LINER ); + return; + } + + PrintStartStruct(); + +#if DBG +# if AFREF_TOTAL != 8 +# error AFREF_TOTAL was assumed to equal 8 +# endif + + PrintULong( RefTypes[ AFREF_CREATE ] ); + PrintULong( RefTypes[ AFREF_RCV_DGRAM ] ); + PrintULong( RefTypes[ AFREF_SEND_DGRAM ] ); + PrintULong( RefTypes[ AFREF_VERIFY ] ); + PrintULong( RefTypes[ AFREF_INDICATION ] ); + PrintULong( RefTypes[ 5 ] ); + PrintULong( RefTypes[ 6 ] ); + PrintULong( RefTypes[ 7 ] ); +#endif + + PrintEnum( Type, EnumStructureType ); + PrintUShort( Size ); + + PrintLL( Linkage ); + + PrintULong( ReferenceCount ); + + PrintEnum( State, EnumAddressFileState ); + + PrintPtr( AddressLock ); + + PrintPtr( Request ); + + PrintJoin(); + PrintPtr( Address ); + if ( AddressFile.Address != NULL ) + { + dprintf( "(" ); + DumpIpxAddress( ( ULONG )( AddressFile.Address ), VERBOSITY_ONE_LINER ); + dprintf( ")" ); + } + dprintf( "\n" ); + +#ifdef ISN_NT + PrintPtr( FileObject ); +#endif + + PrintJoin(); + PrintPtr( Device ); + if ( AddressFile.Device != NULL ) + { + DumpIpxDevice( ( ULONG )( AddressFile.Device ), VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintBool( SpecialReceiveProcessing ); + + PrintBool( ExtendedAddressing ); + PrintBool( ReceiveFlagsAddressing ); + + PrintBool( ReceiveIpxHeader ); + + PrintUChar( DefaultPacketType ); + + PrintBool( FilterOnPacketType ); + + PrintUChar( FilteredType ); + + PrintBool( EnableBroadcast ); + + PrintBool( IsSapSocket ); + + PrintLL( ReceiveDatagramQueue ); + + PrintPtr( CloseRequest ); + + PrintBool( RegisteredReceiveDatagramHandler ); + PrintBool( RegisteredErrorHandler ); + + PrintSymbolPtr( ReceiveDatagramHandler ); + PrintXULong( ReceiveDatagramHandlerContext ); + + PrintSymbolPtr( ErrorHandler ); + PrintXULong( ErrorHandlerContext ); + PrintEndStruct(); +} + +DECLARE_API( ipxaddr ) +{ + ULONG AddressToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &AddressToDump); + } + + if ( AddressToDump == 0 ) + { + dprintf( "Please specify an address.\n" ); + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "IpxBinding", VarName, ( PVOID )AddressToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpIpxAddress( AddressToDump, VERBOSITY_FULL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Address +#define _objAddr prAddress +#define _objType ADDRESS + +VOID +DumpIpxAddress +( + ULONG AddressToDump, + VERBOSITY Verbosity +) +{ + _objType _obj; + _objType *_objAddr; + ULONG result; + + _objAddr = ( _objType * )AddressToDump; + + if ( !ReadMemory( AddressToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + AddressToDump, + "ADDRESS" ); + return; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprint_hardware_address( _obj.LocalAddress.NodeAddress ); + dprintf( ".%d", Address.LocalAddress.Socket ); + return; + } + + PrintStartStruct(); +#if DBG +# if AREF_TOTAL != 4 +# error AREF_TOTAL was assumed to equal 4 +# endif + + PrintULong( RefTypes[ AREF_ADDRESS_FILE ] ); + PrintULong( RefTypes[ AREF_LOOKUP ] ); + PrintULong( RefTypes[ AREF_RECEIVE ] ); + PrintULong( RefTypes[ 3 ] ); +#endif + + PrintEnum( Type, EnumStructureType ); + PrintUShort( Size ); + + PrintLL( Linkage ); + PrintULong( ReferenceCount ); + PrintLock( Lock ); + + PrintPtr( Request ); + + PrintUShort( Socket ); + PrintUShort( SendSourceSocket ); + + + PrintBool( Stopping ); + PrintULong( Flags ); + + PrintJoin(); + PrintPtr( Device ); + if ( Address.Device != NULL ) + { + DumpIpxDevice( ( ULONG )( Address.Device ), VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintPtr( DeviceLock ); + + PrintLL( AddressFileDatabase ); + + PrintTDIAddress( LocalAddress ); + + PrintAddr( SendPacket ); + PrintAddr( ReceivePacket ); + + PrintAddr( SendPacketHeader ); + +#ifdef ISN_NT + PrintStartStruct(); + + if ( Address.ReferenceCount ) + { + PrintULong( u.ShareAccess.OpenCount ); + PrintULong( u.ShareAccess.Readers ); + PrintULong( u.ShareAccess.Writers ); + PrintULong( u.ShareAccess.Deleters ); + PrintULong( u.ShareAccess.SharedRead ); + PrintULong( u.ShareAccess.SharedWrite ); + PrintULong( u.ShareAccess.SharedDelete ); + } + else + { + PrintWorkQueueItem( u.DestroyAddressQueueItem ); + } + + PrintEndStruct(); + + PrintPtr( SecurityDescriptor ); +#endif + + PrintEndStruct(); +} + + +DECLARE_API( ipxadapter ) +{ + ULONG AdapterToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &AdapterToDump ); + } + + if ( AdapterToDump == 0 ) + { + dprintf( "Please specify an address.\n" ); + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "IpxAdapter", VarName, ( PVOID )AdapterToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpIpxAdapter( AdapterToDump, VERBOSITY_FULL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Adapter +#define _objAddr prAdapter +#define _objType ADAPTER + +VOID +DumpIpxAdapter +( + ULONG AdapterToDump, + VERBOSITY Verbosity +) +{ + _objType _obj; + _objType *_objAddr; + ULONG result; + WCHAR Buffer[ 1000 ]; + PWCHAR pAdapterName = NULL; + + _objAddr = ( _objType * )AdapterToDump; + + if ( !ReadMemory( AdapterToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + AdapterToDump, + "ADAPTER" ); + return; + } + + if ( !ReadMemory( ( ULONG )Adapter.AdapterName, + Buffer, + sizeof( WCHAR ) * Adapter.AdapterNameLength, + &result )) + { + dprintf("%08lx: Could not read adapter name structure\n", Adapter.AdapterName ); + } + else + { + pAdapterName = Buffer; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprintf( "\"%S\"", pAdapterName ); + return; + } + + dprintf( "Adapter at 0x%08X\n", AdapterToDump ); + + PrintStart; + + PrintEnum( Type, EnumStructureType ); + PrintUShort( Size ); +#if DBG + PrintNChar( Signature1, sizeof( Adapter.Signature1 )); +#endif + + PrintULong( BindingCount ); + PrintPtr( NdisBindingHandle ); + PrintLL( RequestCompletionQueue ); + +#if ISN_FRAME_TYPE_MAX !=4 +# error ISN_FRAME_TYPE_MAX is no longer 4. +#endif + + PrintULong( DefHeaderSizes[ ISN_FRAME_TYPE_ETHERNET_II ] ); + PrintULong( BcMcHeaderSizes[ ISN_FRAME_TYPE_ETHERNET_II ] ); + PrintPtr( Bindings[ ISN_FRAME_TYPE_ETHERNET_II ] ); + + PrintULong( DefHeaderSizes[ ISN_FRAME_TYPE_802_3 ] ); + PrintULong( BcMcHeaderSizes[ ISN_FRAME_TYPE_802_3 ] ); + PrintPtr( Bindings[ ISN_FRAME_TYPE_802_3 ] ); + + PrintULong( DefHeaderSizes[ ISN_FRAME_TYPE_802_2 ] ); + PrintULong( BcMcHeaderSizes[ ISN_FRAME_TYPE_802_2 ] ); + PrintPtr( Bindings[ ISN_FRAME_TYPE_802_2 ] ); + + PrintULong( DefHeaderSizes[ ISN_FRAME_TYPE_SNAP ] ); + PrintULong( BcMcHeaderSizes[ ISN_FRAME_TYPE_SNAP ] ); + PrintPtr( Bindings[ ISN_FRAME_TYPE_SNAP ] ); + + PrintULong( AllocatedReceiveBuffers ); + PrintLL( ReceiveBufferPoolList ); + PrintL( ReceiveBufferList ); + PrintULong( AllocatedPaddingBuffers ); + PrintL( PaddingBufferList ); + + PrintBool( BroadcastEnabled ); + PrintBool( AutoDetectFound ); + PrintBool( AutoDetectResponse ); + PrintBool( DefaultAutoDetected ); + PrintUShort( FirstWanNicId ); + PrintUShort( LastWanNicId ); + PrintULong( WanNicIdCount ); + PrintUShort( BindSap ); + PrintUShort( BindSapNetworkOrder ); + PrintBool( SourceRouting ); + PrintBool( EnableFunctionalAddress ); + PrintBool( EnableWanRouter ); + PrintULong( ConfigMaxPacketSize ); + + PrintJoin(); + PrintPtr( AdapterName ); + if ( pAdapterName != NULL ) + { + dprintf( "\"%S\"", pAdapterName ); + } + dprintf( "\n" ); + PrintULong( AdapterNameLength ); + + PrintJoin(); + PrintPtr( Device ); + if ( Adapter.Device != NULL ) + { + DumpIpxDevice( ( ULONG )( Adapter.Device ), VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintLock( Lock ); + PrintPtr( DeviceLock ); + PrintHardwareAddress( LocalMacAddress ); + PrintUChar( LastSourceRoutingTime ); + + PrintAddr( NdisRequestEvent ); + PrintXULong( NdisRequestStatus ); + PrintXULong( OpenErrorStatus ); + + PrintStartStruct(); + + PrintULong( MacInfo.MediumType ); + PrintULong( MacInfo.RealMediumType ); + PrintBool( MacInfo.SourceRouting ); + PrintBool( MacInfo.MediumAsync ); + PrintUChar( MacInfo.BroadcastMask ); + PrintULong( MacInfo.CopyLookahead ); + PrintULong( MacInfo.MacOptions ); + PrintULong( MacInfo.MinHeaderLength ); + PrintULong( MacInfo.MaxHeaderLength ); + + PrintEndStruct(); + + PrintULong( MaxReceivePacketSize ); + PrintULong( MaxSendPacketSize ); + PrintULong( ReceiveBufferSpace ); + PrintULong( MediumSpeed ); + +#if IDENTIFIER_TOTAL != 4 +# error IDENTIFIER_TOTAL is assumed to equal 4 +#endif + + PrintBool( SourceRoutingEmpty[IDENTIFIER_NB] ); + PrintPtr( SourceRoutingHeads[IDENTIFIER_NB] ); + + PrintBool( SourceRoutingEmpty[IDENTIFIER_IPX] ); + PrintPtr( SourceRoutingHeads[IDENTIFIER_IPX] ); + + PrintBool( SourceRoutingEmpty[IDENTIFIER_SPX] ); + PrintPtr( SourceRoutingHeads[IDENTIFIER_SPX] ); + + PrintBool( SourceRoutingEmpty[IDENTIFIER_RIP] ); + PrintPtr( SourceRoutingHeads[IDENTIFIER_RIP] ); + + PrintEnd; +} + +DECLARE_API( ipxbinding ) +{ + ULONG BindingToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &BindingToDump); + } + + if ( BindingToDump == 0 ) + { + dprintf( "Please specify an address.\n" ); + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "IpxBinding", VarName, ( PVOID )BindingToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpIpxBinding( BindingToDump, VERBOSITY_FULL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj Binding +#define _objAddr pBinding +#define _objType BINDING + +VOID +DumpIpxBinding +( + ULONG BindingToDump, + VERBOSITY Verbosity +) +{ + BINDING Binding; + PBINDING pBinding; + ULONG result; + + pBinding = ( PBINDING )BindingToDump; + + if ( !ReadMemory( BindingToDump, + &Binding, + sizeof( Binding ), + &result )) + { + dprintf("%08lx: Could not read BINDING structure\n", BindingToDump ); + return; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + if ( Binding.Adapter != NULL ) + { + DumpIpxAdapter( ( ULONG )( Binding.Adapter ), VERBOSITY_ONE_LINER ); + } + + dprintf( " %s\n", Binding.LineUp ? "UP" : "DOWN" ); + return; + } + + dprintf( "Binding at 0x%08X\n", BindingToDump ); + + PrintStart; + PrintStartStruct(); + +#if DBG +# if BREF_TOTAL != 5 +# error The BREF_TOTAL constant has changed, and so must ipxext.c +# endif + + PrintULong( RefTypes[ 0 ] ); + PrintULong( RefTypes[ BREF_BOUND ] ); + PrintULong( RefTypes[ 2 ] ); + PrintULong( RefTypes[ 3 ] ); + PrintULong( RefTypes[ 4 ] ); +#endif + + PrintEnum( Type, EnumStructureType ); + PrintUShort( Size ); + +#if DBG + PrintNChar( Signature1, sizeof( Binding.Signature1 )); +#endif + + PrintULong( ReferenceCount ); + PrintJoin(); + PrintPtr( Adapter ); + + if ( Binding.Adapter != NULL ) + { + DumpIpxAdapter( ( ULONG )( Binding.Adapter ), VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintUShort( NicId ); + + PrintULong( MaxSendPacketSize ); + + PrintJoin(); + PrintULong( MediumSpeed ); // in units of 100 bytes/sec + dprintf( "(In units of 100 bytes/sec)\n" ); + + PrintHardwareAddress( LocalMacAddress ); + PrintHardwareAddress( RemoteMacAddress ); + + PrintFieldName( "WanRemoteNode" ); + dprint_hardware_address( Binding.WanRemoteNode ); + dprintf( "\n" ); + + PrintBool( AutoDetect ); + + PrintBool( DefaultAutoDetect ); + + PrintUShort( MatchingResponses ); + PrintUShort( NonMatchingResponses ); + PrintXULong( TentativeNetworkAddress ); + PrintBool( BindingSetMember ); + PrintBool( ReceiveBroadcast ); + PrintBool( LineUp ); + PrintBool( DialOutAsync ); + + if ( Binding.BindingSetMember ) + { + PrintPtr( NextBinding ); + PrintPtr( CurrentSendBinding ); + PrintPtr( MasterBinding ); + } + + PrintULong( WanInactivityCounter ); + + PrintTDIAddress( LocalAddress ); + + PrintSymbolPtr( SendFrameHandler ); + + PrintPtr( Device ); + + PrintJoin(); + PrintPtr( DeviceLock ); + if ( Binding.DeviceLock != NULL ) + { + DumpCTELock( ( ULONG )Binding.DeviceLock, VERBOSITY_ONE_LINER ); + } + dprintf( "\n" ); + + PrintULong( DefHeaderSize ); + PrintULong( BcMcHeaderSize ); + + PrintULong( AnnouncedMaxDatagramSize ); + PrintULong( RealMaxDatagramSize ); + PrintULong( MaxLookaheadData ); + PrintXULong( FwdAdapterContext ); + PrintULong( InterfaceIndex ); + PrintULong( ConnectionId ); + PrintULong( IpxwanConfigRequired ); + + { + ULONG i; + + for (i=0; iTail.Overlay.CurrentStackLocation; + + for ( idxStack = 0; idxStack < pRequest->StackCount; idxStack ++ ) + { + StackToDump = ( ULONG )prStack; + + if ( !ReadMemory( StackToDump, + &Stack, + sizeof( Stack ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + StackToDump, + "STACK" ); + return; + } + + if ( Stack.DeviceObject == IpxDeviceObject ) + { + break; + } + + prStack --; + } + + if ( Stack.DeviceObject != IpxDeviceObject ) + { + dprintf( "None of the device objects in this IRP's stacks seem to match the IPX device object.\n "); + return; + } + + PrintStartStruct(); + + PrintXEnum( MajorFunction, EnumIrpMajorFunction ); + PrintXEnum( MinorFunction, EnumIrpMinorFunction[ _obj.MajorFunction ] ); + + // Send? Dump Parameters + + PrintAddr( Parameters ); + DumpTdiSendParameters( AddressOf( Parameters ), VERBOSITY_FULL ); + + PrintXULong( Flags ); + PrintXULong( Control ); + + PrintJoin(); + PrintPtr( DeviceObject ); + DumpDeviceObject(( ULONG ) Stack.DeviceObject, VERBOSITY_ONE_LINER ); + + PrintJoin(); + PrintPtr( FileObject ); + + if ( Stack.FileObject != NULL ) + { + FILE_OBJECT FileObject; + if ( !ReadMemory( ( ULONG )Stack.FileObject, + &FileObject, + sizeof( FileObject ), + &result )) + { + dprintf( "%08lx: Could not read FileObject", Stack.FileObject ); + } + else + { + dprintf( "Addressfile at %8X: ", ( ULONG )FileObject.FsContext ); + DumpIpxAddressFile( ( ULONG )FileObject.FsContext, VERBOSITY_ONE_LINER ); + } + } + dprintf( "\n" ); + + PrintSymbolPtr( CompletionRoutine ); + PrintXULong( Context ); + + PrintEndStruct(); +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +#endif + +#define _obj DevObj +#define _objAddr prDevObj +#define _objType DEVICE_OBJECT + +VOID DumpDeviceObject +( + ULONG DevObjToDump, + VERBOSITY Verbosity +) +{ + _objType _obj; + _objType *_objAddr; + ULONG result; + PIO_STACK_LOCATION pStack; + + _objAddr = ( _objType * )DevObjToDump; + + if ( !ReadMemory( DevObjToDump, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf( "%08lx: Could not read %s structure\n", + DevObjToDump, + "DEVICE_OBJECT" ); + return; + } + + if ( Verbosity != VERBOSITY_ONE_LINER ) + { + dprintf( "DumpDeviceObject only support VERBOSITY_ONE_LINER.\n" ); + } + + dprintf( "Ref = %d, Driver = %08X, CurrentIrp = %08X\n", + _obj.ReferenceCount, + _obj.DriverObject, + _obj.CurrentIrp ); +} + diff --git a/private/ntos/tdi/isn/isnext/ipxext.h b/private/ntos/tdi/isn/isnext/ipxext.h new file mode 100644 index 000000000..2b8dfac92 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/ipxext.h @@ -0,0 +1,16 @@ +#if !defined( INCLUDED_IPXEXT_H ) +#define INCLUDED_IPXEXT_H + +extern MEMBER_TABLE IpxDeviceMembers[]; + +VOID +DumpIpxDevice +( + ULONG DeviceToDump, + VERBOSITY Verbosity +); + +#define IPX_MAJOR_STRUCTURES \ +{ "IpxDevice", IpxDeviceMembers, DumpIpxDevice } + +#endif diff --git a/private/ntos/tdi/isn/isnext/isnext.c b/private/ntos/tdi/isn/isnext/isnext.c new file mode 100644 index 000000000..5246508ad --- /dev/null +++ b/private/ntos/tdi/isn/isnext/isnext.c @@ -0,0 +1,345 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + isnext.c + +Abstract: + + This file contains the generic routines and initialization code + for the kernel debugger extensions dll. + +Author: + + Munil Shah + +Environment: + + User Mode + +--*/ +#include "precomp.h" +#pragma hdrstop + +// +// globals +// + +#include "isnipx.h" +#include "isnspx.h" + +ENUM_INFO EnumStructureType[] = +{ + EnumString( IPX_DEVICE_SIGNATURE ), + EnumString( IPX_ADAPTER_SIGNATURE ), + EnumString( IPX_BINDING_SIGNATURE ), + EnumString( IPX_ADDRESS_SIGNATURE ), + EnumString( IPX_ADDRESSFILE_SIGNATURE ), + { 0x4453, "SPX_DEVICE_SIGNATURE" }, + { 0x4441, "SPX_ADDRESS_SIGNATURE" }, + { 0x4641, "SPX_ADDRESSFILE_SIGNATURE" }, + { 0x4643, "SPX_CONNFILE_SIGNATURE" }, + { 0, NULL } +}; + +EXT_API_VERSION ApiVersion = { 3, 5, EXT_API_VERSION_NUMBER, 0 }; +WINDBG_EXTENSION_APIS ExtensionApis; +USHORT SavedMajorVersion; +USHORT SavedMinorVersion; +BOOLEAN ChkTarget; +INT Item; + +HANDLE _hInstance; +HANDLE _hAdditionalReference; +HANDLE _hProcessHeap; + +int _Indent = 0; +char IndentBuf[ 80 ]={"\0 "}; + +DllInit( + HANDLE hModule, + DWORD dwReason, + DWORD dwReserved + ) +{ + switch (dwReason) { + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + + case DLL_PROCESS_ATTACH: + _hInstance = hModule; + _hAdditionalReference = NULL; + break; + } + + return TRUE; +} + + +VOID +WinDbgExtensionDllInit( + PWINDBG_EXTENSION_APIS lpExtensionApis, + USHORT MajorVersion, + USHORT MinorVersion + ) +{ + ExtensionApis = *lpExtensionApis; + + SavedMajorVersion = MajorVersion; + SavedMinorVersion = MinorVersion; + ChkTarget = SavedMajorVersion == 0x0c ? TRUE : FALSE; + return; +} + +DECLARE_API( version ) +{ +#if DBG + PCHAR DebuggerType = "Checked"; +#else + PCHAR DebuggerType = "Free"; +#endif + + dprintf( "%s Extension dll for Build %d debugging %s kernel for Build %d\n", + DebuggerType, + VER_PRODUCTBUILD, + SavedMajorVersion == 0x0c ? "Checked" : "Free", + SavedMinorVersion + ); +} + +VOID +CheckVersion( + VOID + ) +{ + + return; + +#if DBG + if ((SavedMajorVersion != 0x0c) || (SavedMinorVersion != VER_PRODUCTBUILD)) { + dprintf("\r\n*** Extension DLL(%d Checked) does not match target system(%d %s)\r\n\r\n", + VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" ); + } +#else + if ((SavedMajorVersion != 0x0f) || (SavedMinorVersion != VER_PRODUCTBUILD)) { + dprintf("\r\n*** Extension DLL(%d Free) does not match target system(%d %s)\r\n\r\n", + VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" ); + } +#endif +} + +LPEXT_API_VERSION +ExtensionApiVersion( + VOID + ) +{ + return &ApiVersion; +} + +// +// Exported functions +// +DECLARE_API( help ) + +/*++ + +Routine Description: + + Command help for ISN debugger extensions. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + dprintf("NB debugger extension commands:\n\n"); + dprintf("\tnbaddr - Dump an NB ADDRESS object\n"); + dprintf("\tnbaddrfile - Dump an NB ADDRESS_FILE object\n"); + dprintf("\tnbconn - Dump key fields of an NB CONNECTION object\n"); + dprintf("\tnbconnfull - Dump all fields of an NB CONNECTION object\n"); + dprintf("\tnbdev [ptr] - Dump key fields of an NB DEVICE object\n"); + dprintf("\tnbdevfull [ptr] - Dump all fields of an NB DEVICE object\n"); + dprintf("\n"); + + dprintf("SPX debugger extension commands:\n\n"); + dprintf("\tspxdev [ptr] [-l var] - Dump all fields of an IPX DEVICE object\n" ); + dprintf("\tspxaddr \n" ); + dprintf("\tspxaddrfile \n" ); + dprintf("\tspxconnfile \n" ); + dprintf("\n"); + + dprintf("IPX debugger extension commands:\n\n"); + dprintf("\tipxdev [ptr] [-l var] - Dump all fields of an IPX DEVICE object\n" ); + dprintf("\tipxaddr \n" ); + dprintf("\tipxaddrfile \n" ); + dprintf("\tipxbinding \n" ); + dprintf("\tipxadapter \n" ); + dprintf("\tipxrequest - Turn an IRP into an IPX_ADDRESS_FILE\n" ); + dprintf("\n"); + + dprintf("\tnext - Advance to next element in currently focused list.\n\n" ); + dprintf("\tprev - Advance to previous element in currently focused list.\n\n" ); + + dprintf( "Compiled on " __DATE__ " at " __TIME__ "\n" ); + return; +} + + + +VOID +dprintSymbolPtr +( + PVOID Pointer, + PCHAR EndOfLine +) +{ + UCHAR SymbolName[ 80 ]; + ULONG Displacement; + + dprintf("%-10lx", ( ULONG )Pointer ); + + GetSymbol( Pointer, SymbolName, &Displacement ); + + if ( Displacement == 0 ) + { + dprintf( "(%s)%s", SymbolName, EndOfLine ); + } + else + { + dprintf( "(%s + 0x%X)%s", SymbolName, Displacement, EndOfLine ); + } +} + +VOID +dprint_nchar +( + PCHAR pch, + int cch +) +{ + CHAR ch; + int index; + + for ( index = 0; index < cch; index ++ ) + { + ch = pch[ index ]; + dprintf( "%c", ( ch >= 32 ) ? ch : '.' ); + } +} + +VOID +dprint_hardware_address +( + PUCHAR Address +) +{ + dprintf( "%02x-%02x-%02x-%02x-%02x-%02x", + Address[ 0 ], + Address[ 1 ], + Address[ 2 ], + Address[ 3 ], + Address[ 4 ], + Address[ 5 ] ); +} + +BOOL +dprint_enum_name +( + ULONG Value, + PENUM_INFO pEnumInfo +) +{ + while ( pEnumInfo->pszDescription != NULL ) + { + if ( pEnumInfo->Value == Value ) + { + dprintf( "%.40s", pEnumInfo->pszDescription ); + return( TRUE ); + } + pEnumInfo ++; + } + + dprintf( "Unknown enumeration value." ); + return( FALSE ); +} + +BOOL +dprint_flag_names +( + ULONG Value, + PFLAG_INFO pFlagInfo +) +{ + BOOL bFoundOne = FALSE; + + while ( pFlagInfo->pszDescription != NULL ) + { + if ( pFlagInfo->Value & Value ) + { + if ( bFoundOne ) + { + dprintf( " | " ); + } + bFoundOne = TRUE; + + dprintf( "%.15s", pFlagInfo->pszDescription ); + } + pFlagInfo ++; + } + + return( bFoundOne ); +} + +BOOL +dprint_masked_value +( + ULONG Value, + ULONG Mask +) +{ + CHAR Buf[ 9 ]; + ULONG nibble; + int index; + + for ( index = 0; index < 8; index ++ ) + { + nibble = ( Mask & 0xF0000000 ); +/* + dprintf( "#%d: nibble == %08X\n" + " Mask == %08X\n" + " Value == %08X\n", index, nibble, Mask, Value ); + +*/ + if ( nibble ) + { + Buf[ index ] = "0123456789abcdef"[ (( nibble & Value ) >> 28) & 0xF ]; + } + else + { + Buf[ index ] = ' '; + } + + Mask <<= 4; + Value <<= 4; + } + + Buf[ 8 ] = '\0'; + + dprintf( "%s", Buf ); + + return( TRUE ); +} diff --git a/private/ntos/tdi/isn/isnext/isnext.def b/private/ntos/tdi/isn/isnext/isnext.def new file mode 100644 index 000000000..d43dc97ef --- /dev/null +++ b/private/ntos/tdi/isn/isnext/isnext.def @@ -0,0 +1,33 @@ +LIBRARY ISNEXT + +DESCRIPTION 'KD Extensions for isn' + +EXPORTS + + CheckVersion + WinDbgExtensionDllInit + ExtensionApiVersion + + help + + next + prev + + nbaddrfile + nbaddr + nbconn + nbconnfull + nbdev + nbdevfull + + ipxdev + ipxbinding + ipxaddr + ipxaddrfile + ipxadapter + ipxrequest + + spxdev + spxaddr + spxaddrfile + spxconnfile diff --git a/private/ntos/tdi/isn/isnext/isnext.h b/private/ntos/tdi/isn/isnext/isnext.h new file mode 100644 index 000000000..f0222ca21 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/isnext.h @@ -0,0 +1,306 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + isnext.h + +Abstract: + + This file is a common header file for isnext.dll + +Author: + + Munil Shah (munils) 18-May-1995 + +Environment: + + User Mode + +--*/ + +#define ITEMSIZE 25 + +typedef struct +{ + ULONG Value; + PCHAR pszDescription; +} ENUM_INFO, *PENUM_INFO, FLAG_INFO, *PFLAG_INFO; + +#define EnumString( Value ) { Value, #Value } + +extern ENUM_INFO EnumStructureType[]; + +//#define EOL ( (Item++ & 1) ? "\n":"" ) +typedef enum +{ + VERBOSITY_ONE_LINER = 0, + VERBOSITY_NORMAL, + VERBOSITY_FULL +} VERBOSITY; + +#define PrintStart Item = 0; + +extern int _Indent; +extern char IndentBuf[ 80 ]; + +#define IndentChange( cch ) { IndentBuf[_Indent]=' '; _Indent += ( cch ); IndentBuf[_Indent]='\0';} +#define Indent( cch ) IndentChange( cch ) +#define Outdent( cch ) IndentChange( -( cch ) ) + +#define PrintStartStruct() { PrintStart; dprintf( "%s{\n", IndentBuf ); Indent( 2 ); } + +#define PrintStartNamedStruct( _name ) { PrintStart; dprintf( "%s%s {\n", IndentBuf, _name ); Indent( 2 ); } + +static PCHAR pchEol = "\n"; +static PCHAR pchBlank = ""; +static PCHAR * ppchCurrentEol = &pchEol; +static PCHAR * ppchTempEol = &pchEol; + +#define PrintJoin() { ppchCurrentEol = &pchBlank; } + +#define EOL (( ppchTempEol = ppchCurrentEol ), ( ppchCurrentEol = &pchEol ), ( *ppchTempEol )) + +VOID +dprintSymbolPtr +( + PVOID Pointer, + PCHAR EndOfLine +); + +VOID +dprint_nchar +( + PCHAR pch, + int cch +); + +VOID +dprint_hardware_address +( + PUCHAR Address +); + +BOOL +dprint_enum_name +( + ULONG Value, + PENUM_INFO pEnumInfo +); + + +BOOL +dprint_flag_names +( + ULONG Value, + PFLAG_INFO pFlagInfo +); + +BOOL +dprint_masked_value +( + ULONG Value, + ULONG Mask +); + +/* +#define PrintEnd \ + dprintf( "%s", EOL ); \ + Item = 0; +*/ + +#define PrintEnd \ + Item = 0; + +#define PrintEndStruct() { Outdent( 2 ); PrintEnd; dprintf( "%s}\n", IndentBuf ); } + +#define PrintFlushLeft() PrintEnd + +#define PRINTBOOL(var) ( (var) ? "True" : "False") + +#define PrintFieldName(_fieldName) \ + if ( strlen(_fieldName) > 35 ) { \ + dprintf("%s%-.25s..%s = ",IndentBuf,_fieldName, &(_fieldName[strlen(_fieldName)-8])); \ + }else { \ + dprintf("%s%-35.35s = ",IndentBuf,_fieldName ); \ + } + +#define PrintFieldNameAt(_fieldName) \ + if ( strlen(_fieldName) > 35 ) { \ + dprintf("%s%-.25s..%s @ ",IndentBuf,_fieldName, &(_fieldName[strlen(_fieldName)-8])); \ + }else { \ + dprintf("%s%-35.35s @ ",IndentBuf,_fieldName ); \ + } + +#define PrintListFieldName(_fieldName) \ + if ( strlen(_fieldName) > 40 ) { \ + dprintf("%s%-.30s...%s Flink = ",IndentBuf,_fieldName, &(_fieldName[strlen(_fieldName)-7])); \ + }else { \ + dprintf("%s%-40.40s Flink = ",IndentBuf,_fieldName ); \ + } + +#define PrintIndent() dprintf( "%s", IndentBuf ); +/* #define PrintFieldName(_fieldName) \ + dprintf(" %-25.25s = ",_fieldName );*/ + +#define PrintRawBool( _bValue ) \ + dprintf("%-10s%s", (_obj._bValue) ? "True" : "False", EOL) + +#define PrintBool(_field) \ + PrintFieldName(#_field) \ + dprintf("%-10s%s", (_obj._field) ? "True" : "False", EOL) + +#define PrintULong(_field) \ + PrintFieldName(#_field) \ + dprintf("%-10lu%s", _obj._field, EOL) + +#define PrintXULong(_field) \ + PrintFieldName(#_field) \ + dprintf("0x%08lx%s", _obj._field, EOL) + +#define PrintUShort(_field) \ + PrintFieldName(#_field) \ + dprintf("%-10hu%s", _obj._field, EOL) + +#define PrintXUShort(_field) \ + PrintFieldName(#_field) \ + dprintf("0x%04hx%s", _obj._field, EOL) + +#define PrintNChar( _field, count ) \ + PrintFieldName(#_field) \ + dprint_nchar( ( PCHAR )_obj._field, count ); \ + dprintf("%s", EOL) + +#define PrintUChar(_field) \ + PrintFieldName(#_field) \ + dprintf("%-10lu%s", (ULONG) _obj._field, EOL) + +#define PrintXUChar(_field) \ + PrintFieldName(#_field) \ + dprintf("0x%-8lx%s", (ULONG) _obj._field, EOL) + +#define PrintPtr(_field) \ + PrintFieldName(#_field) \ + dprintf("%-10lx%s", _obj._field, EOL) + +#define PrintSymbolPtr( _field ) \ + PrintFieldName(#_field) \ + dprintSymbolPtr( (( PVOID )_obj._field), EOL ); + +#define AddressOf( _field ) ((( ULONG )_objAddr) + FIELD_OFFSET( _objType, _field )) + +#define PrintAddr(_field) \ + PrintFieldNameAt(#_field) \ + dprintf("%-10lx%s", AddressOf( _field ), EOL) + +#define PrintL(_field) \ + PrintFieldName(#_field##".Next") \ + dprintf("%-10lx%s", _obj._field.Next, EOL ) + +#define PrintLL(_field) \ + PrintEnd; \ + PrintListFieldName(#_field ); \ + dprintf("%-10lx", _obj._field.Flink ); \ + dprintf("Blink = %-10lx", _obj._field.Blink ); \ + dprintf("%s\n", ( _obj._field.Flink == _obj._field.Blink ) ? " (Empty)" : "" ); + +#define PrintIrpQ(_field) \ + PrintEnd; \ + PrintFieldName(#_field##".Head"); \ + dprintf("%-10lx", _obj._field.Head ); \ + PrintFieldName(#_field##".Tail"); \ + dprintf("%-10lx\n", _obj._field.Tail ); + +#define PrintFlags( _field, _pFlagStruct ) \ + PrintEnd; \ + PrintFieldName(#_field); \ + dprintf("0x%08lx (", (ULONG) _obj._field ); \ + dprint_flag_names( (ULONG) _obj._field, _pFlagStruct ); \ + dprintf( ")\n" ); + +#define PrintFlagsMask( _field, _pFlagStruct, _Mask ) \ + PrintEnd; \ + PrintFieldName(" & " ## #_Mask ); \ + dprintf("0x"); \ + dprint_masked_value((ULONG) _obj._field, _Mask ); \ + dprintf("("); \ + dprint_flag_names( (ULONG) _obj._field, _pFlagStruct ); \ + dprintf( ")\n" ); + +#define PrintEnum( _field, _pEnumStruct ) \ + PrintEnd; \ + PrintFieldName(#_field); \ + dprintf("%lu (", (ULONG) _obj._field ); \ + dprint_enum_name( (ULONG) _obj._field, _pEnumStruct ); \ + dprintf( ")\n" ); + +#define PrintXEnum( _field, _pEnumStruct ) \ + PrintEnd; \ + PrintFieldName(#_field); \ + dprintf("0x%08lx (", (ULONG) _obj._field ); \ + dprint_enum_name( (ULONG) _obj._field, _pEnumStruct ); \ + dprintf( ")\n" ); + +#define PrintXEnumMask( _field, _pEnumStruct, _Mask ) \ + PrintEnd; \ + PrintFieldName(" & " ## #_Mask ); \ + dprintf("0x"); \ + dprint_masked_value((ULONG) _obj._field, _Mask ); \ + dprintf("("); \ + dprint_enum_name((ULONG) _obj._field & _Mask, _pEnumStruct ); \ + dprintf( ")\n" ); + + +#define PrintHardwareAddress( _field ) \ + PrintFieldName(#_field); \ + dprint_hardware_address( _obj._field.Address ); \ + dprintf( "%s", EOL ); + +#define PrintIpxLocalTarget( _field ) \ + PrintStartNamedStruct( #_field ); \ + PrintFieldName( "NicId" ); \ + dprintf("%-10u%s", _obj._field.NicId, EOL); \ + PrintFieldName( "MacAddress" ); \ + dprint_hardware_address( _obj._field.MacAddress ); \ + dprintf( "%s", EOL ); \ + PrintEndStruct(); + + +#define PrintLock( _field ) \ + PrintULong( _field ) + +#define PrintTDIAddress( _field ) \ + PrintFieldName( #_field ); \ + dprintf( "{ NetworkAddress = %X, NodeAddress = ", _obj._field.NetworkAddress );\ + dprint_hardware_address( _obj._field.NodeAddress );\ + dprintf( ", Socket = %d }%s", _obj._field.Socket, EOL ); + +#define PrintCTETimer( _field ) \ + dprintf( "%s", #_field ); \ + PrintStartStruct(); \ + DumpCTETimer ( ((ULONG)_objAddr) +FIELD_OFFSET( _objType, _field ), VERBOSITY_NORMAL );\ + PrintEndStruct(); + +#define PrintWorkQueueItem( _field ) \ + dprintf( "%s", #_field ); \ + PrintStartStruct(); \ + DumpWorkQueueItem ( ((ULONG)_objAddr) +FIELD_OFFSET( _objType, _field ), VERBOSITY_NORMAL );\ + PrintEndStruct(); + + +extern BOOLEAN ChkTarget; +extern INT Item; + +#define CHECK_SIGNATURE( _field, _signature ) \ + if ( _obj._field != _signature ) \ + { \ + dprintf( "Object at %08X doesn't have signature %s at %08X\n", \ + _objAddr, \ + #_signature, \ + (( ULONG )_objAddr) + FIELD_OFFSET( _objType, _field ));\ + } + + + + diff --git a/private/ntos/tdi/isn/isnext/isnext.rc b/private/ntos/tdi/isn/isnext/isnext.rc new file mode 100644 index 000000000..e69368262 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/isnext.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "kernel debugger extension dll" +#define VER_INTERNALNAME_STR "isnext.dll" +#define VER_ORIGINALFILENAME_STR "isnext.dll" + +#include + diff --git a/private/ntos/tdi/isn/isnext/nbext.c b/private/ntos/tdi/isn/isnext/nbext.c new file mode 100644 index 000000000..053025fb7 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/nbext.c @@ -0,0 +1,873 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + nbext.c + +Abstract: + + This file contains kernel debugger extensions for examining the + NB structure. + +Author: + + Munil Shah (munils) 18-May-1995 + +Environment: + + User Mode + +--*/ +#include "precomp.h" +#pragma hdrstop +#include "isn.h" +#include "isnnb.h" +#include "zwapi.h" +#include "config.h" +#include "nbitypes.h" + + +PCHAR HandlerNames[] = { "Connection", "Disconnect", "Error", "Receive", "ReceiveDatagram", "ExpeditedData" }; + +// +// Local function prototypes +// +VOID +DumpAddrFile( + ULONG AddrFileToDump + ); + + +VOID +DumpAddrObj( + ULONG AddrObjToDump + ); + +VOID +DumpConn( + ULONG ConnToDump, + BOOLEAN Full + ); + +VOID +Dumpdevice( + ULONG deviceToDump, + BOOLEAN Full + ); + + +/////////////////////////////////////////////////////////////////////// +// ADDRESS_FILE +////////////////////////////////////////////////////////////////////// + +#define _obj addrfile +#define _objAddr AddrFileToDump +#define _objType ADDRESS_FILE + +// +// Exported functions +// + +DECLARE_API( nbaddrfile ) + +/*++ + +Routine Description: + + Dumps the most important fields of the specified ADDRESS_FILE object + +Arguments: + + args - Address of args string + +Return Value: + + None + +--*/ + +{ + ULONG addrFileToDump = 0; + + if (!*args) { + dprintf("No address_file object specified\n"); + } + else { + sscanf(args, "%lx", &addrFileToDump); + DumpAddrFile(addrFileToDump); + } + + return; +} + + +// +// Local functions +// + +VOID +DumpAddrFile( + ULONG AddrFileToDump + ) + +/*++ + +Routine Description: + + Dumps the fields of the specified ADDRESS_FILE object + +Arguments: + + AddrFileToDump - The ADDRESS_FILE object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ + +{ + ADDRESS_FILE addrfile; + ULONG result; + UCHAR i; + + if (!ReadMemory( + AddrFileToDump, + &addrfile, + sizeof(addrfile), + &result + ) + ) + { + dprintf("%08lx: Could not read address object\n", AddrFileToDump); + return; + } + + if (addrfile.Type != NB_ADDRESSFILE_SIGNATURE) { + dprintf("Signature does not match, probably not an address object\n"); + return; + } + + dprintf("NBI AddressFile:\n"); + + PrintStart + PrintXUChar(State); + PrintXULong(ReferenceCount); + PrintPtr(FileObject); + PrintPtr(Address); + PrintPtr(OpenRequest); + PrintPtr(CloseRequest); + PrintLL(Linkage); + PrintLL(ConnectionDatabase); + PrintLL(ReceiveDatagramQueue); + PrintEnd + + for ( i= TDI_EVENT_CONNECT; i < TDI_EVENT_SEND_POSSIBLE ; i++ ) { + dprintf(" %sHandler = %lx, Registered = %s, Context = %lx\n", + HandlerNames[i], addrfile.Handlers[i], PRINTBOOL(addrfile.RegisteredHandler[i]),addrfile.HandlerContexts[i] ); + } + return; +} + +/////////////////////////////////////////////////////////////////////// +// ADDRESS +////////////////////////////////////////////////////////////////////// + +#undef _obj +#undef _objAddr +#undef _objType +#define _obj addrobj +#define _objAddr AddrObjToDump +#define _objType ADDRESS + +DECLARE_API( nbaddr ) + +/*++ + +Routine Description: + + Dumps the most important fields of the specified ADDRESS object + +Arguments: + + args - Address of args string + +Return Value: + + None + +--*/ + +{ + ULONG addrobjToDump = 0; + + if (!*args) { + dprintf("No address object specified\n"); + } + else { + sscanf(args, "%lx", &addrobjToDump); + DumpAddrObj(addrobjToDump); + } + + return; +} + + +// +// Local functions +// + +VOID +PrintNetbiosName( + PUCHAR Name + ) +/*++ + +Routine Description: + + Prints out a Netbios name. + +Arguments: + + Name - The array containing the name to print. + +Return Value: + + None + +--*/ + +{ + ULONG i; + + for (i=0; i<16; i++) { + dprintf("%c", Name[i]); + } + return; +} + + +VOID +DumpAddrObj( + ULONG AddrObjToDump + ) + +/*++ + +Routine Description: + + Dumps the fields of the specified ADDRESS object + +Arguments: + + AddrObjToDump - The address object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ + +{ + ADDRESS addrobj; + ULONG result; + NBI_NETBIOS_ADDRESS nbaddr; + + + if (!ReadMemory( + AddrObjToDump, + &addrobj, + sizeof(addrobj), + &result + ) + ) + { + dprintf("%08lx: Could not read address object\n", AddrObjToDump); + return; + } + + if (addrobj.Type != NB_ADDRESS_SIGNATURE) { + dprintf("Signature does not match, probably not an address object\n"); + return; + } + + dprintf("NB Address:\n"); + PrintStart + PrintXULong(State); + PrintXULong(Flags); + PrintULong(ReferenceCount); + PrintLL(Linkage); + PrintEnd + + // Print the netbiosname info. + PrintFieldName("NetbiosName"); + PrintNetbiosName(addrobj.NetbiosAddress.NetbiosName); dprintf("\n"); + dprintf(" %25s = 0x%8x %25s = %10s\n", "NetbiosNameType",addrobj.NetbiosAddress.NetbiosNameType,"Broadcast",PRINTBOOL(addrobj.NetbiosAddress.Broadcast)); + + PrintStart + PrintLL(AddressFileDatabase); + PrintAddr(RegistrationTimer); + PrintXULong(RegistrationCount); + PrintPtr(SecurityDescriptor); + PrintEnd + return; +} + + +/////////////////////////////////////////////////////////////////////// +// CONNECTION_FILE +////////////////////////////////////////////////////////////////////// +#undef _obj +#undef _objAddr +#undef _objType +#define _obj conn +#define _objAddr ConnToDump +#define _objType CONNECTION + + +DECLARE_API( nbconn ) + +/*++ + +Routine Description: + + Dumps the most important fields of the specified CONNECTION object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ + +{ + ULONG connToDump = 0; + + if (!*args) { + dprintf("No conn specified\n"); + } + else { + sscanf(args, "%lx", &connToDump); + DumpConn(connToDump, FALSE); + } + + return; +} + + +DECLARE_API( nbconnfull ) + +/*++ + +Routine Description: + + Dumps all of the fields of the specified CONNECTION object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ + +{ + ULONG connToDump = 0; + + if (!*args) { + dprintf("No conn specified\n"); + } + else { + sscanf(args, "%lx", &connToDump); + DumpConn(connToDump, TRUE); + } + + return; +} + + +// +// Local functions +// +VOID +printSendPtr( + PSEND_POINTER SendPtr, + PSEND_POINTER UnAckedPtr + ) +{ + dprintf(" CurrentSend UnackedSend\n"); + dprintf(" MessageOffset 0x%-8lx 0x%-8lx\n", SendPtr->MessageOffset,UnAckedPtr->MessageOffset); + dprintf(" Request 0x%-8lx 0x%-8lx\n", SendPtr->Request,UnAckedPtr->Request); + dprintf(" Buffer 0x%-8lx 0x%-8lx\n", SendPtr->Buffer,UnAckedPtr->Buffer); + dprintf(" BufferOffset 0x%-8lx 0x%-8lx\n", SendPtr->BufferOffset,UnAckedPtr->BufferOffset); + dprintf(" SendSequence 0x%-8x 0x%-8x\n", SendPtr->SendSequence,UnAckedPtr->SendSequence); +} + +VOID +printRcvPtr( + PRECEIVE_POINTER CurrentPtr, + PRECEIVE_POINTER PreviousPtr + ) +{ + dprintf(" CurrentReceive PreviousReceive\n"); + dprintf(" MessageOffset 0x%-8lx 0x%-8lx\n", CurrentPtr->MessageOffset,PreviousPtr->MessageOffset); + dprintf(" Offset 0x%-8lx 0x%-8lx\n", CurrentPtr->Offset,PreviousPtr->Offset); + dprintf(" Buffer 0x%-8lx 0x%-8lx\n", CurrentPtr->Buffer,PreviousPtr->Buffer); + dprintf(" BufferOffset 0x%-8lx 0x%-8lx\n", CurrentPtr->BufferOffset,PreviousPtr->BufferOffset); +} + +VOID +DumpConn( + ULONG ConnToDump, + BOOLEAN Full + ) + +/*++ + +Routine Description: + + Dumps the fields of the specified CONNECTION object + +Arguments: + + ConnToDump - The conn object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ + +{ + CONNECTION conn; + ULONG result; + + + if (!ReadMemory( + ConnToDump, + &conn, + sizeof(conn), + &result + ) + ) + { + dprintf("%08lx: Could not read conn\n", ConnToDump); + return; + } + + if (conn.Type != NB_CONNECTION_SIGNATURE) { + dprintf("Signature does not match, probably not a conn\n"); + return; + } + + dprintf("NBI Connection General:\n"); + PrintStart + PrintXULong(State); + PrintXULong(SubState); + PrintXULong(ReceiveState); + PrintXULong(ReferenceCount); + PrintXUShort(LocalConnectionId); + PrintXUShort(RemoteConnectionId); + PrintAddr(LocalTarget); + PrintAddr(RemoteHeader); + PrintPtr(Context); + PrintPtr(AddressFile); + PrintXULong(AddressFileLinked); + PrintPtr(NextConnection); + + PrintEnd + + dprintf(" RemoteName = ");PrintNetbiosName((PUCHAR)conn.RemoteName);dprintf("\n"); + + dprintf("\n\nConnection Send Info:\n"); + + PrintStart + PrintIrpQ(SendQueue); + PrintXUShort(SendWindowSequenceLimit); + PrintXUShort(SendWindowSize); + PrintEnd + + printSendPtr( &conn.CurrentSend, &conn.UnAckedSend ); + + if( Full ) { + PrintStart + PrintXUShort(MaxSendWindowSize); + PrintBool(RetransmitThisWindow); + PrintBool(SendWindowIncrease); + PrintBool(ResponseTimeout); + PrintBool(SendBufferInUse); + PrintPtr(FirstMessageRequest); + PrintPtr(LastMessageRequest); + PrintXULong(MaximumPacketSize); + PrintXULong(CurrentMessageLength); + PrintEnd + } + + dprintf("\n\nConnection Receive Info:\n"); + PrintStart + PrintIrpQ(ReceiveQueue); + PrintXUShort(ReceiveSequence); + PrintXUShort(ReceiveWindowSize); + PrintXUShort(LocalRcvSequenceMax); + PrintXUShort(RemoteRcvSequenceMax); + PrintPtr(ReceiveRequest); + PrintXULong(ReceiveLength); + PrintEnd + + printRcvPtr( &conn.CurrentReceive, &conn.PreviousReceive ); + + if( Full ) { + PrintStart + PrintXULong(ReceiveUnaccepted); + PrintXULong(CurrentIndicateOffset); + PrintBool(NoPiggybackHeuristic); + PrintBool(PiggybackAckTimeout); + PrintBool(CurrentReceiveNoPiggyback); + PrintBool(DataAckPending); + PrintEnd + } + + if( Full ) { + PrintStart + PrintPtr(ListenRequest); + PrintPtr(AcceptRequest); + PrintPtr(ClosePending); + PrintPtr(DisassociatePending); + PrintPtr(DisconnectWaitRequest); + PrintPtr(DisconnectRequest); + PrintPtr(ConnectRequest); + PrintEnd + + PrintStart + PrintLL(PacketizeLinkage); + PrintBool(OnPacketizeQueue); + PrintLL(WaitPacketLinkage); + PrintBool(OnWaitPacketQueue); + PrintLL(DataAckLinkage); + PrintBool(OnDataAckQueue); + PrintBool(IgnoreNextDosProbe); + PrintXULong(NdisSendsInProgress); + PrintLL(NdisSendQueue); + PrintPtr(NdisSendReference); + PrintXULong(Retries); + PrintXULong(Status); + PrintBool(FindRouteInProgress); + PrintXULong(CanBeDestroyed); + PrintBool(OnShortList); + PrintLL(ShortList); + PrintLL(LongList); + PrintBool(OnLongList); + PrintXULong(BaseRetransmitTimeout); + PrintXULong(CurrentRetransmitTimeout); + PrintXULong(WatchdogTimeout); + PrintXULong(Retransmit); + PrintXULong(Watchdog); + + + PrintEnd + + PrintStart + PrintAddr(ConnectionInfo); + PrintAddr(Timer); + PrintAddr(FindRouteRequest); + PrintPtr(NextConnection); + PrintAddr(SessionInitAckData); + PrintXULong(SessionInitAckDataLength); + PrintAddr(SendPacket); + PrintAddr(SendPacketHeader); + PrintBool(SendPacketInUse); + PrintAddr(LineInfo); + +#ifdef RSRC_TIMEOUT_DBG + PrintXULong(FirstMessageRequestTime.HighPart); + PrintXULong(FirstMessageRequestTime.LowPart); +#endif RSRC_TIMEOUT_DBG + } + return; +} + +/////////////////////////////////////////////////////////////////////// +// DEVICE +////////////////////////////////////////////////////////////////////// + + +#undef _obj +#undef _objAddr +#undef _objType +#define _obj device +#define _objAddr deviceToDump +#define _objType DEVICE + +// +// Exported functions +// + +DECLARE_API( nbdev ) + +/*++ + +Routine Description: + + Dumps the most important fields of the specified DEVICE_CONTEXT object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ + +{ + ULONG deviceToDump = 0; + ULONG pDevice = 0; + ULONG result; + + if (!*args) { + + pDevice = GetExpression( "nwlnknb!NbiDevice" ); + + if ( !pDevice ) { + dprintf("Could not get NbiDevice, Try !reload\n"); + return; + } else { + + if (!ReadMemory(pDevice, + &deviceToDump, + sizeof(deviceToDump), + &result + ) + ) + { + dprintf("%08lx: Could not read device address\n", pDevice); + return; + } + } + + } + else { + sscanf(args, "%lx", &deviceToDump); + } + + + Dumpdevice(deviceToDump, FALSE); + + return; +} + + +DECLARE_API( nbdevfull ) + +/*++ + +Routine Description: + + Dumps all of the fields of the specified DEVICE_CONTEXT object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ + +{ + ULONG deviceToDump = 0; + ULONG pDevice = 0; + ULONG result; + + if (!*args) { + + pDevice = GetExpression( "nwlnknb!NbiDevice" ); + + if ( !pDevice ) { + dprintf("Could not get NbiDevice, Try !reload\n"); + return; + } else { + + if (!ReadMemory(pDevice, + &deviceToDump, + sizeof(deviceToDump), + &result + ) + ) + { + dprintf("%08lx: Could not read device address\n", pDevice); + return; + } + } + + } + else { + sscanf(args, "%lx", &deviceToDump); + } + + + Dumpdevice(deviceToDump, TRUE); + + return; +} + +// +// Local functions +// + +VOID +Dumpdevice( + ULONG deviceToDump, + BOOLEAN Full + ) + +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + deviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ + +{ + DEVICE device; + ULONG result; + + + if (!ReadMemory( + deviceToDump, + &device, + sizeof(device), + &result + ) + ) + { + dprintf("%08lx: Could not read device context\n", deviceToDump); + return; + } + + if (device.Type != NB_DEVICE_SIGNATURE) { + dprintf("Signature does not match, probably not a device object %lx\n",deviceToDump); + return; + } + + dprintf("Device General Info:\n"); + PrintStart + PrintXUChar(State); + PrintXULong(ReferenceCount); + PrintXUShort(MaximumNicId); + PrintXULong(MemoryUsage); + PrintXULong(MemoryLimit); + PrintXULong(AddressCount); + PrintXULong(AllocatedSendPackets); + PrintXULong(AllocatedReceivePackets); + PrintXULong(AllocatedReceiveBuffers); + PrintXULong(MaxReceiveBuffers); + PrintLL(AddressDatabase); + PrintL(SendPacketList); + PrintL(ReceivePacketList); + PrintLL(GlobalReceiveBufferList); + PrintLL(GlobalSendPacketList); + PrintLL(GlobalReceivePacketList); + PrintLL(GlobalReceiveBufferList); + PrintLL(SendPoolList); + PrintLL(ReceivePoolList); + PrintLL(ReceiveBufferPoolList); + PrintLL(ReceiveCompletionQueue); + PrintLL(WaitPacketConnections); + PrintLL(PacketizeConnections); + PrintLL(WaitingConnects); + PrintLL(WaitingDatagrams); + PrintLL(WaitingAdapterStatus); + PrintLL(WaitingNetbiosFindName); + PrintLL(ActiveAdapterStatus); + PrintLL(ReceiveDatagrams); + PrintLL(ConnectIndicationInProgress); + PrintLL(ListenQueue); + PrintLL(WaitingFindNames); + if ( Full ) { + PrintStart + PrintBool(UnloadWaiting); + PrintBool(DataAckQueueChanged); + PrintBool(ShortListActive); + PrintBool(DataAckActive); + PrintBool(TimersInitialized); + PrintBool(ProcessingShortTimer); + PrintAddr(ShortTimerStart); + PrintAddr(ShortTimer); + PrintXULong(ShortAbsoluteTime); + PrintAddr(LongTimer); + PrintXULong(LongAbsoluteTime); + PrintLL(ShortList); + PrintLL(LongList); + PrintAddr(TimerLock); + PrintEnd + } + + if ( Full ) { + PrintStart + PrintXUShort(FindNameTime); + PrintBool(FindNameTimerActive); + PrintAddr(FindNameTimer); + PrintXULong(FindNameTimeout); + PrintXULong(FindNamePacketCount); + PrintLL(WaitingFindNames); + PrintEnd + + PrintStart + PrintXULong(AckDelayTime ); + PrintXULong(AckWindow ); + PrintXULong(AckWindowThreshold ); + PrintXULong(EnablePiggyBackAck ); + PrintXULong(Extensions ); + PrintXULong(RcvWindowMax ); + PrintXULong(BroadcastCount ); + PrintXULong(BroadcastTimeout ); + PrintXULong(ConnectionCount ); + PrintXULong(ConnectionTimeout ); + PrintXULong(InitPackets ); + PrintXULong(MaxPackets ); + PrintXULong(InitialRetransmissionTime); + PrintXULong(Internet ); + PrintXULong(KeepAliveCount ); + PrintXULong(KeepAliveTimeout ); + PrintXULong(RetransmitMax ); + PrintXULong(RouterMtu); + PrintEnd + } + + PrintPtr(NameCache); + PrintXUShort(CacheTimeStamp); + PrintAddr(Bind); + PrintAddr( ConnectionHash); + PrintAddr( ConnectionlessHeader ); + PrintAddr( UnloadEvent ); + PrintAddr(Information); + PrintAddr(Statistics); + + PrintEnd + + return; +} + diff --git a/private/ntos/tdi/isn/isnext/precomp.h b/private/ntos/tdi/isn/isnext/precomp.h new file mode 100644 index 000000000..a9b5cc014 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/precomp.h @@ -0,0 +1,41 @@ +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include +#include +#include +#include + +// +// Prevent hal.h, included in ntos.h from overriding _BUS_DATA_TYPE +// enum found in ntioapi.h, included from nt.h. +// +#define _HAL_ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include diff --git a/private/ntos/tdi/isn/isnext/spxext.c b/private/ntos/tdi/isn/isnext/spxext.c new file mode 100644 index 000000000..429b80a80 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/spxext.c @@ -0,0 +1,1035 @@ +#include "precomp.h" +#pragma hdrstop + +#include + +VOID +DumpSpxDevice +( + ULONG DeviceToDump, + VERBOSITY Verbosity +); + +VOID +DumpSpxAddress +( + ULONG _objAddr, + VERBOSITY Verbosity +); + +VOID +DumpSpxAddressFile +( + ULONG _objAddr, + VERBOSITY Verbosity +); + +VOID +DumpSpxConnFile +( + ULONG _objAddr, + VERBOSITY Verbosity +); + +PSPX_ADDR +NextSpxAddr +( + PSPX_ADDR pAddr +); + +ENUM_INFO EnumConnFileMain[] = +{ + { SPX_CONNFILE_ACTIVE, "Active" }, + { SPX_CONNFILE_CONNECTING, "Connecting" }, + { SPX_CONNFILE_LISTENING, "Listening" }, + { SPX_CONNFILE_DISCONN, "Disconn" }, + { 0, NULL } +}; + +ENUM_INFO EnumConnFileConnecting[] = +{ + { SPX_CONNECT_SENTREQ, "SentReq" }, + { SPX_CONNECT_NEG, "Neg" }, + { SPX_CONNECT_W_SETUP, "W_Setup" }, + { 0, NULL } +}; + +ENUM_INFO EnumConnFileListening[] = +{ + { SPX_LISTEN_RECDREQ, "RecdReq" }, + { SPX_LISTEN_SENTACK, "SentAck" }, + { SPX_LISTEN_NEGACK, "NegAck" }, + { SPX_LISTEN_SETUP, "Setup" }, + { 0, NULL } +}; + +ENUM_INFO EnumConnFileSend[] = +{ + { SPX_SEND_IDLE, "Idle" }, + { SPX_SEND_PACKETIZE, "Packetize" }, + { SPX_SEND_RETRY, "Retry" }, + { SPX_SEND_RETRYWD, "RetryWd" }, + { SPX_SEND_RENEG, "Reneg" }, + { SPX_SEND_RETRY2, "Retry2" }, + { SPX_SEND_RETRY3, "Retry3" }, + { SPX_SEND_WD, "Wd" }, + { SPX_SEND_NAK_RECD, "Nak_Recd" }, + { 0, NULL } +}; + +ENUM_INFO EnumConnFileReceive[] = +{ + { SPX_RECV_IDLE, "Idle" }, + { SPX_RECV_POSTED, "Posted" }, + { SPX_RECV_PROCESS_PKTS, "Process_Pkts" }, + { 0, NULL } +}; + +ENUM_INFO EnumConnFileDisconnect[] = +{ + { SPX_DISC_IDLE, "Idle" }, + { SPX_DISC_ABORT, "Abort" }, + { SPX_DISC_SENT_IDISC, "Sent_IDisc" }, + { SPX_DISC_POST_ORDREL, "Post_OrdRel" }, + { SPX_DISC_SENT_ORDREL, "Sent_OrdRel" }, + { SPX_DISC_ORDREL_ACKED, "OrdRel_Acked" }, + { SPX_DISC_POST_IDISC, "Post_IDisc" }, + { SPX_DISC_INACTIVATED, "Inactivated" }, + { 0, NULL } +}; + +FLAG_INFO FlagsConnFile[] = +{ + { SPX_CONNFILE_RECVQ, "RecvQ" }, + { SPX_CONNFILE_RENEG_SIZE, "Reneg_Size" }, + { SPX_CONNFILE_ACKQ, "AckQ" }, + { SPX_CONNFILE_PKTQ, "PktQ" }, + { SPX_CONNFILE_ASSOC, "Assoc" }, + { SPX_CONNFILE_NEG, "Neg" }, + { SPX_CONNFILE_SPX2, "SPX2" }, + { SPX_CONNFILE_STREAM, "Stream" }, + { SPX_CONNFILE_R_TIMER, "R_Timer" }, + { SPX_CONNFILE_C_TIMER, "C_Timer" }, + { SPX_CONNFILE_W_TIMER, "W_Timer" }, + { SPX_CONNFILE_T_TIMER, "T_Timer" }, + { SPX_CONNFILE_RENEG_PKT, "Reneg_Pkt" }, + { SPX_CONNFILE_IND_IDISC, "Ind_IDisc" }, + { SPX_CONNFILE_IND_ODISC, "Ind_ODisc" }, + { SPX_CONNFILE_STOPPING, "Stopping" }, + { SPX_CONNFILE_CLOSING, "Closing" }, + { 0, NULL } +}; + +FLAG_INFO Flags2ConnFile[] = +{ + { SPX_CONNFILE2_PKT_NOIND, "Pkt_Noind" }, + { SPX_CONNFILE2_RENEGRECD, "RenegRecd" }, + { SPX_CONNFILE2_PKT, "Pkt" }, + { SPX_CONNFILE2_FINDROUTE, "FindRoute" }, + { SPX_CONNFILE2_NOACKWAIT, "NoAckWait" }, + { SPX_CONNFILE2_IMMED_ACK, "Immed_Ack" }, + { SPX_CONNFILE2_IPXHDR, "IpxHdr" }, + { 0, NULL } +}; + +MEMBER_TABLE SpxConnFileMembers[] = +{ + /* + { "scf_DiscLinkage", + FIELD_OFFSET( SPX_CONN_FILE, scf_DiscLinkage ), + xxxxxxx, + FIELD_OFFSET( NDIS_PACKET, ProtocolReserved ) + FIELD_OFFSET( IPX_SEND_RESERVED, GlobalLinkage ) + }, + */ + { NULL } +}; + +ENUM_INFO EnumSpxSendReqType[] = +{ + EnumString( SPX_REQ_DATA ), + EnumString( SPX_REQ_ORDREL ), + EnumString( SPX_REQ_DISC ), + { 0, NULL } +}; + +VOID dprint_addr_list( ULONG FirstAddress, ULONG OffsetToNextPtr ) +{ + ULONG Address; + ULONG result; + int index; + + Address = FirstAddress; + + if ( Address == (ULONG)NULL ) + { + dprintf( "%08X (Empty)\n", Address ); + return; + } + + dprintf( "{ " ); + + for ( index = 0; Address != (ULONG)NULL; index ++ ) + { + if ( index != 0 ) + { + dprintf( ", "); + } + dprintf( "%08X", Address ); + + if ( !ReadMemory( Address + OffsetToNextPtr, + &Address, + sizeof( Address ), + &result )) + { + dprintf( "ReadMemory() failed." ); + Address = (ULONG)NULL; + } + } + dprintf( " }\n" ); +} + +DECLARE_API( spxdev ) +/*++ + +Routine Description: + + Dumps the most important fields of the specified DEVICE_CONTEXT object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ +{ + ULONG deviceToDump = 0; + ULONG pDevice = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &deviceToDump); + } + + if ( deviceToDump == 0 ) { + + pDevice = GetExpression( "nwlnkspx!SpxDevice" ); + + if ( !pDevice ) { + dprintf("Could not get nwlnkspx!SpxDevice, Try !reload\n"); + return; + } else { + + if ( !ReadMemory( pDevice, + &deviceToDump, + sizeof(deviceToDump), + &result )) + { + dprintf("%08lx: Could not read device address\n", pDevice); + return; + } + } + } + + if ( bFocusOnMemberVariable ) + { +// if ( !LocateMemberVariable( "IpxDevice", VarName, ( PVOID )deviceToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpSpxDevice( deviceToDump, VERBOSITY_NORMAL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +# undef _objTypeName +#endif + +#define _obj Device +#define _objAddr DeviceToDump +#define _objType DEVICE +#define _objTypeName "DEVICE" + +VOID +DumpSpxDevice +( + ULONG DeviceToDump, + VERBOSITY Verbosity +) +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + DeviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ +{ + _objType _obj; + ULONG result; + unsigned int index; + WCHAR Buffer[ 1000 ]; + PWCHAR pDeviceName = NULL; + PSPX_ADDR pAddr; + + if ( !ReadMemory( _objAddr, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read %s structure.\n", _objAddr, _objTypeName ); + return; + } + + if (Device.dev_Type != SPX_DEVICE_SIGNATURE) + { + dprintf( "Signature does not match, probably not a device object %lx\n", DeviceToDump); + dprintf( "Device.Type == %04X, and I think it should be %04X\n", Device.dev_Type, SPX_DEVICE_SIGNATURE ); + dprintf( "DeviceToDump = %08X\n", DeviceToDump ); + dprintf( "Offset to Device.Type = %d\n", FIELD_OFFSET( DEVICE, dev_Type ) ); + return; + } + + if ( !ReadMemory( ( ULONG )_obj.dev_DeviceName, + Buffer, + sizeof( WCHAR ) * _obj.dev_DeviceNameLen, + &result )) + { + dprintf("%08lx: Could not read device name buffer\n", _obj.dev_DeviceName ); + } + else + { + pDeviceName = Buffer; + } + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprintf( "\"%S\"", pDeviceName ); + return; + } + + PrintStartStruct(); + + PrintPtr( dev_DevObj ); + +#if DBG +# if DREF_TOTAL != 5 +# error DREF_TOTAL is assumed to equal 5 +# endif + PrintULong( dev_RefTypes[ DREF_CREATE ] ); + PrintULong( dev_RefTypes[ DREF_LOADED ] ); + PrintULong( dev_RefTypes[ DREF_ADAPTER ] ); + PrintULong( dev_RefTypes[ DREF_ADDRESS ] ); + PrintULong( dev_RefTypes[ DREF_ORPHAN ] ); +#endif + + PrintUShort( dev_Type ); + PrintUShort( dev_Size ); + +#if DBG + PrintNChar( dev_Signature1, sizeof( _obj.dev_Signature1 )); +#endif + + PrintULong( dev_RefCount ); + PrintUChar( dev_State ); + + PrintUShort( dev_Adapters ); + + PrintLock( dev_Interlock ); + PrintLock( dev_Lock ); + + for ( index = 0; index < NUM_SPXADDR_HASH_BUCKETS; index ++ ) + { + PrintIndent(); + dprintf( "dev_AddrHashTable[ %d ] = ", index ); + + dprint_addr_list( ( ULONG )_obj.dev_AddrHashTable[ index ], + FIELD_OFFSET( SPX_ADDR, sa_Next )); + } + + for ( index = 0; index < NUM_SPXCONN_HASH_BUCKETS; index ++ ) + { + PrintIndent(); + dprintf( "dev_GlobalActiveConnList[ %d ] = ", index ); + + dprint_addr_list( ( ULONG )_obj.dev_GlobalActiveConnList[ index ], + FIELD_OFFSET( SPX_CONN_FILE, scf_Next )); + } + + PrintUShort( dev_NextConnId ); + + PrintUShort( dev_CurrentSocket ); + + PrintFieldName( "dev_Network" ); + dprintf("0x%-8hx%s", *(( ULONG *)_obj.dev_Network ), EOL); + + PrintFieldName( "dev_Node" ); + dprint_hardware_address( _obj.dev_Node ); + dprintf( "%s", EOL ); + + PrintPtr( dev_ConfigInfo ); + + PrintULong( dev_CcId ); + + PrintJoin(); + PrintPtr( dev_DeviceName ); + dprintf( "\"%S\"\n", pDeviceName ); + + PrintULong( dev_DeviceNameLen ); + +#if DBG + PrintNChar( dev_Signature2, sizeof( _obj.dev_Signature2 )); +#endif + + PrintAddr( dev_NdisBufferPoolHandle ); + + PrintAddr( dev_StatInterlock ); + PrintAddr( dev_StatSpinLock ); + + PrintAddr( dev_Stat ); + PrintAddr( dev_AddrResource ); + + PrintAddr( dev_ProviderInfo ); + + PrintEndStruct(); +} + +DECLARE_API( spxaddr ) +/*++ + +Routine Description: + + Dumps the most important fields of the specified DEVICE_CONTEXT object + +Arguments: + + args - Address + +Return Value: + + None + +--*/ +{ + ULONG addressToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &addressToDump); + } + + if ( bFocusOnMemberVariable ) + { +// if ( !LocateMemberVariable( "IpxDevice", VarName, ( PVOID )deviceToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpSpxAddress( addressToDump, VERBOSITY_NORMAL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +# undef _objTypeName +#endif + +#define _obj Address +#define _objAddr AddressToDump +#define _objType SPX_ADDR +#define _objTypeName "SPX_ADDR" + +VOID +DumpSpxAddress +( + ULONG _objAddr, + VERBOSITY Verbosity +) +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + DeviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ +{ + _objType _obj; + ULONG result; + unsigned int index; + + if ( !ReadMemory( _objAddr, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read %s structure.\n", _objAddr, _objTypeName ); + return; + } + + CHECK_SIGNATURE( sa_Type, SPX_ADDRESS_SIGNATURE ); + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + dprintf( "Socket = %d", _obj.sa_Socket ); + if ( _obj.sa_Flags & SPX_ADDR_CLOSING ) + { + dprintf(" (CLOSING)"); + } + return; + } + + PrintStartStruct(); + +#if DBG +# if AREF_TOTAL !=4 +# error AREF_TOTAL is assumed to equal 4 +# endif + + PrintULong( sa_RefTypes[ AREF_ADDR_FILE ] ); + PrintULong( sa_RefTypes[ AREF_LOOKUP ] ); + PrintULong( sa_RefTypes[ AREF_RECEIVE ] ); + PrintULong( sa_RefTypes[ 3 ] ); +#endif + + PrintUShort( sa_Size ); + PrintXEnum( sa_Type, EnumStructureType ); + + PrintULong( sa_RefCount ); + + PrintPtr( sa_Next ); + PrintXULong( sa_Flags ); + + PrintFieldName( "sa_AddrFileList" ); + dprint_addr_list( ( ULONG )_obj.sa_AddrFileList, + FIELD_OFFSET( SPX_ADDR_FILE, saf_Next )); + + PrintFieldName( "sa_InactiveConnList" ); + dprint_addr_list( ( ULONG )_obj.sa_InactiveConnList, + FIELD_OFFSET( SPX_CONN_FILE, scf_Next )); + + PrintFieldName( "sa_ActiveConnList" ); + dprint_addr_list( ( ULONG )_obj.sa_ActiveConnList, + FIELD_OFFSET( SPX_CONN_FILE, scf_Next )); + + PrintFieldName( "sa_ListenConnList" ); + dprint_addr_list( ( ULONG )_obj.sa_ListenConnList, + FIELD_OFFSET( SPX_CONN_FILE, scf_Next )); + + PrintLock( sa_Lock ); + PrintUShort( sa_Socket ); + PrintPtr( sa_Device ); + PrintPtr( sa_DeviceLock ); + + PrintStartNamedStruct( "union" ); + + PrintAddr( u.sa_ShareAccess ); + PrintAddr( u.sa_DestroyAddrQueueItem ); + + PrintEndStruct(); + + PrintPtr( sa_SecurityDescriptor ); + PrintEndStruct(); +} + +DECLARE_API( spxaddrfile ) +{ + ULONG addressToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &addressToDump); + } + + if ( bFocusOnMemberVariable ) + { +// if ( !LocateMemberVariable( "IpxDevice", VarName, ( PVOID )deviceToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpSpxAddressFile( addressToDump, VERBOSITY_NORMAL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +# undef _objTypeName +#endif + +#define _obj AddressFile +#define _objAddr AddressFileToDump +#define _objType SPX_ADDR_FILE +#define _objTypeName "SPX_ADDR_FILE" + +VOID +DumpSpxAddressFile +( + ULONG _objAddr, + VERBOSITY Verbosity +) +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + DeviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ +{ + _objType _obj; + ULONG result; + unsigned int index; + + if ( !ReadMemory( _objAddr, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read %s structure.\n", _objAddr, _objTypeName ); + return; + } + + CHECK_SIGNATURE( saf_Type, SPX_ADDRESSFILE_SIGNATURE ); + + if ( Verbosity == VERBOSITY_ONE_LINER ) + { + switch ( _obj.saf_Flags & 0x03 ) + { + case SPX_ADDRFILE_OPENING: + dprintf( "OPENING " ); + break; + case SPX_ADDRFILE_OPEN: + dprintf( "OPEN " ); + break; + case SPX_ADDRFILE_CLOSING: + dprintf( "CLOSING " ); + break; + } + DumpSpxAddress( ( ULONG )_obj.saf_Addr, VERBOSITY_ONE_LINER ); + return; + } + + PrintStartStruct(); +#if DBG +# if AFREF_TOTAL !=4 +# error AFREF_TOTAL is assumed equal to 4. +# endif + + PrintULong( saf_RefTypes[ AFREF_CREATE ] ); + PrintULong( saf_RefTypes[ AFREF_VERIFY ] ); + PrintULong( saf_RefTypes[ AFREF_INDICATION ] ); + PrintULong( saf_RefTypes[ AFREF_CONN_ASSOC ] ); +#endif + + PrintXEnum( saf_Type, EnumStructureType ); + PrintUShort( saf_Size ); + + PrintULong( saf_RefCount ); + PrintFieldName( "saf_Next" ); + dprint_addr_list( ( ULONG )_obj.saf_Next, + FIELD_OFFSET( SPX_ADDR_FILE, saf_Next )); + + PrintFieldName( "saf_GlobalNext" ); + dprint_addr_list( ( ULONG )_obj.saf_GlobalNext, + FIELD_OFFSET( SPX_ADDR_FILE, saf_GlobalNext)); + + PrintFieldName( "saf_AssocConnList" ); + dprint_addr_list( ( ULONG )_obj.saf_AssocConnList, + FIELD_OFFSET( SPX_CONN_FILE, scf_AssocNext )); + + PrintXUShort( saf_Flags ); + PrintPtr( saf_Addr ); + PrintPtr( saf_AddrLock ); + + PrintPtr( saf_FileObject ); + PrintPtr( saf_Device ); + PrintPtr( saf_CloseReq ); + + PrintSymbolPtr( saf_ConnHandler ); + PrintXULong( saf_ConnHandlerCtx ); + + PrintSymbolPtr( saf_DiscHandler ); + PrintXULong( saf_DiscHandlerCtx ); + + PrintSymbolPtr( saf_RecvHandler ); + PrintXULong( saf_RecvHandlerCtx ); + + PrintSymbolPtr( saf_SendPossibleHandler ); + PrintXULong( saf_SendPossibleHandlerCtx ); + + PrintSymbolPtr( saf_ErrHandler ); + PrintXULong( saf_ErrHandlerCtx ); + + PrintPtr( saf_ErrHandlerOwner ); + + PrintEndStruct(); +} + +DECLARE_API( spxconnfile ) +{ + ULONG addressToDump = 0; + ULONG result; + char VarName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + MEMBER_VARIABLE_INFO MemberInfo; + BOOL bFocusOnMemberVariable = FALSE; + + if ( *args ) + { + bFocusOnMemberVariable = ReadArgsForTraverse( args, VarName ); + } + + if ( *args && *args!='-' ) + { + sscanf(args, "%lx", &addressToDump); + } + + if ( bFocusOnMemberVariable ) + { + if ( !LocateMemberVariable( "SpxConnFile", VarName, ( PVOID )addressToDump, &MemberInfo )) + { + return; + } + + WriteMemberInfo( &MemberInfo ); + next( hCurrentProcess, hCurrentThread, dwCurrentPc, dwProcessor, "" ); + return; + } + + DumpSpxConnFile( addressToDump, VERBOSITY_NORMAL ); + + return; +} + +#ifdef _obj +# undef _obj +# undef _objAddr +# undef _objType +# undef _objTypeName +#endif + +#define _obj ConnFile +#define _objAddr ConnFileToDump +#define _objType SPX_CONN_FILE +#define _objTypeName "SPX_CONN_FILE" + +VOID +DumpSpxConnFile +( + ULONG _objAddr, + VERBOSITY Verbosity +) +/*++ + +Routine Description: + + Dumps the fields of the specified DEVICE_CONTEXT structure + +Arguments: + + DeviceToDump - The device context object to display + Full - Display a partial listing if 0, full listing otherwise. + +Return Value: + + None + +--*/ +{ + _objType _obj; + ULONG result; + unsigned int index; + BOOL bActive; + + if ( !ReadMemory( _objAddr, + &_obj, + sizeof( _obj ), + &result )) + { + dprintf("%08lx: Could not read %s structure.\n", _objAddr, _objTypeName ); + return; + } + + CHECK_SIGNATURE( scf_Type, SPX_CONNFILE_SIGNATURE ); + + if ( Verbosity = VERBOSITY_ONE_LINER ) + { + dprintf( "NOT IMPLEMENTED" ); + return; + } + + PrintStartStruct(); + +#if DBG +# if CFREF_TOTAL != 14 +# error CFREF_TOTAL is assumed equal to 13. +# endif + + PrintULong( scf_RefTypes[ CFREF_CREATE ] ); + PrintULong( scf_RefTypes[ CFREF_VERIFY ] ); + PrintULong( scf_RefTypes[ CFREF_INDICATION ] ); + PrintULong( scf_RefTypes[ CFREF_BYCTX ] ); + PrintULong( scf_RefTypes[ CFREF_BYID ] ); + PrintULong( scf_RefTypes[ CFREF_ADDR ] ); + PrintULong( scf_RefTypes[ CFREF_REQ ] ); + PrintULong( scf_RefTypes[ CFREF_TIMER ] ); + PrintULong( scf_RefTypes[ CFREF_PKTIZE ] ); + PrintULong( scf_RefTypes[ CFREF_RECV ] ); + PrintULong( scf_RefTypes[ CFREF_ABORTPKT ] ); + PrintULong( scf_RefTypes[ CFREF_ERRORSTATE ] ); + PrintULong( scf_RefTypes[ CFREF_FINDROUTE ] ); + PrintULong( scf_RefTypes[ CFREF_DISCWAITSPX ] ); +#endif + + PrintXEnum( scf_Type, EnumStructureType ); + PrintUShort( scf_Size ); + + PrintULong( scf_RefCount ); + + PrintFieldName( "scf_Next" ); + dprint_addr_list( ( ULONG )_obj.scf_Next, + FIELD_OFFSET( SPX_CONN_FILE, scf_Next )); + + PrintFieldName( "scf_AssocNext" ); + dprint_addr_list( ( ULONG )_obj.scf_AssocNext, + FIELD_OFFSET( SPX_CONN_FILE, scf_AssocNext )); + + PrintFieldName( "scf_GlobalActiveNext" ); + dprint_addr_list( ( ULONG )_obj.scf_GlobalActiveNext, + FIELD_OFFSET( SPX_CONN_FILE, scf_GlobalActiveNext )); + + PrintFieldName( "scf_GlobalNext" ); + dprint_addr_list( ( ULONG )_obj.scf_GlobalNext, + FIELD_OFFSET( SPX_CONN_FILE, scf_GlobalNext )); + + PrintFieldName( "scf_PktNext" ); + dprint_addr_list( ( ULONG )_obj.scf_PktNext, + FIELD_OFFSET( SPX_CONN_FILE, scf_PktNext )); + + PrintFieldName( "scf_ProcessRcvNext" ); + dprint_addr_list( ( ULONG )_obj.scf_ProcessRecvNext, + FIELD_OFFSET( SPX_CONN_FILE, scf_ProcessRecvNext )); + + bActive = ( _obj.scf_Flags & SPX_CONNFILE_MAINMASK ) == SPX_CONNFILE_ACTIVE; + + PrintXULong( scf_Flags ); + PrintXEnumMask( scf_Flags, EnumConnFileMain, SPX_CONNFILE_MAINMASK ); + + if (( _obj.scf_Flags & SPX_CONNFILE_MAINMASK ) == SPX_CONNFILE_LISTENING ) + { + PrintXEnumMask( scf_Flags, EnumConnFileListening, SPX_LISTEN_MASK ); + } + + if (( _obj.scf_Flags & SPX_CONNFILE_MAINMASK ) == SPX_CONNFILE_CONNECTING ) + { + PrintXEnumMask( scf_Flags, EnumConnFileConnecting, SPX_CONNECT_MASK ); + } + + if ( bActive ) + { + PrintXEnumMask( scf_Flags, EnumConnFileSend, SPX_SEND_MASK ); + PrintXEnumMask( scf_Flags, EnumConnFileReceive, SPX_RECV_MASK ); + } + + if ( (( _obj.scf_Flags & SPX_CONNFILE_MAINMASK ) == SPX_CONNFILE_LISTENING ) || + bActive ) + { + PrintXEnumMask( scf_Flags, EnumConnFileDisconnect, SPX_DISC_MASK ); + } + + PrintFlagsMask( scf_Flags, FlagsConnFile, 0xFFFF0000 ); + + PrintFlags( scf_Flags2, Flags2ConnFile ); + +#if DBG + PrintXULong( scf_GhostFlags ); + PrintXULong( scf_GhostFlags2 ); + PrintULong( scf_GhostRefCount ); + PrintPtr( scf_GhostDiscReq ); +#endif + + if ( bActive ) + { + PrintULong( scf_WRetryCount ); + } + else + { + PrintULong( scf_CRetryCount ); + } + + PrintULong( scf_RRetryCount ); + PrintXUShort( scf_RRetrySeqNum ); + + if ( bActive ) + { + PrintULong( scf_RTimerId ); + } + else + { + PrintULong( scf_CTimerId ); + } + + + PrintULong( scf_WTimerId ); + PrintULong( scf_TTimerId ); + PrintULong( scf_ATimerId ); + + PrintULong( scf_BaseT1 ); + PrintULong( scf_AveT1 ); + PrintULong( scf_DevT1 ); + + PrintUShort( scf_LocalConnId ); + PrintUShort( scf_SendSeqNum ); + PrintUShort( scf_SentAllocNum ); + PrintUShort( scf_RecvSeqNum ); + PrintUShort( scf_RecdAckNum ); + PrintUShort( scf_RecdAllocNum ); + PrintUShort( scf_RetrySeqNum ); + + PrintUShort( scf_RenegAckAckNum ); + + PrintNChar( scf_RemAddr, sizeof( _obj.scf_RemAddr )); + PrintNChar( scf_RemAckAddr, sizeof( _obj.scf_RemAckAddr )); + + PrintUShort( scf_RemConnId ); + PrintUShort( scf_RenegMaxPktSize ); + + PrintIpxLocalTarget( scf_AckLocalTarget ); + + PrintUShort( scf_MaxPktSize ); + PrintUChar( scf_DataType ); + + PrintIpxLocalTarget( scf_LocalTarget ); + PrintLock( scf_Lock ); + + PrintJoin(); + PrintPtr( scf_AddrFile ); + dprintf( "(" ); + DumpSpxAddressFile( ( ULONG )_obj.scf_AddrFile, VERBOSITY_ONE_LINER ); + dprintf( ")\n" ); + + PrintPtr( scf_ConnCtx ); + + PrintPtr( scf_FileObject ); + + PrintLL( scf_DiscLinkage ); + PrintLL( scf_ReqLinkage ); + PrintLL( scf_ReqDoneLinkage ); + PrintLL( scf_RecvDoneLinkage ); + PrintLL( scf_RecvLinkage ); + PrintPtr( scf_CurRecvReq ); + PrintULong( scf_CurRecvOffset ); + PrintULong( scf_CurRecvSize ); + PrintPtr( scf_ReqPkt ); + PrintULong( scf_ReqPktOffset ); + PrintULong( scf_ReqPktSize ); + PrintULong( scf_ReqPktFlags ); + PrintEnum( scf_ReqPktType, EnumSpxSendReqType ); + PrintPtr( scf_SendSeqListHead ); + PrintPtr( scf_SendSeqListTail ); + PrintPtr( scf_SendListHead ); + PrintPtr( scf_SendListTail ); + PrintPtr( scf_RecvListHead ); + PrintPtr( scf_RecvListTail ); + PrintPtr( scf_ConnectReq ); + PrintPtr( scf_CleanupReq ); + PrintPtr( scf_CloseReq ); + +#if DBG + PrintUShort( scf_PktSeqNum ); + PrintXULong( scf_PktFlags ); + PrintXULong( scf_PktFlags2 ); + + PrintULong( scf_IndBytes ); + PrintULong( scf_IndLine ); +#endif + +#if DBG_WDW_CLOSE + PrintULong( scf_WdwCloseAve ); + PrintAddr( scf_WdwCloseTime ); +#endif + + PrintPtr( scf_Device ); + + PrintEndStruct(); +} + diff --git a/private/ntos/tdi/isn/isnext/spxext.h b/private/ntos/tdi/isn/isnext/spxext.h new file mode 100644 index 000000000..ec94d99c3 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/spxext.h @@ -0,0 +1,16 @@ +#if !defined( INCLUDED_SPXEXT_H ) +#define INCLUDED_SPXEXT_H + +extern MEMBER_TABLE SpxConnFileMembers[]; + +VOID +DumpSpxConnFile +( + ULONG DeviceToDump, + VERBOSITY Verbosity +); + +#define SPX_MAJOR_STRUCTURES \ +{ "SpxConnFile", SpxConnFileMembers, DumpSpxConnFile } + +#endif diff --git a/private/ntos/tdi/isn/isnext/traverse.c b/private/ntos/tdi/isn/isnext/traverse.c new file mode 100644 index 000000000..98715779c --- /dev/null +++ b/private/ntos/tdi/isn/isnext/traverse.c @@ -0,0 +1,419 @@ +#include "precomp.h" +#pragma hdrstop + +MEMBER_VARIABLE_INFO _MemberInfo; + + +#define STATE_FILENAME "isnext.state" + +STRUCTURE_TABLE StructureTable[] = +{ + IPX_MAJOR_STRUCTURES, + SPX_MAJOR_STRUCTURES, + { NULL } +}; + +BOOL NextListEntry( ULONG Current, PULONG Next ); +BOOL PrevListEntry( ULONG Current, PULONG Prev ); + +VOID NextElement( PMEMBER_VARIABLE_INFO pMemberInfo ); +VOID PrevElement( PMEMBER_VARIABLE_INFO pMemberInfo ); +VOID DumpListItem( PMEMBER_VARIABLE_INFO pMemberInfo ); + + +BOOL +LocateMemberVariable +( + PCHAR pchStructName, + PCHAR pchMemberName, + PVOID pvStructure, + PMEMBER_VARIABLE_INFO pMemberInfo +) +{ + BOOL bMatch; + int index; + PMEMBER_TABLE pMemberTable; + CHAR pchCurrent[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + CHAR _pchStructName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + CHAR _pchMemberName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ]; + + dprintf( "LocateMemberVariable( \"%s\", \"%s\", 0x%08X )\n", pchStructName, pchMemberName, pMemberInfo ); + + strcpy( _pchStructName, pchStructName ); + strcpy( _pchMemberName, pchMemberName ); + + _strupr( _pchStructName ); + _strupr( _pchMemberName ); + + pMemberInfo->StructureIndex = 0; + pMemberInfo->MemberIndex = 0; + + bMatch = FALSE; + + for ( index = 0; StructureTable[ index ].pchStructName != NULL; index ++ ) + { + strcpy( pchCurrent, StructureTable[ index ].pchStructName ); + _strupr( pchCurrent ); + + if ( strstr( pchCurrent, _pchStructName )) + { + if ( bMatch ) + { + dprintf( "The specified structure name is ambiguous.\n" ); + return( FALSE ); + } + + pMemberInfo->StructureIndex = index; + + bMatch = TRUE; + } + } + + if ( !bMatch ) + { + dprintf( "No matching structure name was found.\n" ); + return( FALSE ); + } + + pMemberTable = StructureTable[ pMemberInfo->StructureIndex ].pMemberTable; + + bMatch = FALSE; + + for ( index = 0; pMemberTable[ index ].pchMemberName != NULL; index ++ ) + { + strcpy( pchCurrent, pMemberTable[ index ].pchMemberName ); + _strupr( pchCurrent ); + + if ( strstr( pchCurrent, _pchMemberName )) + { + if ( bMatch ) + { + dprintf( "The variable specified is ambiguous.\n" ); + return( FALSE ); + } + + pMemberInfo->MemberIndex = index; + + bMatch = TRUE; + } + } + + if ( !bMatch ) + { + dprintf( "No matching member name was found in the %s structure.\n", pchStructName ); + return( FALSE ); + } + + pMemberInfo->prHeadContainingObject = ( ULONG )pvStructure; + pMemberInfo->prHeadLinkage = (( ULONG )pvStructure ) + pMemberTable[ pMemberInfo->MemberIndex ].cbOffsetToHead; + pMemberInfo->prCurrentLinkage = pMemberInfo->prHeadLinkage; + pMemberInfo->cCurrentElement = 0; + + return( TRUE ); +} + +BOOL WriteMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo ) +{ + HANDLE hStateFile; + DWORD dwWritten; + + hStateFile = CreateFile( STATE_FILENAME, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if ( hStateFile == NULL ) + { + dprintf( "Can't create state file\n" ); + return( FALSE ); + } + + if ( !WriteFile( hStateFile, + pMemberInfo, + sizeof( MEMBER_VARIABLE_INFO ), + &dwWritten, + NULL ) || ( dwWritten != sizeof( MEMBER_VARIABLE_INFO ))) + { + dprintf( "Can't write to state file\n" ); + CloseHandle( hStateFile ); + return( FALSE ); + } + + CloseHandle( hStateFile ); + + return( TRUE ); +} + +BOOL ReadMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo ) +{ + HANDLE hStateFile; + DWORD dwRead; + + hStateFile = CreateFile( STATE_FILENAME, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if ( hStateFile == NULL ) + { + dprintf( "Can't open state file\n" ); + return( FALSE ); + } + + if ( !ReadFile( hStateFile, + pMemberInfo, + sizeof( MEMBER_VARIABLE_INFO ), + &dwRead, + NULL ) || ( dwRead != sizeof( MEMBER_VARIABLE_INFO ))) + { + dprintf( "Can't read from state file\n" ); + CloseHandle( hStateFile ); + return( FALSE ); + } + + CloseHandle( hStateFile ); + return( TRUE ); +} + + +DECLARE_API( next ) +{ + MEMBER_VARIABLE_INFO MemberInfo; + + if ( !ReadMemberInfo( &MemberInfo ) ) + { + return; + } + + NextElement( &MemberInfo ); + DumpListItem( &MemberInfo ); + WriteMemberInfo( &MemberInfo ); +} + +DECLARE_API( prev ) +{ + MEMBER_VARIABLE_INFO MemberInfo; + + if ( !ReadMemberInfo( &MemberInfo ) ) + { + return; + } + + PrevElement( &MemberInfo ); + DumpListItem( &MemberInfo ); + WriteMemberInfo( &MemberInfo ); +} + + +VOID DumpListItem( PMEMBER_VARIABLE_INFO pMemberInfo ) +{ + PBYTE pbObject; + PMEMBER_TABLE pMemberTable; + + dprintf( "Focus is on: %s.%s, element # %d\n", + StructureTable[ pMemberInfo->StructureIndex ].pchStructName, + StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ].pchMemberName, + pMemberInfo->cCurrentElement ); + + pMemberTable = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ]; + + if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage ) + { + // + // Rather than dumping the head list item, dump all the items on the list, + // in summary form. + // + + do + { + NextElement( pMemberInfo ); + + if ( pMemberInfo->prCurrentLinkage != pMemberInfo->prHeadLinkage ) + { + pbObject = (( PBYTE )pMemberInfo->prCurrentLinkage ) + - pMemberTable->cbOffsetToLink; + + pMemberTable->DumpStructure( ( ULONG )pbObject, VERBOSITY_ONE_LINER ); + dprintf( "\n" ); + } + } while ( pMemberInfo->prCurrentLinkage != pMemberInfo->prHeadLinkage ); + } + else + { + pbObject = (( PBYTE )pMemberInfo->prCurrentLinkage ) + - pMemberTable->cbOffsetToLink; + + pMemberTable->DumpStructure( ( ULONG )pbObject, VERBOSITY_NORMAL ); + } +} + + +VOID NextElement( PMEMBER_VARIABLE_INFO pMemberInfo ) +{ + ULONG NextLinkage; + PMEMBER_TABLE pMember; + + pMember = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ]; + + if ( !pMember->Next( pMemberInfo->prCurrentLinkage, &NextLinkage )) + { + dprintf( "Command failed.\n" ); + return; + } + + pMemberInfo->prCurrentLinkage = NextLinkage; + pMemberInfo->cCurrentElement++; + + if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage ) + { + pMemberInfo->cCurrentElement = 0; + } +} + +BOOL NextListEntry( ULONG Current, PULONG Next ) +{ + ULONG result; + ULONG prNextEntry; + LIST_ENTRY Entry; + LIST_ENTRY NextEntry; + + if ( !ReadMemory( Current, + &Entry, + sizeof( Entry ), + &result )) + { + dprintf( "Couldn't read current list entry at 0x%08X.\n", Current ); + return( FALSE ); + } + + prNextEntry = ( ULONG )Entry.Flink; + + if ( !ReadMemory( prNextEntry, + &NextEntry, + sizeof( NextEntry ), + &result )) + { + dprintf( "Couldn't read next list entry at 0x%08X.\n", prNextEntry ); + return( FALSE ); + } + + if ( ( ULONG )NextEntry.Blink != Current ) + { + dprintf( "Next entry's Blink doesn't match current entry's address.\n" ); + dprintf( "The list might be corrupt, or you may be using traversal state saved before the list changed.\n" ); + return( FALSE ); + } + + *Next = prNextEntry; + return( TRUE ); +} + +VOID PrevElement( PMEMBER_VARIABLE_INFO pMemberInfo ) +{ + ULONG PrevLinkage; + PMEMBER_TABLE pMember; + + pMember = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ]; + + if ( !pMember->Prev( pMemberInfo->prCurrentLinkage, &PrevLinkage )) + { + dprintf( "Command failed.\n" ); + return; + } + + pMemberInfo->prCurrentLinkage = PrevLinkage; + pMemberInfo->cCurrentElement++; + + if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage ) + { + pMemberInfo->cCurrentElement = 0; + } +} + +BOOL PrevListEntry( ULONG Current, PULONG Prev ) +{ + ULONG result; + ULONG prPrevEntry; + LIST_ENTRY Entry; + LIST_ENTRY PrevEntry; + + if ( !ReadMemory( Current, + &Entry, + sizeof( Entry ), + &result )) + { + dprintf( "Couldn't read current list entry at 0x%08X.\n", Current ); + return( FALSE ); + } + + prPrevEntry = ( ULONG )Entry.Blink; + + if ( !ReadMemory( prPrevEntry, + &PrevEntry, + sizeof( PrevEntry ), + &result )) + { + dprintf( "Couldn't read previous list entry at 0x%08X.\n", prPrevEntry ); + return( FALSE ); + } + + if ( ( ULONG )PrevEntry.Blink != Current ) + { + dprintf( "Previous entry's Blink doesn't match current entry's address.\n" ); + dprintf( "The list might be corrupt, or you may be using traversal state saved before the list changed.\n" ); + return( FALSE ); + } + + *Prev = prPrevEntry; + return( TRUE ); +} + +BOOL ReadArgsForTraverse( const char *args, char *VarName ) +{ + PCHAR pchListVar; + int index; + BOOL bRetval = FALSE; + + pchListVar = strstr( args, "-l" ); + + if ( pchListVar ) + { + pchListVar += 2; + + while ( *pchListVar == ' ' ) + { + pchListVar ++; + } + + if ( *pchListVar == '\0' ) + { + dprintf( "NOT IMPLEMENTED: usage on -l\n" ); +// ipxdev_usage(); + return( bRetval ); + } + + for ( index = 0; index < MAX_LIST_VARIABLE_NAME_LENGTH; index ++ ) + { + VarName[ index ] = *pchListVar; + + if ( *pchListVar == ' ' || *pchListVar == '\0' ) + { + VarName[ index ] = '\0'; + break; + } + + VarName[ index + 1 ] = '\0'; + + pchListVar ++; + } + + bRetval = TRUE; + } + + return( bRetval ); +} diff --git a/private/ntos/tdi/isn/isnext/traverse.h b/private/ntos/tdi/isn/isnext/traverse.h new file mode 100644 index 000000000..287b57a13 --- /dev/null +++ b/private/ntos/tdi/isn/isnext/traverse.h @@ -0,0 +1,55 @@ +#if !defined( INCLUDED_TRAVERSE_H ) +#define INCLUDED_TRAVERSE_H + +#define MAX_LIST_VARIABLE_NAME_LENGTH 200 + +typedef struct +{ + int StructureIndex; + int MemberIndex; + + ULONG prHeadContainingObject; + ULONG prHeadLinkage; + ULONG prCurrentLinkage; + int cCurrentElement; +} MEMBER_VARIABLE_INFO, *PMEMBER_VARIABLE_INFO; + +typedef VOID (*pfDumpStructure)( ULONG , VERBOSITY ); +typedef BOOL (*pfNextStructure)( ULONG Current, PULONG Next ); +typedef BOOL (*pfPrevStructure)( ULONG Current, PULONG Prev ); + +typedef struct +{ + PCHAR pchMemberName; + + LONG cbOffsetToHead; + + pfDumpStructure DumpStructure; + pfNextStructure Next; + pfPrevStructure Prev; + LONG cbOffsetToLink; + +} MEMBER_TABLE, *PMEMBER_TABLE; + +typedef struct +{ + PCHAR pchStructName; + PMEMBER_TABLE pMemberTable; + + pfDumpStructure DumpStructure; +} STRUCTURE_TABLE, *PSTRUCTURE_TABLE; + +BOOL ReadArgsForTraverse( const char *args, char *VarName ); +BOOL ReadMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo ); +BOOL WriteMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo ); +BOOL LocateMemberVariable( PCHAR pchStructName, PCHAR pchMemberName, PVOID pvStructure, PMEMBER_VARIABLE_INFO pMemberInfo ); + + +DECLARE_API( next ); +DECLARE_API( prev ); + +extern BOOL NextListEntry( ULONG Current, PULONG Next ); +extern BOOL PrevListEntry( ULONG Current, PULONG Prev ); + + +#endif diff --git a/private/ntos/tdi/isn/nb/action.c b/private/ntos/tdi/isn/nb/action.c new file mode 100644 index 000000000..9ff843a76 --- /dev/null +++ b/private/ntos/tdi/isn/nb/action.c @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which implements the TDI action + dispatch routines. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles action requests. + +Arguments: + + Device - The netbios device. + + Request - The request describing the action. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + union { + PNB_ACTION_GET_COUNTS GetCounts; + } u; // BUGBUG: Make these unaligned?? + PNWLINK_ACTION NwlinkAction; + UINT i; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + NB_DEBUG (QUERY, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + NB_DEBUG (QUERY, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != NB_ADDRESSFILE_SIGNATURE)) { + + NB_DEBUG (QUERY, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + NB_DEBUG (QUERY, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + case (I_MIPX | 351): + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(NB_ACTION_GET_COUNTS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetCounts = (PNB_ACTION_GET_COUNTS)(NwlinkAction->Data); + + u.GetCounts->MaximumNicId = NbiDevice->MaximumNicId; + + for (i = 0; i < 32 ; i++) { + u.GetCounts->NicIdCounts[i] = 0; + } + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[i].Connections; + + while (Connection != NULL) { +#if defined(_PNP_POWER) + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicHandle.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicHandle.NicId]; + } +#else + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicId]; + } +#endif _PNP_POWER + Connection = Connection->NextConnection; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + NB_DEBUG (QUERY, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* NbiTdiAction */ + diff --git a/private/ntos/tdi/isn/nb/address.c b/private/ntos/tdi/isn/nb/address.c new file mode 100644 index 000000000..2eb882b80 --- /dev/null +++ b/private/ntos/tdi/isn/nb/address.c @@ -0,0 +1,2406 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == sizeof(TDI_ADDRESS_NETBIOS)) { + return ((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbiParseTdiAddress */ + + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbiPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbiValidateTdiAddress */ + + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the Netbios transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif +#if 0 + TA_NETBIOS_ADDRESS FakeAddress; +#endif + + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; +#if 0 + TdiBuildNetbiosAddress( + "ADAMBA67 ", + FALSE, + &FakeAddress); + name = (PTRANSPORT_ADDRESS)&FakeAddress; +#endif + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type Netbios. This call returns (PVOID)-1 if the + // address is the broadcast address. + // + + NetbiosAddress = NbiParseTdiAddress (name, TRUE); + + if (NetbiosAddress == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no Netbios Address\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // get an address file structure to represent this address. + // + + AddressFile = NbiCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + +#if defined(_PNP_POWER) + + Address = NbiFindAddress ( + Device, + ( NetbiosAddress == (PVOID)-1 ) ? (PVOID)-1 : NetbiosAddress->NetbiosName + ); + + if (Address == NULL) { + +#else + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Address = NbiLookupAddress (Device, NetbiosAddress); + + if (Address == NULL) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif _PNP_POWER + + // + // This address doesn't exist. Create it. + // This initializes the address with a ref + // of type ADDRESS_FILE, so if we fail here + // we need to remove that. + // + + Address = NbiCreateAddress ( + Device, + NetbiosAddress); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { +#else + if (Device->State == DEVICE_STATE_STOPPING) { +#endif _PNP_POWER + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Address = Address; + + NB_INSERT_TAIL_LIST( + &Address->AddressFileDatabase, + &AddressFile->Linkage, + &Address->Lock); + + if (NetbiosAddress == (PVOID)-1) { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } else { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + NbiStartRegistration (Address); + } + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + NbiDestroyAddressFile (AddressFile); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + +#if !defined(_PNP_POWER) + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif !_PNP_POWER + NB_DEBUG2 (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // Make sure the types do not conflict. + // + + if ((NetbiosAddress != (PVOID)-1) && + (NetbiosAddress->NetbiosNameType != Address->NetbiosAddress.NetbiosNameType)) { + + NB_DEBUG (ADDRESS, ("Address types conflict %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + NB_DEBUG (ADDRESS, ("Address access not allowed %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + NB_DEBUG (ADDRESS, ("Address share access wrong %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // Insert the address file on the address + // list; we will pend this open if the address + // is still registering. If the address has + // already failed as duplicate, then we + // fail the open. + // + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Address duplicated %lx\n", Address)); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + // + // Start registration unless it is registered or + // it is the broadcast address. + // + + if ((Address->State == ADDRESS_STATE_REGISTERING) && + (NetbiosAddress != (PVOID)-1)) { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + } else { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + } + + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + + NbiReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } + + } + } + } + + // + // Remove the reference from NbiLookupAddress. + // + + NbiDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* NbiOpenAddress */ + + +VOID +NbiStartRegistration( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process for a netbios name + by sending out the first add name packet and starting the timer + so that NbiRegistrationTimeout is called after the correct timeout. + +Arguments: + + Address - The address which is to be registered. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NB_DEBUG2 (ADDRESS, ("StartRegistration of %lx\n", Address)); + + // + // First send out an add name packet. + // + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + Address->RegistrationCount = 0; + + // + // Now start the timer. + // + + NbiReferenceAddress (Address, AREF_TIMER); + + CTEInitTimer (&Address->RegistrationTimer); + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + +} /* NbiStartRegistration */ + + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the address registration + timer expires. It sends another add name if needed, or + checks the result if the correct number have been sent. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the address pointer. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Context; + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PLIST_ENTRY p; + + ++Address->RegistrationCount; + + if ((Address->RegistrationCount < Address->Device->BroadcastCount) && + ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0)) { + + NB_DEBUG2 (ADDRESS, ("Send add name %d for %lx\n", Address->RegistrationCount+1, Address)); + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + + } else { + + // + // The correct number of frames have been sent, see what + // happened. + // + + NB_DEBUG2 (ADDRESS, ("Done with add names for %lx\n", Address)); + + ReferencedAddressFile = NULL; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + Address->State = ADDRESS_STATE_OPEN; + } else { + Address->State = ADDRESS_STATE_STOPPING; + } + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + CTEAssert (AddressFile->State == ADDRESSFILE_STATE_OPENING); + CTEAssert (AddressFile->OpenRequest != NULL); + + NbiReferenceAddressFileLock (AddressFile, AFREF_TIMEOUT); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + // + // Now see what to do with this address file. + // + + REQUEST_INFORMATION(AddressFile->OpenRequest) = 0; + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Open of address file %lx failed, duplicate\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_DUPLICATE_NAME; + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + NB_DEBUG2 (ADDRESS, ("Complete open of address file %lx\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_SUCCESS; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + } + + NbiCompleteRequest (AddressFile->OpenRequest); + NbiFreeRequest (Address->Device, AddressFile->OpenRequest); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + ReferencedAddressFile = AddressFile; + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + NbiDereferenceAddress (Address, AREF_TIMER); + + } + +} /* NbiRegistrationTimeout */ + + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_FIND_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Quick check for any names starting with this character. + // + + if (Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] == 0) { + return; + } + + // + // Always respond to broadcast requests. + // +#if defined(_PNP_POWER) + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + NbiDereferenceAddress (Address, AREF_FIND); + + } else if ( NbiFindAdapterAddress( NbConnectionless->NameFrame.Name, LOCK_NOT_ACQUIRED ) ) { + + NbiSendNameFrame( + NULL, + (UCHAR)(NB_NAME_UNIQUE | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + } +#else + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + NbiDereferenceAddress (Address, AREF_FIND); + + } +#endif _PNP_POWER +} /* NbiProcessFindName */ + + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_ADD_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + BOOLEAN LocalFrame; + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Ignore any frame that came from us, except for the purpose + // of updating the cache. + // + + if ((Device->Bind.QueryHandler)( + IPX_QUERY_IS_ADDRESS_LOCAL, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + NbConnectionless->IpxHeader.SourceNetwork, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + LocalFrame = TRUE; + + } else { + + LocalFrame = FALSE; + + } + + if (!LocalFrame) { + + if ((Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] != 0) && + (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name))) { + + if (NB_NODE_BROADCAST(NbConnectionless->IpxHeader.DestinationNode)) { + + // + // If this frame is an add name (identified because it is a + // broadcast frame) then respond if we have it registered + // unique, or we have it group and someone is trying to add + // it unique. + // + + if ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) || + ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) && + ((NbConnectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0))) { + + // + // According to GeorgeJ's doc, on a name in use we just + // echo back the name type flags from the request. + // + + NbiSendNameFrame( + Address, + NbConnectionless->NameFrame.NameTypeFlag, + NB_CMD_NAME_IN_USE, + RemoteAddress, +#if defined(_PNP_POWER) + NbConnectionless); +#else + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); +#endif _PNP_POWER + } + + } else if ((*(UNALIGNED ULONG *)NbConnectionless->IpxHeader.DestinationNetwork == + *(UNALIGNED ULONG *)Device->Bind.Network) && + NB_NODE_EQUAL(NbConnectionless->IpxHeader.DestinationNode, Device->Bind.Node)) { + + // + // If this is an add name response (which will be sent + // directly to us) then we need to mark the address + // as such. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + } + + + // + // Pass this frame over to the netbios cache management + // routines to check if they need to update their cache. + // + + CacheUpdateFromAddName (RemoteAddress, NbConnectionless, LocalFrame); + +} /* NbiProcessAddName */ + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetbiosAddress - The name to assign to this address, or -1 if it + is the broadcast address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + + Address = (PADDRESS)NbiAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + NB_DEBUG (ADDRESS, ("Create address %.16s failed\n", + (NetbiosAddress == (PVOID)-1) ? "" : NetbiosAddress->NetbiosName)); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address %lx (%.16s)\n", Address, + (NetbiosAddress == (PVOID)-1) ? "" : NetbiosAddress->NetbiosName)); + RtlZeroMemory (Address, sizeof(ADDRESS)); + + Address->Type = NB_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + Address->State = ADDRESS_STATE_REGISTERING; + Address->Flags = 0; + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock.Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + + if (NetbiosAddress == (PVOID)-1) { + Address->NetbiosAddress.Broadcast = TRUE; + } else { + Address->NetbiosAddress.Broadcast = FALSE; + Address->NetbiosAddress.NetbiosNameType = NetbiosAddress->NetbiosNameType; + RtlCopyMemory (Address->NetbiosAddress.NetbiosName, NetbiosAddress->NetbiosName, 16); + ++Device->AddressCounts[NetbiosAddress->NetbiosName[0]]; + } + + if (Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) { + Address->NameTypeFlag = NB_NAME_UNIQUE; + } else { + Address->NameTypeFlag = NB_NAME_GROUP; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&Device->AddressDatabase, &Address->Linkage); + ++Device->AddressCount; + + NbiReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* NbiCreateAddress */ + + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + + ConflictIsOk - TRUE if we should succeed the verify even if the + corresponding address is in CONFLICT. ( For Close and + cleanup we return STATUS_SUCCESS even if we are in conflict + so that the addressfile can be destroyed) + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + BOOLEAN LockHeld = FALSE; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == NB_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == NB_ADDRESS_SIGNATURE) ) { + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + LockHeld = TRUE; + +#if defined(_PNP_POWER) + if (Address->State != ADDRESS_STATE_STOPPING && + ( ConflictIsOk || ( !(Address->Flags & ADDRESS_FLAGS_CONFLICT) )) ) { +#else + if (Address->State != ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + + NbiReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbiPrint1("NbiVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyAddressFile: AF %lx exception\n", Address); + if (LockHeld) { + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyAddressFile */ + + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by NbiDerefAddress when + the reference count goes to 0. + + This thread is only queued by NbiDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + NB_DEBUG2 (ADDRESS, ("Destroy address %lx <%.16s>\n", Address, + Address->NetbiosAddress.Broadcast ? "" : Address->NetbiosAddress.NetbiosName)); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (!Address->NetbiosAddress.Broadcast) { + --Device->AddressCounts[Address->NetbiosAddress.NetbiosName[0]]; + } + --Device->AddressCount; + RemoveEntryList (&Address->Linkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + NbiDereferenceDevice (Device, DREF_ADDRESS); + +} /* NbiDestroyAddress */ + + +#if DBG +VOID +NbiRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); +} /* NbiRefAddress */ + + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); + +} /* NbiRefAddressLock */ +#endif + + +VOID +NbiDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &Address->ReferenceCount ); + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbiDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + NbiDestroyAddress(Address); +#endif + + } + +} /* NbiDerefAddress */ + + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + UINT i; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)NbiAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + NB_DEBUG (ADDRESS, ("Create address file failed\n")); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = NB_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + for (i = 0; i < 6; i++) { + AddressFile->RegisteredHandler[i] = FALSE; + AddressFile->HandlerContexts[i] = NULL; + AddressFile->Handlers[i] = TdiDefaultHandlers[i]; + } + + CTEAssert (AddressFile->ConnectionHandler == TdiDefaultConnectHandler); + CTEAssert (AddressFile->DisconnectHandler == TdiDefaultDisconnectHandler); + CTEAssert (AddressFile->ErrorHandler == TdiDefaultErrorHandler); + CTEAssert (AddressFile->ReceiveHandler == TdiDefaultReceiveHandler); + CTEAssert (AddressFile->ReceiveDatagramHandler == TdiDefaultRcvDatagramHandler); + CTEAssert (AddressFile->ExpeditedDataHandler == TdiDefaultRcvExpeditedHandler); + + return AddressFile; + +} /* NbiCreateAddressFile */ + + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbiDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + BOOLEAN StopAddress; + + NB_DEBUG2 (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle1); + Address->State = ADDRESS_STATE_STOPPING; + NB_FREE_LOCK (&Device->Lock, LockHandle1); + + StopAddress = TRUE; + + } else { + + StopAddress = FALSE; + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + if (StopAddress && (!Address->NetbiosAddress.Broadcast)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | + NB_NAME_USED | NB_NAME_REGISTERED | NB_NAME_DEREGISTERED), + NB_CMD_DELETE_NAME, + NULL, + NULL); + } + + // + // Now dereference the owning address. + // + + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + NbiFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + NbiCompleteRequest (CloseRequest); + NbiFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* NbiDestroyAddressFile */ + + +#if DBG +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); +} /* NbiRefAddressFile */ + + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); + +} /* NbiRefAddressFileLock */ + +#endif + + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &AddressFile->ReferenceCount ); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + NbiDestroyAddressFile (AddressFile); + } + +} /* NbiDerefAddressFile */ + +#if !defined(_PNP_POWER) + +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosAddress - The name to look up, or -1 if the broadcast + address is being searched for. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->State == ADDRESS_STATE_STOPPING) { + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosAddress != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if (NetbiosAddress == (PVOID)-1) { + continue; + } + + if (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosAddress->NetbiosName, + 16)) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* NbiLookupAddress */ +#endif !_PNP_POWER + + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NetbiosName + values. If a match is found, the address is referenced and the + pointer is returned. + + We ignore any addresses which are either STOPPING or are under + CONFLICT state. + + A name in CONFLICT is dead for all practical purposes + except Close. This routine is called by various name service, + datagram and session sevice routines. We hide any names in CONFLICT + from these routines. + + This routine is also called by NbiTdiOpenAddress(). + A name could have been marked in CONFLICT ages ago(but is not closed + yet). We must allow another open of the same name as that might + succeed now. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosName - The name to look up, or -1 for the broadcast name. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + +#if defined(_PNP_POWER) + if ( ( Address->State == ADDRESS_STATE_STOPPING ) || + ( Address->Flags & ADDRESS_FLAGS_CONFLICT ) ) { +#else + if (Address->State == ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosName != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if ((NetbiosName == (PVOID)-1) || + (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosName, + 16))) { + continue; + } + } + + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_FIND); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + +} /* NbiFindAddress */ + + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PLIST_ENTRY p; + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle1, LockHandle2; + LIST_ENTRY SendDatagramList; + PNB_SEND_RESERVED Reserved; + PREQUEST DatagramRequest; + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + LIST_ENTRY DatagramQ; + + + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + NB_FREE_LOCK (&Address->Lock, LockHandle1); + return STATUS_SUCCESS; + } + + + // + // This prevents anybody else from being put on the + // ConnectionDatabase. + // + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + CTEAssert (Connection->AddressFile == AddressFile); + Connection->AddressFileLinked = FALSE; + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->ReferenceCount == 0) { + + // + // The refcount is already 0, so we can just + // NULL out this field to complete the disassociate. + // + + Connection->AddressFile = NULL; + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + // + // Mark this so we know to disassociate when the + // count goes to 0, but that there is no specific + // request pending on it. We also stop the connection + // to shut it down. + // + + Connection->DisassociatePending = (PVOID)-1; + NbiReferenceConnectionLock (Connection, CREF_DISASSOC); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle3); + + // + // This call frees the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle3)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_DISASSOC); + + } + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Abort all pending send datagrams. + // + // BUGBUG: Also make them cancellable. + // + + InitializeListHead (&SendDatagramList); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + if (Reserved->u.SR_DG.AddressFile == AddressFile) { + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&SendDatagramList, &Reserved->WaitLinkage); + + } + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + for (p = SendDatagramList.Flink; p != &SendDatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Aborting datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + + // + // Abort all pending receive datagrams. + // + + InitializeListHead( &DatagramQ ); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + while (!IsListEmpty(&AddressFile->ReceiveDatagramQueue)) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + // Insert it on a private Q, so it can be completed later. + InsertTailList( &DatagramQ, p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + for( p = DatagramQ.Flink; p != &DatagramQ; ) { + Request = LIST_ENTRY_TO_REQUEST ( p ); + + p = p->Flink; + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + } + + + return STATUS_SUCCESS; + +} /* NbiStopAddressFile */ + + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + NbiStopAddressFile (AddressFile, Address); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* NbiCloseAddressFile */ + +#if defined(_PNP_POWER) + + +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ) + +/*++ + +Routine Description: + + This routine creates an adapter address sttuctures which stores + the netbios name of an adapter. the netbios name has 12 0's + followed by the mac address of the adapter. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AdapterMacAddress - pointer to the adapter mac address given to us + by IPX. + +Return Value: + + The newly created address, or NULL if none can be allocated. + THIS ROUTINE MUST BE CALLED WITH THE DEVICE LOCK HELD. + +--*/ + +{ + PADAPTER_ADDRESS AdapterAddress; + CTELockHandle LockHandle; + PDEVICE Device = NbiDevice; + + AdapterAddress = (PADAPTER_ADDRESS)NbiAllocateMemory (sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "Adapter Address"); + if (AdapterAddress == NULL) { + NB_DEBUG (ADDRESS, ("Create Adapter Address %<2.2x><2.2x><2.2x><2.2x><2.2x><2.2x> failed\n", + AdapterMacAddress[0], + AdapterMacAddress[1], + AdapterMacAddress[2], + AdapterMacAddress[3], + AdapterMacAddress[4], + AdapterMacAddress[5] + )); + return NULL; + } + + AdapterAddress->Type = NB_ADAPTER_ADDRESS_SIGNATURE; + AdapterAddress->Size = sizeof (ADDRESS); + + RtlZeroMemory(AdapterAddress->NetbiosName, 10); + RtlCopyMemory(&AdapterAddress->NetbiosName[10], AdapterMacAddress, 6); + + + InsertTailList (&Device->AdapterAddressDatabase, &AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + return AdapterAddress; + +} /* NbiCreateAdapterAddress */ + + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine destroys the adapter address structure and removes it + from the list. + +Arguments: + + AdapterAddress - Pointer to an adapter address structure to be destroyed + NULL if AdapterMacAddress is given. + + AdapterMacAddress - Mac Address of the adapter which just got deleted. so find + the corresponding adapter address structure and remove it. + NULL if AdapterAddress is supplied. + +Return Value: + + STATUS_SUCCESS or STATUS_UNSUCCESSFUL if address not found. + + THIS ROUTINE ASSUMES THE THE DEVICE IS LOCK IS HELD BY THE CALLER + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + UCHAR NetbiosName[NB_NETBIOS_NAME_SIZE]; + + + // + + CTEAssert( AdapterAddress || AdapterMacAddress ); + if ( !AdapterAddress ) { + RtlZeroMemory( NetbiosName, 10); + RtlCopyMemory( &NetbiosName[10], AdapterMacAddress, 6 ); + + AdapterAddress = NbiFindAdapterAddress( NetbiosName, LOCK_ACQUIRED ); + + if ( !AdapterAddress ) { + return STATUS_UNSUCCESSFUL; + } + } + + NB_DEBUG2 (ADDRESS, ("Destroy Adapter address %lx <%.16s>\n", AdapterAddress,AdapterAddress->NetbiosName)); + RemoveEntryList (&AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + NbiFreeMemory (AdapterAddress, sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "AdapterAddress"); + + return STATUS_SUCCESS; +} /* NbiDestroyAdapterAddress */ + + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ) + +/*++ + +Routine Description: + + This routine finds an adapter address ( netbios name ) for the given + AdapterMacAddress and returns a pointer to it. Note that no reference + is done on this address, so if this routine is called without the device + lock, the caller must not use this pointer directly. + +Arguments: + + NetbiosName - NetbiosName to be found. + + LockHeld - is device lock already held or not. + +Return Value: + + Pointer to the adapter address if found, NULL otherwise. + +--*/ + +{ + + PLIST_ENTRY p; + CTELockHandle LockHandle; + PADAPTER_ADDRESS AdapterAddress; + PDEVICE Device = NbiDevice; + + + if ( !LockHeld ) { + NB_GET_LOCK( &Device->Lock, &LockHandle ); + } + for ( p = Device->AdapterAddressDatabase.Flink; + p != &Device->AdapterAddressDatabase; + p = p->Flink ) { + + AdapterAddress = CONTAINING_RECORD( p, ADAPTER_ADDRESS, Linkage ); + if ( RtlEqualMemory( + NetbiosName, + AdapterAddress->NetbiosName, + NB_NETBIOS_NAME_SIZE ) ) { + break; + } + } + + + if ( !LockHeld ) { + NB_FREE_LOCK( &Device->Lock, LockHandle ); + } + + if ( p == &Device->AdapterAddressDatabase ) { + return NULL; + } else { + return AdapterAddress; + } + +} /* NbiFindAdapterAddress */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isn/nb/autodial.c b/private/ntos/tdi/isn/nb/autodial.c new file mode 100644 index 000000000..ec56e2351 --- /dev/null +++ b/private/ntos/tdi/isn/nb/autodial.c @@ -0,0 +1,526 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + NT specific routines for interfacing with the + RAS AutoDial driver (rasacd.sys). + +Author: + + Anthony Discolo (adiscolo) Aug 30, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + adiscolo 08-30-95 created + +Notes: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include +#include + +// +// Global variables +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbi '; + + + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PDEVICE pDevice = pArgs[0]; + PCONNECTION pConnection = pArgs[1]; + PREQUEST pRequest = pArgs[2]; + CTELockHandle ConnectionLH, DeviceLH; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // +#if notdef // DBG + DbgPrint("NbiRetryTdiConnect: fSuccess=%d, pConnection=0x%x\n", fSuccess, pConnection); +#endif + + status = NbiVerifyConnection(pConnection); + if (!NT_SUCCESS(status)) { + DbgPrint( + "NbiRetryTdiConnect: NbiVerifyConnection failed on connection 0x%x (status=0x%x)\n", + pConnection, + status); + return; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&pConnection->Lock, &ConnectionLH); + NB_GET_LOCK (&pDevice->Lock, &DeviceLH); + +#if notdef // DBG + DbgPrint( + "NbiRetryTdiConnect: AddressFile=0x%x, DisassociatePending=0x%x, ClosePending=0x%x\n", + pConnection->AddressFile, + pConnection->DisassociatePending, + pConnection->ClosePending); +#endif + + if ((pConnection->AddressFile != NULL) && + (pConnection->AddressFile != (PVOID)-1) && + (pConnection->DisassociatePending == NULL) && + (pConnection->ClosePending == NULL)) + { + NbiReferenceConnectionLock(pConnection, CREF_CONNECT); + // + // Clear the AUTOCONNECTING flag since we + // done with the automatic connection attempt. + // Set the AUTOCONNECTED flag to prevent us + // from attempting an automatic connection + // for this connection again. + // + pConnection->Flags &= ~CONNECTION_FLAGS_AUTOCONNECTING; + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTED; + + pConnection->State = CONNECTION_STATE_CONNECTING; + pConnection->Retries = pDevice->ConnectionCount; + status = NbiTdiConnectFindName( + pDevice, + pRequest, + pConnection, + CancelLH, + ConnectionLH, + DeviceLH, + &bLockFreed); + } + else { + DbgPrint("NbiRetryTdiConnect: Connect on invalid connection 0x%x\n", pConnection); + + pConnection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&pDevice->Lock, DeviceLH); + status = STATUS_INVALID_CONNECTION; + } + if (!bLockFreed) { + NB_FREE_LOCK (&pConnection->Lock, ConnectionLH); + NB_FREE_CANCEL_LOCK(CancelLH); + } + // + // Complete the irp if necessary. + // + if (status != STATUS_PENDING) { + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = status; + + NbiCompleteRequest(pRequest); + NbiFreeRequest(pDevice, pRequest); + } + NbiDereferenceConnection(pConnection, CREF_VERIFY); +} /* NbiRetryTdiConnect */ + + + +BOOLEAN +NbiCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ +#if notdef // DBG + DbgPrint("NbiCancelAutodialRequest: pArg=0x%x\n", pArg); +#endif + if (nArgs != 2) + return FALSE; + + return (pArgs[1] == pArg); +} // NbiCancelAutoDialRequest + + + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDevice: a pointer to the device object for this driver + + pRequest: a pointer to the irp to be cancelled + + pConnection: a pointer to the connnection to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint( + "NbiCancelTdiConnect: pIrp=0x%x, RemoteName=%-15.15s, pConnection=0x%x\n", + pRequest, + addr.cNetbios, + pConnection); +#endif + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbiCancelAutoDialRequest, + pConnection); +} // NbiCancelTdiConnect + + + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pDevice - a pointer to the DEVICE structure for this connection + + pConnection - a pointer to the CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pRequest - a pointer to the request irp + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[3]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic connection + // on this connection, don't try it again. + // + if (pConnection->Flags & CONNECTION_FLAGS_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint("NbiAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbiRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pDevice; + pArgs[1] = pConnection; + pArgs[2] = pRequest; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 3, + pArgs); + if (bSuccess) { + // + // Set the AUTOCONNECTING flag so we know + // to also cancel the connection in the + // automatic connection driver if this + // request gets canceled. + // + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTING; + } +} // NbiAttemptAutoDial + + + +VOID +NbiNoteNewConnection( + IN PCONNECTION pConnection + ) +{ + NTSTATUS status; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG i; + TDI_ADDRESS_IPX tdiIpxAddress; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); + // + // Determine the mac address of the adapter + // over which the connection has been made. + // + status = (pConnection->Device->Bind.QueryHandler)( + IPX_QUERY_IPX_ADDRESS, +#if defined(_PNP_POWER) + &pConnection->LocalTarget.NicHandle, +#else + pConnection->LocalTarget.NicId, +#endif _PNP_POWER + &tdiIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + if (status != STATUS_SUCCESS) { +#if notdef // DBG + DbgPrint("NbiNoteNewConnection: QueryHandler(IPX_QUERY_IPX_ADDRESS) failed (status=0x%x)\n", status); + return; +#endif + } + // + // Copy the source mac address to identify + // the adapter. + // + adapter.fType = ACD_ADAPTER_MAC; + for (i = 0; i < 6; i++) + adapter.cMac[i] = tdiIpxAddress.NodeAddress[i]; +#if notdef // DBG + DbgPrint( + "NbiNoteNewConnection: address=%-15.15s, remote mac=%02x:%02x:%02x:%02x:%02x:%02x\n", + addr.cNetbios, + adapter.cMac[0], + adapter.cMac[1], + adapter.cMac[2], + adapter.cMac[3], + adapter.cMac[4], + adapter.cMac[5]); +#endif + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbiNoteNewConnection + + + +VOID +NbiAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdBind + + + +VOID +NbiAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/tdi/isn/nb/bind.c b/private/ntos/tdi/isn/nb/bind.c new file mode 100644 index 000000000..58509f692 --- /dev/null +++ b/private/ntos/tdi/isn/nb/bind.c @@ -0,0 +1,594 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiBind) +#endif + +#if defined(_PNP_POWER) +// +// local functions. +// +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine binds the Netbios module of ISN to the IPX + module, which provides the NDIS binding services. + +Arguments: + + Device - Pointer to the Netbios device. + + Config - Pointer to the configuration information. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; +/* union { + IPX_INTERNAL_BIND_INPUT Input; + IPX_INTERNAL_BIND_OUTPUT Output; + } Bind; +*/ + InitializeObjectAttributes( + &ObjectAttributes, + &Config->BindName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwCreateFile( + &Device->BindHandle, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(Status)) { + + NB_DEBUG (BIND, ("Could not open IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + return Status; + } + + // + // Fill in our bind data. + // + +#if defined(_PNP_POWER) + Device->BindInput.Version = ISN_VERSION; +#else + Device->BindInput.Version = 1; +#endif _PNP_POWER + Device->BindInput.Identifier = IDENTIFIER_NB; + Device->BindInput.BroadcastEnable = TRUE; + Device->BindInput.LookaheadRequired = 192; + Device->BindInput.ProtocolOptions = 0; + Device->BindInput.ReceiveHandler = NbiReceive; + Device->BindInput.ReceiveCompleteHandler = NbiReceiveComplete; + Device->BindInput.StatusHandler = NbiStatus; + Device->BindInput.SendCompleteHandler = NbiSendComplete; + Device->BindInput.TransferDataCompleteHandler = NbiTransferDataComplete; + Device->BindInput.FindRouteCompleteHandler = NbiFindRouteComplete; + Device->BindInput.LineUpHandler = NbiLineUp; + Device->BindInput.LineDownHandler = NbiLineDown; + Device->BindInput.ScheduleRouteHandler = NULL; +#if defined(_PNP_POWER) + Device->BindInput.PnPHandler = NbiPnPNotification; +#endif _PNP_POWER + + + Status = ZwDeviceIoControlFile( + Device->BindHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + &Device->BindInput, // Input Buffer + sizeof(Device->BindInput), // Input Buffer Length + &Device->Bind, // OutputBuffer + sizeof(Device->Bind)); // OutputBufferLength + + // + // We open synchronous, so this shouldn't happen. + // + + CTEAssert (Status != STATUS_PENDING); + + // + // Save the bind data. + // + + if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (BIND, ("Successfully bound to IPX (%ws)\n", + Config->BindName.Buffer)); +// RtlCopyMemory (&Device->Bind, &Bind.Output, sizeof(IPX_INTERNAL_BIND_OUTPUT)); + +#if !defined(_PNP_POWER) + RtlZeroMemory (Device->ReservedNetbiosName, 16); + RtlCopyMemory (&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAXIMUM_NIC_ID, + (USHORT)0, + &Device->MaximumNicId, + sizeof(Device->MaximumNicId), + NULL); + CTEAssert (Status == STATUS_SUCCESS); +#endif !_PNP_POWER + + } else { + + NB_DEBUG (BIND, ("Could not bind to IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_BINDING_FAILED, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + ZwClose(Device->BindHandle); + } + + return Status; + +} /* NbiBind */ + + +VOID +NbiUnbind( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This function closes the binding between the Netbios over + IPX module and the IPX module previously established by + NbiBind. + +Arguments: + + Device - The netbios device object. + +Return Value: + + None. + +--*/ + +{ + ZwClose (Device->BindHandle); + +} /* NbiUnbind */ + + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +/*++ + +Routine Description: + + This function receives a status indication from IPX, + corresponding to a status indication from an underlying + NDIS driver. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + GeneralStatus - The general status code. + + StatusBuffer - The status buffer. + + StatusBufferLength - The length of the status buffer. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiStatus */ + + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + + +/*++ + +Routine Description: + + This function receives line up indications from IPX, + indicating that the specified adapter is now up with + the characteristics shown. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + LineInfo - Information about the adapter's medium. + + DeviceType - The type of the adapter. + + ConfigurationData - IPX-specific configuration data. + +Return Value: + + None. + +--*/ + +{ + PIPXCP_CONFIGURATION Configuration = (PIPXCP_CONFIGURATION)ConfigurationData; + + // + // Update queries have NULL as the ConfigurationData. These + // only indicate changes in LineInfo. BUGBUG Ignore these + // for the moment. + // + + if (Configuration == NULL) { + return; + } + +#if !defined(_PNP_POWER) + // + // Since Netbios outgoing queries only go out on network 1, + // we ignore this (BUGBUG for the moment) unless that is + // the NIC it is on. + // + + if (NicId == 1) { + + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNetwork, Configuration->Network, 4); + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNode, Configuration->LocalNode, 6); + + } +#endif !_PNP_POWER +} /* NbiLineUp */ + + +VOID +NbiLineDown( + IN USHORT NicId, + IN ULONG FwdAdapterContext + ) + + +/*++ + +Routine Description: + + This function receives line down indications from IPX, + indicating that the specified adapter is no longer + up. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiLineDown */ + +#if defined(_PNP_POWER) + +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX. + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + UCHAR PrevReservedName[NB_NETBIOS_NAME_SIZE]; + UNICODE_STRING UnicodeDeviceName; + + + NB_DEBUG2( DEVICE, ("Received a pnp notification, opcode %d\n",OpCode )); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + BOOLEAN ReallocReceiveBuffers = FALSE; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + if ( PnPInfo->NewReservedAddress ) { + + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + +// RtlZeroMemory(Device->ReservedNetbiosName, NB_NETBIOS_NAME_SIZE); +// RtlCopyMemory(&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + } + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->State != DEVICE_STATE_OPEN ); + + + // + // we must do this while we still have the device lock. + // + if ( !Device->LongTimerRunning ) { + Device->LongTimerRunning = TRUE; + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } + + Device->State = DEVICE_STATE_OPEN; + + CTEAssert( !Device->MaximumNicId ); + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + Device->Bind.LineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumSendSize; + ReallocReceiveBuffers = TRUE; + } else { + if ( PnPInfo->LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + ReallocReceiveBuffers = TRUE; + } + // + // MaxSendSize could become smaller. + // + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + } + + Device->MaximumNicId++; + + + // + // + NbiCreateAdapterAddress( PnPInfo->NodeAddress ); + + // + // And finally remove all the failed cache entries since we might + // find those routes using this new adapter + // + FlushFailedNetbiosCacheEntries(Device->NameCache); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + if ( ReallocReceiveBuffers ) { + PWORK_QUEUE_ITEM WorkItem; + + WorkItem = NbiAllocateMemory( sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buffer work item"); + + if ( WorkItem ) { + ExInitializeWorkItem( WorkItem, NbiReAllocateReceiveBufferPool, (PVOID) WorkItem ); + ExQueueWorkItem( WorkItem, DelayedWorkQueue ); + } else { + NB_DEBUG( DEVICE, ("Cannt schdule work item to realloc receive buffer pool\n")); + } + } + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->DeviceName; + UnicodeDeviceName.MaximumLength = Device->DeviceNameLength; + UnicodeDeviceName.Length = Device->DeviceNameLength - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to register nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + CTEAssert( Device->MaximumNicId ); + Device->MaximumNicId--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->State = DEVICE_STATE_LOADED; + Device->MaximumNicId = 0; + + } + + + // + // MaximumSendSize could change if the card with the smallest send size just + // got removed. MaximumPacketSize could only become smaller and we ignore that + // since we dont need to(want to) realloc ReceiveBuffers. + // + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + RemoveInvalidRoutesFromNetbiosCacheTable( Device->NameCache, &PnPInfo->NicHandle ); + + NbiDestroyAdapterAddress( NULL, PnPInfo->NodeAddress ); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + +/* // + // Now mark the previous reserved name in conflict if it has + // been registered by any of our client + // + if ( Address = NbiFindAddress( Device, PrevReservedName ) ) { + NB_GET_LOCK( &Address->Lock, &LockHandle ); + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + NB_FREE_LOCK( &Address->Lock, LockHandle ); + + NB_DEBUG( ADDRESS, ("Reserved Address %lx<%.16s> is marked CONFLICT\n",Address,Address->NetbiosAddress.NetbiosName)); + // + // nbifindaddress added a reference, so deref + // + NbiDereferenceAddress( Address, AREF_FIND ); + } +*/ + + // + // inform tdi clients about the device deletion + // + if ( PnPInfo->FirstORLastDevice ) { + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to Deregister nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + PADDRESS Address; + BOOLEAN ReservedNameClosing = FALSE; + + CTEAssert( PnPInfo->NewReservedAddress ); + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isn/nb/cache.c b/private/ntos/tdi/isn/nb/cache.c new file mode 100644 index 000000000..cbd27ad67 --- /dev/null +++ b/private/ntos/tdi/isn/nb/cache.c @@ -0,0 +1,2746 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module contains the name cache routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 20-December-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include +#include + +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ); + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); +#endif // RASAUTODIAL + +// +// BUGBUG: We should change to monitor add name packets better, +// so if we get an add for a different place we attempt to determine +// if it is real or bogus and update if possible. +// + + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +) + +/*++ + +Routine Description: + + This routine looks up a particular remote name in the + Netbios name cache. If it cannot find it, a find name + request is queued up. + + THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The netbios device. + + Type - Defines the type. The effect this has is: + FindNameConnect - On connects we will ignore an existing + cache entry if it got no response before. + FindNameNetbiosFindName - For these we ignore an existing + cache entry if it is for a group name -- this is + because the find name wants the address of every + machine, not just the network list. + FindNameOther - Normal handling is done. + + RemoteName - The name to be discovered -- will be NULL if it + is the broadcast address. + + CacheName - Returns the cache entry that was discovered. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE FoundCacheName; + PNB_SEND_RESERVED Reserved; + PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName + + // + // First scan the netbios name cache to see if we know + // about this remote. + // + + if (RemoteName) { + RealRemoteName = RemoteName; + } else { + RealRemoteName = NetbiosBroadcastName; + } + + if ( FindInNetbiosCacheTable ( Device->NameCache, + RealRemoteName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + // + // If this is a netbios find name, we only can use unique + // names in the cache; for the group ones we need to requery + // because the cache only lists networks, not individual machines. + // For connect requests, if we find an empty cache entry we + // remove it and requery. + // + + if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) { + + if (FoundCacheName->NetworksUsed > 0) { + + *CacheName = FoundCacheName; + NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_SUCCESS; + + } else { + + if (Type != FindNameConnect) { + + if (FoundCacheName->FailedOnDownWan) { + NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_DEVICE_DOES_NOT_EXIST; + } else { + NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + // + // This is a connect and the current cache entry + // has zero names; delete it. + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + CTEAssert (FoundCacheName->ReferenceCount == 1); + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + } + + } + } + } + } + + + // + // There was no suitable cache entry for this network, first see + // if there is one pending. + // + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // For this purpose we ignore a packet if a route + // has been found and it was for a unique name. This + // is because the cache information has already been + // inserted for this name. Otherwise if the name has + // since been deleted from the cache, the request + // that is looking for this name will starve because + // FindNameTimeout will just destroy the packet. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + continue; + } + + if (RtlEqualMemory( + Reserved->u.SR_FN.NetbiosName, + RealRemoteName, 16)) { + + NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "")); + + // + // There is already one pending. If it is for a group + // name and this is a netbios find name, we make sure + // the retry count is such that at least one more + // query will be sent, so the netbios find name + // buffer can be filled with the responses from this. + // + + if ((Type == FindNameNetbiosFindName) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) && + (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) { + + --Reserved->u.SR_FN.RetryCount; + } + + return STATUS_PENDING; + } + } + + s = NbiPopSendPacket(Device, TRUE); + + if (s == NULL) { + NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = FALSE; + Reserved->Type = SEND_TYPE_FIND_NAME; + RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16); + Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE + Reserved->u.SR_FN.RetryCount = 0; + Reserved->u.SR_FN.NewCache = NULL; + Reserved->u.SR_FN.SendTime = Device->FindNameTime; +#if !defined(_PNP_POWER) + Reserved->u.SR_FN.CurrentNicId = 1; + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAX_TYPE_20_NIC_ID, + (USHORT)0, + &Reserved->u.SR_FN.MaximumNicId, + sizeof(USHORT), + NULL); + + if (Reserved->u.SR_FN.MaximumNicId == 0) { + Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one + } +#endif !_PNP_POWER + NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n", + Reserved, RemoteName ? RemoteName : "")); + + + InsertHeadList( + &Device->WaitingFindNames, + &Reserved->WaitLinkage); + + ++Device->FindNamePacketCount; + + if (!Device->FindNameTimerActive) { + + Device->FindNameTimerActive = TRUE; + NbiReferenceDevice (Device, DREF_FN_TIMER); + + CTEStartTimer( + &Device->FindNameTimer, + 1, // 1 ms, i.e. expire immediately + FindNameTimeout, + (PVOID)Device); + } + + NbiReferenceDevice (Device, DREF_FIND_NAME); + + return STATUS_PENDING; + +} /* CacheFindName */ + + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the find name timer expires. + It is called every FIND_NAME_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, q; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + PNETBIOS_CACHE FoundCacheName; + NDIS_STATUS NdisStatus; +#if !defined(_PNP_POWER) + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif !_PNP_POWER + NB_DEFINE_LOCK_HANDLE (LockHandle) + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->FindNameTime; + + if (Device->FindNamePacketCount == 0) { + + NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n")); + + Device->FindNameTimerActive = FALSE; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + NbiDereferenceDevice (Device, DREF_FN_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // +#if defined(_PNP_POWER) + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // + + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If this is the first retry, we need to initialize the packet + // + if ( Reserved->u.SR_FN.RetryCount == 1 ) { + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + + } + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + + break; + + } +#else + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + // + // Save this now, then change it if needed. + // + + BroadcastTarget.NicId = Reserved->u.SR_FN.CurrentNicId; + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + } + + + // + // Increment the current NIC ID for next time. + // + + if (Reserved->u.SR_FN.CurrentNicId >= Reserved->u.SR_FN.MaximumNicId) { + Reserved->u.SR_FN.CurrentNicId = 1; + } else { + ++Reserved->u.SR_FN.CurrentNicId; + } + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // If we are going to wrap around the maximum NIC ID + // after sending this packet we wait the full configured + // amount, otherwise we wait almost the minimum (but still + // insert ourselves at the back of the queue to be fair). + // + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + } else { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + 2); + } + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx to %d\n", Reserved, BroadcastTarget.NicId)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + if (NdisStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + + // + // This send was done on a down wan line. To avoid + // extensive delays sending find names on systems + // with a lot of wan lines, we loop around immediately + // and do the next send. We put this packet back on + // the head of the list and change its SendTime so + // it will be resent immediately. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + NB_DEBUG2 (CACHE, ("FindNameTimeout resending %lx, wan send failed\n", Reserved)); + + RemoveEntryList (&Reserved->WaitLinkage); + InsertHeadList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + Reserved->u.SR_FN.SendTime = Device->FindNameTime; + + continue; + + } else { + + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } + + + break; + + } +#endif _PNP_POWER + + // + // Since we did something this time, we restart the timer. + // + + CTEStartTimer( + &Device->FindNameTimer, + FIND_NAME_GRANULARITY, + FindNameTimeout, + (PVOID)Device); + +} /* FindNameTimeout */ + + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams and connects + that were waiting for a route to be discovered to a + given Netbios NAME. THIS ROUTINE IS CALLED WITH + DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + RemoteName - The netbios name that was being searched for. + + Result - Indicates if the name was found, or not found due + to no response or wan lines being down. + + CacheName - If Result is NetbiosNameFound, the cache entry for this name. + This entry has been referenced and this routine will deref it. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY ConnectList; + LIST_ENTRY AdapterStatusList; + LIST_ENTRY NetbiosFindNameList; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + PLIST_ENTRY p; + PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + InitializeListHead (&DatagramList); + InitializeListHead (&ConnectList); + InitializeListHead (&AdapterStatusList); + InitializeListHead (&NetbiosFindNameList); + + // + // Put all connect requests on ConnectList. They will + // be continued or failed later. + // + + p = Device->WaitingConnects.Flink; + + while (p != &Device->WaitingConnects) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + p = p->Flink; + + if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) { + + RemoveEntryList (REQUEST_LINKAGE(ConnectRequest)); + InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest)); + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + } + + } + + + // + // Put all the datagrams on Datagram list. They will be + // sent or failed later. + // + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + // + // Check differently based on whether we were looking for + // the broadcast address or not. + // + + if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) { + if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) { + continue; + } + } else { + + if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) { + continue; + } + } + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&DatagramList, &Reserved->WaitLinkage); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the adapter status requests on AdapterStatus + // list. They will be sent or failed later. + // + + p = Device->WaitingAdapterStatus.Flink; + + while (p != &Device->WaitingAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(AdapterStatusRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the netbios find name requests on NetbiosFindName + // list. They will be completed later. + // + + p = Device->WaitingNetbiosFindName.Flink; + + while (p != &Device->WaitingNetbiosFindName) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest)); + InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest)); + + } + + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Now that the lock is free, process all the packets on + // the various lists. + // + + for (p = ConnectList.Flink; p != &ConnectList; ) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection)); + + // + // Continue with the connection sequence. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!ConnectRequest->Cancel) { + + IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 ); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection)); + + goto AbortConnect; + + // + // Jumps down into the else below. + // + + } + + } else { + BOOLEAN bAutodialAttempt = FALSE; + + NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection)); +AbortConnect: + + ASSERT (Connection->ConnectRequest == ConnectRequest); + +#ifdef RASAUTODIAL + if (fAcdLoadedG) { + CTELockHandle adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + CTEGetLock(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbiAttemptAutoDial( + Device, + Connection, + 0, + NbiRetryTdiConnect, + ConnectRequest)) + { + NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + bAutodialAttempt = TRUE; + } + } +#endif // RASAUTODIAL + + if (!bAutodialAttempt) { + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH; + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + } + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + } else { + + // BUGBUG What happens to the IRP? Who completes it? + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + + } + + + for (p = DatagramList.Flink; p != &DatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile)); + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + + // + // CacheName was referenced above. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest)); + } + + NbiTransmitDatagram (Reserved); + + } else { + + // + // BETABUGBUG: Should we send it once as a broadcast + // on net 0, just in case?? + // + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // If the failure was due to a down wan line indicate + // that, otherwise return success (so the browser won't + // confuse this with a down wan line). + // + + if (Result == NetbiosNameNotFoundWanDown) { + REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST; + REQUEST_INFORMATION(DatagramRequest) = 0; + } else { + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + } + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + } + + } + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest)); + + // + // Continue with the AdapterStatus sequence. We put + // it in ActiveAdapterStatus, it will either get + // completed when a response is received or timed + // out by the long timeout. + // + + REQUEST_STATUS(AdapterStatusRequest) = (NTSTATUS)CacheName; + + // + // CacheName was referenced above. + // + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + + NB_INSERT_TAIL_LIST( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (AdapterStatusRequest), + &Device->Lock); + + NbiSendStatusQuery (AdapterStatusRequest); + + } else { + + NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest)); + + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + } + + + for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // + // In fact there is not much difference between success or + // failure, since in the successful case the information + // will already have been written to the buffer. Just + // complete the request with the appropriate status, + // which will already be stored in the request. + // + + if (Result == NetbiosNameFound) { + + if (CacheName->Unique) { + + NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } else { + + NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + } else { + + CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT); + NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + // + // This sets REQUEST_INFORMATION(Request) to the correct value. + // + + NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest); + + NbiCompleteRequest(NetbiosFindNameRequest); + NbiFreeRequest (Device, NetbiosFindNameRequest); + + NbiDereferenceDevice (Device, DREF_NB_FIND_NAME); + + } + + + // + // We referenced this temporarily so we could use it in here, + // deref and check if we need to delete it. + // + + if (Result == NetbiosNameFound) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free in CacheHandlePending"); + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + + } + +} /* CacheHandlePending */ + + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_NAME_RECOGNIZED frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + PNETBIOS_CACHE NameCache; + PREQUEST NetbiosFindNameRequest; + PNB_SEND_RESERVED Reserved; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteNetbiosAddress; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + +#if 0 + // + // BETABUGBUG: We should handle responses from network 0 + // differently -- if they are for a group name, we should + // keep them around but only until we get a non-zero + // response from the same card. + // + + if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) { + return; + } +#endif + + + // + // We need to scan our queue of pending find name packets + // to see if someone is waiting for this name. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // Find names which have already found unique names are + // "dead", waiting for FindNameTimeout to remove them, + // and should be ignored when scanning the list. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + continue; + } + + if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) { + break; + } + } + + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Scan for any netbios find name requests on the queue, and + // inform them about this remote. We need to do this on every + // response because group names need every computer recorded, + // but the normal cache only includes one entry per network. + // + + for (p = Device->WaitingNetbiosFindName.Flink; + p != &Device->WaitingNetbiosFindName; + p = p->Flink) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + Connectionless->NameFrame.Name, + RemoteNetbiosAddress->NetbiosName, + 16)) { + continue; + } + + // + // This will update the request status if needed. + // + + NbiUpdateNetbiosFindName( + NetbiosFindNameRequest, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + (TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork, + (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0)); + + } + + + // + // See what is up with this pending find name packet. + // + + if (Reserved->u.SR_FN.NewCache == NULL) { + + // + // This is the first response we have received, so we + // allocate the initial entry with room for a single + // entry. + // + + NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (NameCache == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n", + NameCache, Reserved->u.SR_FN.NetbiosName, + *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork))); + + RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16); + NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); + NameCache->ReferenceCount = 1; + RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + NameCache->NetworksAllocated = 1; + NameCache->NetworksUsed = 1; + NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + + if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) { + + NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup); + NameCache->Unique = FALSE; + + } else { + + NB_SET_SR_FN_STATUS( + Reserved, + NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup); + + } + + Reserved->u.SR_FN.NewCache = NameCache; + + // + // If this packet was not routed to us and is for a group name, + // rather than use whatever local target it happened to come + // from we set it up so that it is broadcast on that net. + // + + if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) { +#if defined(_PNP_POWER) + NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); + RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[0].LocalTarget = *RemoteAddress; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // Complete pending requests now, since it is a unique + // name we have all the information we will get. + // + + NameCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + NameCache); + + // + // Reference it since CacheHandlePending uses it + // with the lock released. CacheHandlePending + // will dereference it. + // + + ++NameCache->ReferenceCount; + + // + // This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + NameCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // We already have a response to this frame. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // BUGBUG: Should we check that the response is also + // unique? Not much to do since I don't know of an + // equivalent to the netbeui NAME_IN_CONFLICT. + // + + } else { + + // + // This is a group name. + // + + if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) { + + // + // Update our information about this network if needed. + // This may free the existing cache and allocate a new one. + // + + Reserved->u.SR_FN.NewCache = + CacheUpdateNameCache( + Reserved->u.SR_FN.NewCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *) + Connectionless->IpxHeader.SourceNetwork, + FALSE); + + } else { + + // + // BUGBUG: This respondent thinks it is a unique name + // but we think it is group, should we do something? + // + + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessNameRecognized */ + + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ) + +/*++ + +Routine Description: + + This routine is called to update a netbios cache entry + with a new network, if it is does not already contain + information about the network. It is called when a frame + is received advertising the appropriate cache entry, which + is either a group name or the broadcast name. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH IT HELD. + +Arguments: + + NameCache - The name cache entry to update. + + RemoteAddress - The remote address on which a frame was received. + + IpxAddress - The source IPX address of the frame. + + ModifyQueue - TRUE if we should update the queue which this + cache entry is in, if we reallocate it. + +Return Value: + + The netbios cache entry, either the original or a reallocated one. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT NewNetworks; + PNETBIOS_CACHE NewNameCache; + PLIST_ENTRY OldPrevious; + UINT i; + + // + // See if we already know about this network. + // + + for (i = 0; i < NameCache->NetworksUsed; i++) { + if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) { + return NameCache; + } + } + + // + // We need to add information about this network + // to the name cache entry. If we have to allocate + // a new one we do that. + // + + NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n", + SourceAddress->NetworkAddress, + NameCache->NetbiosName)); + + if (NameCache->NetworksUsed == NameCache->NetworksAllocated) { + + // + // We double the number of entries allocated until + // we hit 16, then add 8 at a time. + // + + if (NameCache->NetworksAllocated < 16) { + NewNetworks = NameCache->NetworksAllocated * 2; + } else { + NewNetworks = NameCache->NetworksAllocated + 8; + } + + + NewNameCache = NbiAllocateMemory( + sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge cache entry"); + + if (NewNameCache == NULL) { + return NameCache; + } + + NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n", + NameCache, NewNameCache, NameCache->NetbiosName)); + + // + // Copy the new current data to the new one. + // + + RtlCopyMemory( + NewNameCache, + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK))); + + NewNameCache->NetworksAllocated = NewNetworks; + NewNameCache->ReferenceCount = 1; + + if (ModifyQueue) { + + // + // Insert at the same place as the old one. The time + // stamp is the same as the old one. + // + + + ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache ); + + } + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + NameCache = NewNameCache; + + } + + NameCache->Networks[NameCache->NetworksUsed].Network = + SourceAddress->NetworkAddress; + + // + // If this packet was not routed to us, then store the local + // target for a correct broadcast. + // + + if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) { +#if defined(_PNP_POWER) + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress; + } + + ++NameCache->NetworksUsed; + + return NameCache; + +} /* CacheUpdateNameCache */ + + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ) + +/*++ + +Routine Description: + + This routine is called when an add name frame is received. + If it is for a group name it checks if our cache entry for + that group name needs to be updated to include a new network; + for all frames it checks if our broadcast cache entry needs + to be updated to include a new network. + +Arguments: + + RemoteAddress - The address the frame was received from. + + Connectionless - The header of the received add name. + + LocalFrame - TRUE if the frame was sent locally. + +Return Value: + + None. + +--*/ + +{ + PUCHAR NetbiosName; + PNETBIOS_CACHE NameCache; + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + // + // First look up the broadcast name. + // + // BUGBUG: We should cache a pointer to the cache name + // for the broadcast entry, if there is one. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if (!LocalFrame) { + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosBroadcastName, + &NameCache ) == STATUS_SUCCESS ) { + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + } + + } + + + // + // Now see if our database needs to be updated based on this. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Connectionless->NameFrame.Name, + &NameCache ) == STATUS_SUCCESS ) { + + + if (!NameCache->Unique) { + + if (!LocalFrame) { + + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + + } + + } else { + + // + // To be safe, delete any unique names we get add + // names for (we will requery next time we need it). + // BUGBUG: Update the database instead -- but then + // we may not get the best route?? + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache ); + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + +} /* CacheUpdateFromAddName */ + + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_DELETE_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PUCHAR NetbiosName; + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // We want to update our netbios cache to reflect the + // fact that this name is no longer valid. + // + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosName, + &CacheName ) == STATUS_SUCCESS ) { + + // + // We don't track group names since we don't know if + // this is the last person that owns it. We also drop + // the frame if does not come from the person we think + // owns this name. + // + + if ((!CacheName->Unique) || + (CacheName->NetworksUsed == 0) || + (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName)); + + }else { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // We have a cache entry, take it out of the list. If no + // one else is using it, delete it; if not, they will delete + // it when they are done. + // + + + RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName); + + if (--CacheName->ReferenceCount == 0) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessDeleteName */ + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry in the hash table + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + USHORT HashIndex; + + // + // Keep a threshold of how many entries do we keep in the table. + // If it crosses the threshold, just remove the oldest entry + // + if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) { + PNETBIOS_CACHE OldestCacheEntry = NULL; + PNETBIOS_CACHE NextEntry; + PLIST_ENTRY p; + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) { + NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + if ( OldestCacheEntry ) { + if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) { + OldestCacheEntry = NextEntry; + } + } else { + OldestCacheEntry = NextEntry; + } + } + } + + CTEAssert( OldestCacheEntry ); + + NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry)); + RemoveEntryList (&OldestCacheEntry->Linkage); + CacheTable->CurrentEntries--; + + if (--OldestCacheEntry->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry)); + + NbiFreeMemory( + OldestCacheEntry, + sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } + HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage ); + CacheTable->CurrentEntries++; +} /* InsertInNetbiosCacheTable */ + + +__inline +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry at the same place where + the old entry was. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY OldPrevious; + + OldPrevious = OldEntry->Linkage.Blink; + RemoveEntryList (&OldEntry->Linkage); + InsertHeadList (OldPrevious, &NewEntry->Linkage); +} /* ReinsertInNetbiosCacheTable */ + +__inline +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine removes an entry from the cache table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + RemoveEntryList( &CacheEntry->Linkage ); + CacheTable->CurrentEntries--; +} /* RemoveFromNetbiosCacheTable */ + + + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ) + +/*++ + +Routine Description: + + This routine removes all the old entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + AgeLimit - All the entries older than AgeLimit will be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // see if any entries have been around for more than agelimit + // + + if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) { + + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } else { + + break; + + } + } // for loop + } // for loop +} /* FlushOldFromNetbiosCacheTable */ + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all the failed entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // flush all the failed cache entries. + // We do this when a new adapter appears, and there's a possiblity that + // the failed entries might succeed now on the new adapter. + // + + if (CacheName->NetworksUsed == 0) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + CTEAssert( CacheName->ReferenceCount == 1 ); + CTEAssert( CacheName->NetworksAllocated == 1 ); + + NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE), + MEMORY_CACHE, + "Aged out"); + + } + } // for loop + } // for loop +} /* FlushFailedNetbiosCacheEntries */ + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ) + +/*++ + +Routine Description: + + This routine removes all invalid route entries from the hash table. + Routes become invalid when the binding is deleted in Ipx due to PnP + event. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + InvalidRouteNicId - NicId of the invalid routes. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + USHORT HashIndex; + PDEVICE Device = NbiDevice; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + + for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) { + for (p = Device->NameCache->Bucket[ HashIndex ].Flink; + p != &Device->NameCache->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Flink; + + + // + // Remove each of those routes which is using this NicId. + // if no routes left, then flush the cache entry also. + // ( unique names have only one route anyways ) + // + for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) { + if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) { + CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE))); + for ( j = i+1; j < CacheName->NetworksUsed; j++ ) { + CacheName->Networks[j-1] = CacheName->Networks[j]; + } + NetworksRemoved++; + } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) { + CacheName->Networks[i].LocalTarget.NicHandle.NicId--; + } + } + CTEAssert( NetworksRemoved <= CacheName->NetworksUsed ); + if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId )); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + } + } // for loop + } // for loop +} /* RemoveInvalidRoutesFromNetbiosCacheTable */ + + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ) + +/*++ + +Routine Description: + + This routine finds a netbios name in the Hash Table and returns + the corresponding cache entry. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Pointer to the netbios cache entry if found. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_UNSUCCESSFUL - otherwise. + +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE FoundCacheName; + + + HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink; + p != &CacheTable->Bucket[ HashIndex ]; + p = p->Flink) { + + FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + // + // See if this entry is for the same name we are looking for. + + if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) { + *CacheEntry = FoundCacheName; + return STATUS_SUCCESS; + } + } + + return STATUS_UNSUCCESSFUL; +} /* FindInNetbiosCacheTable */ + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ) + +/*++ + +Routine Description: + + This routine creates a new hash table for netbios cache + and initializes it. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + NewTable - The pointer of the table to be created. + + MaxHashIndex - Number of buckets in the hash table. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory. + +--*/ + +{ + USHORT i; + + *NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) , + MEMORY_CACHE, "Cache Table"); + + if ( *NewTable ) { + for ( i = 0; i < MaxHashIndex; i++ ) { + InitializeListHead(& (*NewTable)->Bucket[i] ); + } + + (*NewTable)->MaxHashIndex = MaxHashIndex; + (*NewTable)->CurrentEntries = 0; + return STATUS_SUCCESS; + } + else { + NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") ); + return STATUS_INSUFFICIENT_RESOURCES; + } + +} /* CreateNetbiosCacheTable */ + + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all entries from the hash table. + and free up the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) { + + p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] )); + CacheTable->CurrentEntries--; + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free entries"); + + } + } // for loop + + CTEAssert( CacheTable->CurrentEntries == 0 ); + + NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) , + MEMORY_CACHE, "Free Cache Table"); + +} /* DestroyNetbiosCacheTable */ + + diff --git a/private/ntos/tdi/isn/nb/config.c b/private/ntos/tdi/isn/nb/config.c new file mode 100644 index 000000000..8689c5d20 --- /dev/null +++ b/private/ntos/tdi/isn/nb/config.c @@ -0,0 +1,661 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN Netbios module. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiGetConfiguration) +#pragma alloc_text(INIT,NbiFreeConfiguration) +#pragma alloc_text(INIT,NbiGetConfigValue) +#pragma alloc_text(INIT,NbiAddBind) +#pragma alloc_text(INIT,NbiAddExport) +#pragma alloc_text(INIT,NbiReadLinkageInformation) +#endif + + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of Netbios' node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG One = 1; + ULONG Two = 2; + ULONG Three = 3; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG FortyEight = 48; + ULONG Sixty = 60; + ULONG TwoFifty = 250; + ULONG FiveHundred = 500; + ULONG MaxMTU = 0xffffffff; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"AckDelayTime", &TwoFifty } , // milliseconds + { L"AckWindow", &Two } , + { L"AckWindowThreshold", &FiveHundred } , // milliseconds + { L"EnablePiggyBackAck", &One } , + { L"Extensions", &One } , + { L"RcvWindowMax", &Four } , + { L"BroadcastCount", &Three } , + { L"BroadcastTimeout", &One } , // half-seconds + { L"ConnectionCount", &Five } , + { L"ConnectionTimeout", &Two } , // half-seconds + { L"InitPackets", &Eight } , + { L"MaxPackets", &FortyEight } , + { L"InitialRetransmissionTime", &FiveHundred } , // milliseconds + { L"Internet", &One } , + { L"KeepAliveCount", &Eight } , + { L"KeepAliveTimeout", &Sixty } , // half-seconds + { L"RetransmitMax", &Eight } , + { L"RouterMTU", &MaxMTU } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = NbiAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, sizeof(CONFIG), MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + Config->BindName.Buffer = NULL; + Config->DriverObject = DriverObject; // save this to log errors + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbiReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)NbiAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG); + NbiFreeConfiguration(Config); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + + // + // Determine what name to export and who to bind to. + // + + Status = NbiReadLinkageInformation (Config); + + if (Status != STATUS_SUCCESS) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + return Status; + } + + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-18) Call NbiSetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = NbiGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 19) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + NbiFreeConfiguration(Config); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 701, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + NbiFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* NbiGetConfiguration */ + + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get free any storage that was allocated + by NbiGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + if (Config->BindName.Buffer) { + NbiFreeMemory (Config->BindName.Buffer, Config->BindName.MaximumLength, MEMORY_CONFIG, "BindName"); + } + + if (Config->DeviceName.Buffer) { + NbiFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + NbiFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* NbiFreeConfig */ + + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + ULONG Data = *(UNALIGNED ULONG *)ValueData; + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + + switch ( (ULONG) EntryContext ) { + case CONFIG_ROUTER_MTU: + if ( ( Data - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER) ) <= 0 ) { + Config->Parameters[CONFIG_ROUTER_MTU] = 0xffffffff; + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 704, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_SUCCESS; + } + break; + default: + break; + } + + NB_DEBUG2 (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, Data)); + Config->Parameters[(ULONG)EntryContext] = Data; + + return STATUS_SUCCESS; + +} /* NbiGetConfigValue */ + + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read bind value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "BindName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->BindName.Buffer = NameBuffer; + Config->BindName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->BindName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddBind */ + + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddExport */ + + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; // set to TRUE when a value is read correctly + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call NbiAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = NbiAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 702, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + + // + // 1) Change to call NbiAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = NbiAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 703, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* NbiReadLinkageInformation */ + diff --git a/private/ntos/tdi/isn/nb/config.h b/private/ntos/tdi/isn/nb/config.h new file mode 100644 index 000000000..99b6c6357 --- /dev/null +++ b/private/ntos/tdi/isn/nb/config.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN Netbios module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_ACK_DELAY_TIME 0 +#define CONFIG_ACK_WINDOW 1 +#define CONFIG_ACK_WINDOW_THRESHOLD 2 +#define CONFIG_ENABLE_PIGGYBACK_ACK 3 +#define CONFIG_EXTENSIONS 4 +#define CONFIG_RCV_WINDOW_MAX 5 +#define CONFIG_BROADCAST_COUNT 6 +#define CONFIG_BROADCAST_TIMEOUT 7 +#define CONFIG_CONNECTION_COUNT 8 +#define CONFIG_CONNECTION_TIMEOUT 9 +#define CONFIG_INIT_PACKETS 10 +#define CONFIG_MAX_PACKETS 11 +#define CONFIG_INIT_RETRANSMIT_TIME 12 +#define CONFIG_INTERNET 13 +#define CONFIG_KEEP_ALIVE_COUNT 14 +#define CONFIG_KEEP_ALIVE_TIMEOUT 15 +#define CONFIG_RETRANSMIT_MAX 16 +#define CONFIG_ROUTER_MTU 17 +#define CONFIG_PARAMETERS 18 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + NDIS_STRING BindName; // device to bind to + PWSTR RegistryPathBuffer; // path to config info + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ); + diff --git a/private/ntos/tdi/isn/nb/connect.c b/private/ntos/tdi/isn/nb/connect.c new file mode 100644 index 000000000..7ee7204b5 --- /dev/null +++ b/private/ntos/tdi/isn/nb/connect.c @@ -0,0 +1,3628 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This routine contains the code to handle connect requests + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef RASAUTODIAL +#include +#include + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ); +#endif // RASAUTODIAL + + + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +/*++ + +Routine Description: + + This routine is called when a find route request + previously issued to IPX completes. + +Arguments: + + FindRouteRequest - The find route request that was issued. + + FoundRoute - TRUE if the route was found. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + UINT i; + BOOLEAN LocalRoute; + USHORT TickCount; + PREQUEST RequestToComplete; + PUSHORT Counts; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + Connection = CONTAINING_RECORD (FindRouteRequest, CONNECTION, FindRouteRequest); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection->FindRouteInProgress = FALSE; + + if (FoundRoute) { + + // + // See if the route is local or not (for local routes + // we use the real MAC address in the local target, but + // the NIC ID may not be what we expect. + // + + LocalRoute = TRUE; + + for (i = 0; i < 6; i++) { + if (FindRouteRequest->LocalTarget.MacAddress[i] != 0x00) { + LocalRoute = FALSE; + } + } + + if (LocalRoute) { + +#if defined(_PNP_POWER) + Connection->LocalTarget.NicHandle = FindRouteRequest->LocalTarget.NicHandle; +#else + Connection->LocalTarget.NicId = FindRouteRequest->LocalTarget.NicId; +#endif _PNP_POWER + + } else { + + Connection->LocalTarget = FindRouteRequest->LocalTarget; + + } + + Counts = (PUSHORT)(&FindRouteRequest->Reserved2); + TickCount = Counts[0]; + + if (TickCount > 1) { + + // + // Each tick is 55 ms, and for our timeout we use 10 ticks + // worth (this makes tick count of 1 be about 500 ms, the + // default). + // + // We get 55 milliseconds from + // + // 1 second * 1000 milliseconds 55 ms + // -------- ----------------- = ----- + // 18.21 ticks 1 second tick + // + + Connection->TickCount = TickCount; + Connection->BaseRetransmitTimeout = (TickCount * 550) / SHORT_TIMER_DELTA; + if (Connection->State != CONNECTION_STATE_ACTIVE) { + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + } + + Connection->HopCount = Counts[1]; + + } + + // + // If the call failed we just use whatever route we had before + // (on a connect it will be from the name query response, on + // a listen from whatever the incoming connect frame had). + // + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_W_ROUTE)) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // Continue on with the session init frame. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + // Maximum packet size is the lower of RouterMtu and MaximumSendSize. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER) , Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION) ; + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + // + // Don't set RcvSequenceMax yet because we don't know + // if the connection is old or new netbios. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + + // + // We found a route, we need to start the connect + // process by sending out the session initialize + // frame. We start the timer to handle retries. + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiSendSessionInitialize (Connection); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ROUTE)) { + + if (Connection->ListenRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_ACTIVE); + RequestToComplete = Connection->ListenRequest; + Connection->ListenRequest = NULL; + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + + } else if (Connection->AcceptRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_ACCEPT, CREF_ACTIVE); + RequestToComplete = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } else { + + CTEAssert (FALSE); + RequestToComplete = NULL; + + } + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + + // Take the lowest of MaximumPacketSize ( set from the sessionInit + // frame ), MaximumSendSize and RouterMtu. + + if (Connection->MaximumPacketSize > Connection->LineInfo.MaximumSendSize - sizeof(NB_CONNECTION)) { + + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER), Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION); + + } else { + + // Connection->MaximumPacketSize is what was set by the sender so already + // accounts for the header. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER), Connection->MaximumPacketSize ) ; + + } + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + if (Connection->NewNetbios) { + CTEAssert (Connection->LocalRcvSequenceMax == 4); // should have been set + Connection->LocalRcvSequenceMax = Connection->ReceiveWindowSize; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + ++Device->Statistics.OpenConnections; + + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // StartWatchdog acquires TimerLock, so we have to + // free Lock first. + // + + + NbiStartWatchdog (Connection); + + // + // This releases the connection lock, so that SessionInitAckData + // can't be freed before it is copied. + // + + NbiSendSessionInitAck( + Connection, + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + &LockHandle1); + + if (RequestToComplete != NULL) { + + REQUEST_STATUS(RequestToComplete) = STATUS_SUCCESS; + + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_FIND_ROUTE); + +} /* NbiFindRouteComplete */ + + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PCONNECTION Connection; + PFILE_FULL_EA_INFORMATION ea; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + if (!(Connection = NbiCreateConnection (Device))) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &Connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)Connection; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONNECTION_FILE; +#ifdef ISN_NT + Connection->FileObject = IrpSp->FileObject; +#endif + + return STATUS_SUCCESS; + +} /* NbiOpenConnection */ + + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called to stop an active connection. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection to be stopped. + + DisconnectStatus - The reason for the disconnect. One of: + STATUS_LINK_FAILED: We timed out trying to probe the remote. + STATUS_REMOTE_DISCONNECT: The remote sent a session end. + STATUS_LOCAL_DISCONNECT: The local side disconnected. + STATUS_CANCELLED: A send or receive on this connection was cancelled. + STATUS_INVALID_CONNECTION: The local side closed the connection. + STATUS_INVALID_ADDRESS: The local side closed the address. + + LockHandle - The handle which the connection lock was acquired with. + +Return Value: + + None. + +--*/ + +{ + PREQUEST ListenRequest, AcceptRequest, SendRequest, ReceiveRequest, + DisconnectWaitRequest, ConnectRequest; + PREQUEST Request, TmpRequest; + BOOLEAN DerefForPacketize; + BOOLEAN DerefForWaitPacket; + BOOLEAN DerefForActive; + BOOLEAN DerefForWaitCache; + BOOLEAN SendSessionEnd; + BOOLEAN ActiveReceive; + BOOLEAN IndicateToClient; + BOOLEAN ConnectionWasActive; + PDEVICE Device = NbiDevice; + PADDRESS_FILE AddressFile; + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + + NB_DEBUG2 (CONNECTION, ("Stop connection %lx (%lx)\n", Connection, DisconnectStatus)); + + // + // These flags control our actions after we set the state to + // DISCONNECT. + // + + DerefForPacketize = FALSE; + DerefForWaitPacket = FALSE; + DerefForActive = FALSE; + DerefForWaitCache = FALSE; + SendSessionEnd = FALSE; + ActiveReceive = FALSE; + IndicateToClient = FALSE; + ConnectionWasActive = FALSE; + + // + // These contain requests or queues of request to complete. + // + + ListenRequest = NULL; + AcceptRequest = NULL; + SendRequest = NULL; + ReceiveRequest = NULL; + DisconnectWaitRequest = NULL; + ConnectRequest = NULL; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + --Device->Statistics.OpenConnections; + + ConnectionWasActive = TRUE; + + Connection->Status = DisconnectStatus; + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_LOCAL_DISCONNECT)) { + + // + // Send out session end frames, but fewer if + // we timed out. + // + // BUGBUG: What about STATUS_CANCELLED? + // + + Connection->Retries = (DisconnectStatus == STATUS_LOCAL_DISCONNECT) ? + Device->ConnectionCount : + (Device->ConnectionCount / 2); + + SendSessionEnd = TRUE; + Connection->SubState = CONNECTION_SUBSTATE_D_W_ACK; + + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + if (Connection->ReceiveState == CONNECTION_RECEIVE_TRANSFER) { + ActiveReceive = TRUE; + } + + Connection->State = CONNECTION_STATE_DISCONNECT; + DerefForActive = TRUE; + + if (Connection->DisconnectWaitRequest != NULL) { + DisconnectWaitRequest = Connection->DisconnectWaitRequest; + Connection->DisconnectWaitRequest = NULL; + } + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_REMOTE_DISCONNECT) || + (DisconnectStatus == STATUS_CANCELLED)) { + + IndicateToClient = TRUE; + + } + + // + // If we are inside NbiAssignSequenceAndSend, add + // a reference so the connection won't go away during it. + // + + if (Connection->NdisSendsInProgress > 0) { + *(Connection->NdisSendReference) = TRUE; + NB_DEBUG2 (SEND, ("Adding CREF_NDIS_SEND to %lx\n", Connection)); + NbiReferenceConnectionLock (Connection, CREF_NDIS_SEND); + } + + // + // Clean up some other stuff. + // + + Connection->ReceiveUnaccepted = 0; + Connection->CurrentIndicateOffset = 0; + + // + // Update our counters. BUGBUG: Some of these we + // never use. + // + + switch (DisconnectStatus) { + + case STATUS_LOCAL_DISCONNECT: + ++Device->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + ++Device->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + ++Device->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + ++Device->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + ++Device->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + ++Device->Statistics.RemoteResourceFailures; + break; + case STATUS_INVALID_CONNECTION: + case STATUS_INVALID_ADDRESS: + case STATUS_INSUFFICIENT_RESOURCES: + ++Device->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + case STATUS_REMOTE_NOT_LISTENING: + ++Device->Statistics.NotFoundFailures; + break; + default: + CTEAssert(FALSE); + break; + } + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // + // There is a connect in progress. We have to find ourselves + // in the pending connect queue if we are there. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) { + RemoveEntryList (REQUEST_LINKAGE(Connection->ConnectRequest)); + DerefForWaitCache = TRUE; + } + + if (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) { + + ConnectRequest = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + } + + } + + + // + // If we allocated this memory, free it. + // + + if (Connection->SessionInitAckDataLength > 0) { + + NbiFreeMemory( + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + MEMORY_CONNECTION, + "SessionInitAckData"); + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + + } + + + if (Connection->ListenRequest != NULL) { + + ListenRequest = Connection->ListenRequest; + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(ListenRequest)); // take out of Device->ListenQueue + + } + + if (Connection->AcceptRequest != NULL) { + + AcceptRequest = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } + + + // + // BUGBUG: Do we need to stop the connection timer? + // I don't think so. + // + + + + // + // A lot of this we only have to tear down if we were + // active before this, because once we are stopping nothing + // new will get started. BUGBUG: Some of the other stuff + // can be put inside this if also. + // + + if (ConnectionWasActive) { + + // + // Stop any receives. If there is one that is actively + // transferring we leave it and just run down the rest + // of the queue. If not, we queue the rest of the + // queue on the back of the current one and run + // down them all. + // + + if (ActiveReceive) { + + ReceiveRequest = Connection->ReceiveQueue.Head; + + // + // Connection->ReceiveRequest will get set to NULL + // when the transfer completes. + // + + } else { + + ReceiveRequest = Connection->ReceiveRequest; + if (ReceiveRequest) { + REQUEST_SINGLE_LINKAGE (ReceiveRequest) = Connection->ReceiveQueue.Head; + } else { + ReceiveRequest = Connection->ReceiveQueue.Head; + } + Connection->ReceiveRequest = NULL; + + } + + Connection->ReceiveQueue.Head = NULL; + + + if ((Request = Connection->FirstMessageRequest) != NULL) { + + // + // If the current request has some sends outstanding, then + // we dequeue it from the queue to let it complete when + // the sends complete. In that case we set SendRequest + // to be the rest of the queue, which will be aborted. + // If the current request has no sends, then we put + // queue everything to SendRequest to be aborted below. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + // + // NOTE: If this is a multi-request message, then + // the linkage of Request will already point to the + // send queue head, but we don't bother checking. + // + + SendRequest = Request; + REQUEST_SINGLE_LINKAGE (Request) = Connection->SendQueue.Head; + + } else { + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->FirstMessageRequest = NULL; + + } else { + + // + // This may happen if we were sending a probe when a + // send was submitted, and the probe timed out. + // + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->SendQueue.Head = NULL; + + } + + + if (Connection->OnWaitPacketQueue) { + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + DerefForWaitPacket = TRUE; + } + + if (Connection->OnPacketizeQueue) { + Connection->OnPacketizeQueue = FALSE; + RemoveEntryList (&Connection->PacketizeLinkage); + DerefForPacketize = TRUE; + } + + // + // BUGBUG: Should we check if DataAckPending is TRUE and + // send an ack?? + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // We can't acquire TimerLock with Lock held, since + // we sometimes call ReferenceConnection (which does an + // interlocked add using Lock) with TimerLock held. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle3); + + if (Connection->OnShortList) { + Connection->OnShortList = FALSE; + RemoveEntryList (&Connection->ShortList); + } + + if (Connection->OnLongList) { + Connection->OnLongList = FALSE; + RemoveEntryList (&Connection->LongList); + } + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + RemoveEntryList (&Connection->DataAckLinkage); + Device->DataAckQueueChanged = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle3); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + + if (IndicateToClient) { + + AddressFile = Connection->AddressFile; + + if (AddressFile->RegisteredHandler[TDI_EVENT_DISCONNECT]) { + + NB_DEBUG2 (CONNECTION, ("Session end indicated on connection %lx\n", Connection)); + + (*AddressFile->DisconnectHandler)( + AddressFile->HandlerContexts[TDI_EVENT_DISCONNECT], + Connection->Context, + 0, // DisconnectData + NULL, + 0, // DisconnectInformation + NULL, + TDI_DISCONNECT_RELEASE); // DisconnectReason. BUGBUG: Clean it up? + + } + + } + + + if (DisconnectWaitRequest != NULL) { + + // + // Make the TDI tester happy by returning CONNECTION_RESET + // here. + // + + if (DisconnectStatus == STATUS_REMOTE_DISCONNECT) { + REQUEST_STATUS(DisconnectWaitRequest) = STATUS_CONNECTION_RESET; + } else { + REQUEST_STATUS(DisconnectWaitRequest) = DisconnectStatus; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (DisconnectWaitRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (DisconnectWaitRequest); + NbiFreeRequest (Device, DisconnectWaitRequest); + + } + + if (ConnectRequest != NULL) { + + REQUEST_STATUS (ConnectRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ConnectRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION(ListenRequest) = 0; + REQUEST_STATUS(ListenRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest(Device, ListenRequest); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } + + if (AcceptRequest != NULL) { + + REQUEST_INFORMATION(AcceptRequest) = 0; + REQUEST_STATUS(AcceptRequest) = STATUS_LOCAL_DISCONNECT; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest(Device, AcceptRequest); + + NbiDereferenceConnection (Connection, CREF_ACCEPT); + + } + + while (ReceiveRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (ReceiveRequest); + + REQUEST_STATUS (ReceiveRequest) = DisconnectStatus; + REQUEST_INFORMATION (ReceiveRequest) = 0; + + NB_DEBUG2 (RECEIVE, ("StopConnection aborting receive %lx\n", ReceiveRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ReceiveRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ReceiveRequest); + NbiFreeRequest (Device, ReceiveRequest); + + ++Connection->ConnectionInfo.ReceiveErrors; + + ReceiveRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } + + while (SendRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + + REQUEST_STATUS (SendRequest) = DisconnectStatus; + REQUEST_INFORMATION (SendRequest) = 0; + + NB_DEBUG2 (SEND, ("StopConnection aborting send %lx\n", SendRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + + ++Connection->ConnectionInfo.TransmissionErrors; + + SendRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + if (SendSessionEnd) { + NbiSendSessionEnd (Connection); + } + + if (DerefForWaitCache) { + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + } + + if (DerefForPacketize) { + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + } + + if (DerefForWaitPacket) { + NbiDereferenceConnection (Connection, CREF_W_PACKET); + } + + if (DerefForActive) { + NbiDereferenceConnection (Connection, CREF_ACTIVE); + } + +} /* NbiStopConnection */ + + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (CONNECTION, ("Close connection %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (Connection->ReferenceCount == 0) { + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // Even if the ref count is zero and some thread has already done cleanup, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + if ( Connection->CanBeDestroyed && ( Connection->ThreadsInHandleConnectionZero == 0 ) ) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NbiDestroyConnection(Connection); + Status = STATUS_SUCCESS; + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + return Status; + +} /* NbiCloseConnection */ + + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and + the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; +#ifdef ISN_NT + PFILE_OBJECT FileObject; +#endif + PADDRESS_FILE AddressFile; + PADDRESS Address; + PTDI_REQUEST_KERNEL_ASSOCIATE Parameters; + CTELockHandle LockHandle; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + + // + // The request request parameters hold + // get a pointer to the address FileObject, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)REQUEST_PARAMETERS(Request); + +#ifdef ISN_NT + + Status = ObReferenceObjectByHandle ( + Parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + AddressFile = (PADDRESS_FILE)(FileObject->FsContext); + +#else + + // + // I don't know how this works in a VxD. + // + + AddressFile = (PADDRESS_FILE)(Parameters->AddressHandle); + +#endif + + // + // Make sure the address file is valid, and reference it. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + +#ifdef ISN_NT + ObDereferenceObject (FileObject); +#endif + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Associate connection %lx with address file %lx\n", + Connection, AddressFile)); + + + // + // Now insert the connection into the database of the address. + // + + Address = AddressFile->Address; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFile != NULL) { + + // + // The connection is already associated with + // an address file. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_CONNECTION; + + } else { + + if (AddressFile->State == ADDRESSFILE_STATE_OPEN) { + + Connection->AddressFile = AddressFile; + Connection->AddressFileLinked = TRUE; + InsertHeadList (&AddressFile->ConnectionDatabase, &Connection->AddressFileLinkage); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_ADDRESS; + } + + } + +#ifdef ISN_NT + + // + // We don't need the reference to the file object, we just + // used it to get from the handle to the object. + // + + ObDereferenceObject (FileObject); + +#endif + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAssociateAddress */ + + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the disassociation of the connection + and the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Disassociate connection %lx\n", Connection)); + + + // + // First check if the connection is still active. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if (Connection->State != CONNECTION_STATE_INACTIVE) { + + // + // This releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + // + // BUGBUG: Keep the sync through the function?? + // + + NB_END_SYNC (&SyncContext); + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Make sure the connection is associated and is not in the + // middle of disassociating. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL)) { + + if (Connection->ReferenceCount == 0) { + + // + // Because the connection still has a reference to + // the address file, we know it is still valid. We + // set the connection address file to the temporary + // value of -1, which prevents somebody else from + // disassociating it and also prevents a new association. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + // + // Set this so when the count goes to 0 it will + // be disassociated and the request completed. + // + + Connection->DisassociatePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisassociateAddress */ + + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine posts a listen on a connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the listen. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_WAITING; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + + if (!Request->Cancel) { + + NB_DEBUG2 (CONNECTION, ("Queued listen %lx on %lx\n", Request, Connection)); + InsertTailList (&Device->ListenQueue, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelListen); + Connection->ListenRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_LISTEN); + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled listen %lx on %lx\n", Request, Connection)); + Connection->State = CONNECTION_STATE_INACTIVE; + Status = STATUS_CANCELLED; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiListen */ + + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine accepts a connection to a remote machine. The + connection must previously have completed a listen with + the TDI_QUERY_ACCEPT flag on. + +Arguments: + + Device - The netbios device. + + Request - The request describing the accept. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiTransferReferenceConnection (Connection, CREF_W_ACCEPT, CREF_ACCEPT); + Connection->AcceptRequest = Request; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->Retries = NbiDevice->KeepAliveCount; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + NB_DEBUG2 (CONNECTION, ("Accept received on %lx\n", Connection)); + + Status = STATUS_PENDING; + + } else { + + NB_DEBUG (CONNECTION, ("Accept received on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAccept */ + + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_CONNECT Parameters; +#if 0 + PLARGE_INTEGER RequestedTimeout; + LARGE_INTEGER RealTimeout; +#endif + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteName == NULL) { + + // + // There is no netbios remote address specified. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_BAD_NETWORK_PATH; + + } else { + + NbiReferenceConnectionLock (Connection, CREF_CONNECT); + Connection->State = CONNECTION_STATE_CONNECTING; + RtlCopyMemory (Connection->RemoteName, RemoteName->NetbiosName, 16); + + Connection->Retries = Device->ConnectionCount; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + Status = NbiTdiConnectFindName( + Device, + Request, + Connection, + CancelLH, + LockHandle1, + LockHandle2, + &bLockFreed); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Connect on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + if (!bLockFreed) { + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiConnect */ + + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ) +{ + NTSTATUS Status; + PNETBIOS_CACHE CacheName; + + // + // See what is up with this Netbios name. + // + + Status = CacheFindName( + Device, + FindNameConnect, + Connection->RemoteName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this connect + // request and processing will be resumed when + // we get a response. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_FIND_NAME; + + + if (!Request->Cancel) { + + InsertTailList( &Device->WaitingConnects, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelConnectFindName); + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_WAIT_CACHE); + NB_DEBUG2 (CONNECTION, ("Queueing up connect %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // We don't need to worry about referencing CacheName + // because we stop using it before we release the lock. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelConnectWaitResponse); + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, ConnectionLH); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_DEBUG2 (CONNECTION, ("Found connect cached %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NB_FREE_LOCK (&Connection->Lock, ConnectionLH); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + Status = STATUS_PENDING; + + // + // This jump is like falling out of the if, except + // it skips over freeing the connection lock since + // we just did that. + // + + *pbLockFreed = TRUE; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else { + + // + // We could not find or queue a request for + // this remote, fail it. When the refcount + // drops the state will go to INACTIVE and + // the connection ID will be deassigned. + // + + if (Status == STATUS_DEVICE_DOES_NOT_EXIST) { + Status = STATUS_BAD_NETWORK_PATH; + } + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + } + + return Status; +} /* NbiTdiConnectFindName */ + + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + BOOLEAN DisconnectWait; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + CTELockHandle CancelLH; + + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + DisconnectWait = (BOOLEAN) + ((((PTDI_REQUEST_KERNEL_DISCONNECT)(REQUEST_PARAMETERS(Request)))->RequestFlags & + TDI_DISCONNECT_WAIT) != 0); + + NB_GET_CANCEL_LOCK( &CancelLH ); + + // + // We need to be inside a sync because NbiStopConnection + // expects that. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (DisconnectWait) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // This disconnect wait will get completed by + // NbiStopConnection. + // + + if (Connection->DisconnectWaitRequest == NULL) { + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelDisconnectWait); + NB_DEBUG2 (CONNECTION, ("Disconnect wait queued on connection %lx\n", Connection)); + Connection->DisconnectWaitRequest = Request; + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled disconnect wait on connection %lx\n", Connection)); + Status = STATUS_CANCELLED; + } + + } else { + + // + // We got a second disconnect request and we already + // have one pending. + // + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, already queued on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + NB_DEBUG (CONNECTION, ("Disconnect wait submitted on disconnected connection %lx\n", Connection)); + Status = Connection->Status; + + } else { + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, bad state on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } else { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + + NB_DEBUG2 (CONNECTION, ("Disconnect of active connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + // + // This call releases the connection lock, sets + // the state to DISCONNECTING, and sends out + // the first session end. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // There is already a disconnect pending. Queue + // this one up so it completes when the refcount + // goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of disconnecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + // + // We were waiting for an accept, but instead we got + // a disconnect. Remove the reference and the teardown + // will proceed. The disconnect will complete when the + // refcount goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of accept pending connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_W_ACCEPT); + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // We are connecting, and got a disconnect. We call + // NbiStopConnection which will handle this case + // and abort the connect. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of connecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // This call releases the connection lock and + // aborts the connect request. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Disconnect of invalid connection (%d) %lx\n", + Connection->State, Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Status = STATUS_INVALID_CONNECTION; + + } + + } + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisconnect */ + + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to assign a connection ID. It picks + one whose hash table has the fewest entries. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. THE CONNECTION IS INSERTED INTO THE CORRECT HASH + ENTRY BY THIS CALL. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + TRUE if it could be successfully assigned. + +--*/ + +{ + UINT Hash; + UINT i; + USHORT ConnectionId, HashId; + PCONNECTION CurConnection; + + + CTEAssert (Connection->LocalConnectionId == 0xffff); + + // + // Find the hash bucket with the fewest entries. + // + + Hash = 0; + for (i = 1; i < CONNECTION_HASH_COUNT; i++) { + if (Device->ConnectionHash[i].ConnectionCount < Device->ConnectionHash[Hash].ConnectionCount) { + Hash = i; + } + } + + + // + // Now find a valid connection ID within that bucket. + // + + ConnectionId = Device->ConnectionHash[Hash].NextConnectionId; + + while (TRUE) { + + // + // Scan through the list to see if this ID is in use. + // + + HashId = (USHORT)(ConnectionId | (Hash << CONNECTION_HASH_SHIFT)); + + CurConnection = Device->ConnectionHash[Hash].Connections; + + while (CurConnection != NULL) { + if (CurConnection->LocalConnectionId != HashId) { + CurConnection = CurConnection->NextConnection; + } else { + break; + } + } + + if (CurConnection == NULL) { + break; + } + + if (ConnectionId >= CONNECTION_MAXIMUM_ID) { + ConnectionId = 1; + } else { + ++ConnectionId; + } + + // + // BUGBUG: What if we have 64K-1 sessions and loop forever? + // + } + + if (Device->ConnectionHash[Hash].NextConnectionId >= CONNECTION_MAXIMUM_ID) { + Device->ConnectionHash[Hash].NextConnectionId = 1; + } else { + ++Device->ConnectionHash[Hash].NextConnectionId; + } + + Connection->LocalConnectionId = HashId; + Connection->RemoteConnectionId = 0xffff; + NB_DEBUG2 (CONNECTION, ("Assigned ID %lx to %x\n", Connection->LocalConnectionId, Connection)); + + Connection->NextConnection = Device->ConnectionHash[Hash].Connections; + Device->ConnectionHash[Hash].Connections = Connection; + ++Device->ConnectionHash[Hash].ConnectionCount; + + return TRUE; + +} /* NbiAssignConnectionId */ + + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to deassign a connection ID. It removes + the connection from the hash bucket for its ID. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + None. + +--*/ + +{ + UINT Hash; + PCONNECTION CurConnection; + PCONNECTION * PrevConnection; + + // + // Make sure the connection has a valid ID. + // + + CTEAssert (Connection->LocalConnectionId != 0xffff); + + Hash = (Connection->LocalConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + CurConnection = Device->ConnectionHash[Hash].Connections; + PrevConnection = &Device->ConnectionHash[Hash].Connections; + + while (TRUE) { + + CTEAssert (CurConnection != NULL); + + // + // We can loop until we find it because it should be + // on here. + // + + if (CurConnection == Connection) { + *PrevConnection = Connection->NextConnection; + --Device->ConnectionHash[Hash].ConnectionCount; + break; + } + + PrevConnection = &CurConnection->NextConnection; + CurConnection = CurConnection->NextConnection; + + } + + Connection->LocalConnectionId = 0xffff; + +} /* NbiDeassignConnectionId */ + + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the connection timer expires. + This is either because we need to send the next session + initialize, or because our listen has timed out. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the connection. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection = (PCONNECTION)Context; + PDEVICE Device = NbiDevice; + PREQUEST Request; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (CancelLH) + + // + // Take the lock and see what we need to do. + // + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (--Connection->Retries == 0) { + + NB_DEBUG2 (CONNECTION, ("Timing out session initializes on %lx\n", Connection)); + + // + // We have just timed out this connect, we fail the + // request. When the reference count goes to 0 we + // will set the state to INACTIVE and deassign + // the connection ID. + // + + Request = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_BAD_NETWORK_PATH; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session initialize. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionInitialize (Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + if ((Connection->SubState != CONNECTION_SUBSTATE_D_W_ACK) || + (--Connection->Retries == 0)) { + + NB_DEBUG2 (CONNECTION, ("Timing out disconnect of %lx\n", Connection)); + + // + // Just dereference the connection, that will cause the + // disconnect to be completed, the state to be set + // to INACTIVE, and our connection ID deassigned. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session end. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionEnd(Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } + +} /* NbiConnectionTimeout */ + + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + listen. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_LISTEN)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_WAITING) && + (Connection->ListenRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled listen on %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel listen on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelListen */ + + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for the name to be found. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) && + (Connection->ConnectRequest == Request)) { + + // + // Make sure the request is still on the queue + // before cancelling it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + for (p = Device->WaitingConnects.Flink; + p != &Device->WaitingConnects; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + break; + } + } + + if (p != &Device->WaitingConnects) { + + NB_DEBUG2 (CONNECTION, ("Cancelled find name connect on %lx\n", Connection)); + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + Connection->ConnectRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + +#ifdef RASAUTODIAL + if (Connection->Flags & CONNECTION_FLAGS_AUTOCONNECTING) + fCanceled = NbiCancelTdiConnect(Device, Request, Connection); +#endif // RASAUTODIAL + + if (fCanceled) { + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect not found on queue %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectFindName */ + + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for a rip or session init response + from the remote. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN TimerWasStopped = FALSE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) && + (Connection->ConnectRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled wait response connect on %lx\n", Connection)); + + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectWaitResponse */ + + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + disconnect wait. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_DISCONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->DisconnectWaitRequest == Request) { + + Connection->DisconnectWaitRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelDisconnectWait */ + + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine looks up a connection based on the context. + The connection is assumed to be associated with the + specified address file. + +Arguments: + + AddressFile - Pointer to an address file. + + ConnectionContext - Connection context to find. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + CTELockHandle LockHandle1, LockHandle2; + PLIST_ENTRY p; + PADDRESS Address = AddressFile->Address; + PCONNECTION Connection; + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p=AddressFile->ConnectionDatabase.Flink; + p != &AddressFile->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + NB_GET_LOCK (&Connection->Lock, &LockHandle2); + + // + // BUGBUG: Does this spinlock ordering hurt us + // somewhere else? + // + + if (Connection->Context == ConnectionContext) { + + NbiReferenceConnection (Connection, CREF_BY_CONTEXT); + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return Connection; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return NULL; + +} /* NbiLookupConnectionByContext */ + + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates a transport connection and associates it with + the specified transport device context. The reference count in the + connection is automatically set to 1, and the reference count of the + device context is incremented. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + connection. + +Return Value: + + The newly created connection, or NULL if none can be allocated. + +--*/ + +{ + PCONNECTION Connection; + PNB_SEND_RESERVED SendReserved; + ULONG ConnectionSize; + ULONG HeaderLength; + NTSTATUS Status; + CTELockHandle LockHandle; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION); + ConnectionSize = FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + HeaderLength; + + Connection = (PCONNECTION)NbiAllocateMemory (ConnectionSize, MEMORY_CONNECTION, "Connection"); + if (Connection == NULL) { + NB_DEBUG (CONNECTION, ("Create connection failed\n")); + return NULL; + } + + NB_DEBUG2 (CONNECTION, ("Create connection %lx\n", Connection)); + RtlZeroMemory (Connection, ConnectionSize); + + +#if defined(NB_OWN_PACKETS) + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + +#else // !NB_OWN_PACKETS + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &Connection->SendPacketPoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status)); + Connection->SendPacketInUse = TRUE; + } else { + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(Connection->SendPacketPoolHandle); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + } + +#endif NB_OWN_PACKETS + + Connection->Type = NB_CONNECTION_SIGNATURE; + Connection->Size = (USHORT)ConnectionSize; + +#if 0 + Connection->AddressFileLinked = FALSE; + Connection->AddressFile = NULL; +#endif + + Connection->State = CONNECTION_STATE_INACTIVE; +#if 0 + Connection->SubState = 0; + Connection->ReferenceCount = 0; +#endif + + Connection->CanBeDestroyed = TRUE; + + Connection->TickCount = 1; + Connection->HopCount = 1; + + // + // Device->InitialRetransmissionTime is in milliseconds, as is + // SHORT_TIMER_DELTA. + // + + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + // + // Device->KeepAliveTimeout is in half-seconds, while LONG_TIMER_DELTA + // is in milliseconds. + // + + Connection->WatchdogTimeout = (Device->KeepAliveTimeout * 500) / LONG_TIMER_DELTA; + + + Connection->LocalConnectionId = 0xffff; + + // + // When the connection becomes active we will replace the + // destination address of this header with the correct + // information. + // + + RtlCopyMemory(&Connection->RemoteHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + Connection->Device = Device; + Connection->DeviceLock = &Device->Lock; + CTEInitLock (&Connection->Lock.Lock); + + CTEInitTimer (&Connection->Timer); + + InitializeListHead (&Connection->NdisSendQueue); +#if 0 + Connection->NdisSendsInProgress = 0; + Connection->DisassociatePending = NULL; + Connection->ClosePending = NULL; + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; +#endif + Connection->Flags = 0; + + NbiReferenceDevice (Device, DREF_CONNECTION); + + return Connection; + +} /* NbiCreateConnection */ + + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. We reference + it to keep it from disappearing while we use it. + +Arguments: + + Connection - potential pointer to a CONNECTION object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE Device = NbiDevice; + BOOLEAN LockHeld = FALSE; + + try { + + if ((Connection->Size == FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) && + (Connection->Type == NB_CONNECTION_SIGNATURE)) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LockHeld = TRUE; + + if (Connection->State != CONNECTION_STATE_CLOSING) { + + NbiReferenceConnectionLock (Connection, CREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx closing\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx bad signature\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyConnection: C %lx exception\n", Connection); + if (LockHeld) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyConnection */ + + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to nonpaged system pool. + +Arguments: + + Connection - Pointer to a transport connection structure to be destroyed. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = Connection->Device; +#if 0 + CTELockHandle LockHandle; +#endif + + NB_DEBUG2 (CONNECTION, ("Destroy connection %lx\n", Connection)); + + if (!Connection->SendPacketInUse) { + NbiDeinitializeSendPacket (Device, &Connection->SendPacket, Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(Connection->SendPacketPoolHandle); +#endif + } + + NbiFreeMemory (Connection, (ULONG)Connection->Size, MEMORY_CONNECTION, "Connection"); + + NbiDereferenceDevice (Device, DREF_CONNECTION); + +} /* NbiDestroyConnection */ + + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + (VOID)ExInterlockedAddUlong ( + &Connection->ReferenceCount, + 1, + &Connection->DeviceLock->Lock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnection */ + + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when the device lock is already held. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + ++Connection->ReferenceCount; + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionLock */ + + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when we are in a sync routine. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + (VOID)NB_ADD_ULONG ( + &Connection->ReferenceCount, + 1, + Connection->DeviceLock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionSync */ + + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiHandleConnectionZero to complete any disconnect, disassociate, + or close requests that have pended on the connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + CTELockHandle LockHandle; + + NB_GET_LOCK( Connection->DeviceLock, &LockHandle ); + CTEAssert( Connection->ReferenceCount ); + if ( !(--Connection->ReferenceCount) ) { + + Connection->ThreadsInHandleConnectionZero++; + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + + // + // If the refcount has dropped to 0, then the connection can + // become inactive. We reacquire the spinlock and if it has not + // jumped back up then we handle any disassociates and closes + // that have pended. + // + + NbiHandleConnectionZero (Connection); + } else { + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + } + + +} /* NbiDerefConnection */ + + +#endif + + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine handles a connection's refcount going to 0. + + BUGBUG: If two threads are in this at the same time and + the close has already come through, one of them might + destroy the connection while the other one is looking + at it. We minimize the chance of this by not derefing + the connection after calling CloseConnection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST DisconnectPending; + PREQUEST DisassociatePending; + PREQUEST ClosePending; + + + Device = Connection->Device; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + +#if DBG + // + // Make sure if our reference count is zero, all the + // sub-reference counts are also zero. + // + + if (Connection->ReferenceCount == 0) { + + UINT i; + for (i = 0; i < CREF_TOTAL; i++) { + if (Connection->RefTypes[i] != 0) { + DbgPrint ("NBI: Connection reftype mismatch on %lx\n", Connection); + DbgBreakPoint(); + } + } + } +#endif + + // + // If the connection was assigned an ID, then remove it + // (it is assigned one when it leaves INACTIVE). + // + + if (Connection->LocalConnectionId != 0xffff) { + NbiDeassignConnectionId (Device, Connection); + } + + // + // Complete any pending disconnects. + // + + if (Connection->DisconnectRequest != NULL) { + + DisconnectPending = Connection->DisconnectRequest; + Connection->DisconnectRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_STATUS(DisconnectPending) = STATUS_SUCCESS; + NbiCompleteRequest (DisconnectPending); + NbiFreeRequest (Device, DisconnectPending); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // This should have been completed by NbiStopConnection, + // or else not allowed to be queued. + // + + CTEAssert (Connection->DisconnectWaitRequest == NULL); + + + Connection->State = CONNECTION_STATE_INACTIVE; + + // + // BUGBUG: Make NbiInitializeConnection() to take care of all this. + // + + RtlZeroMemory (&Connection->ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + Connection->TickCount = 1; + Connection->HopCount = 1; + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + + Connection->ConnectionInfo.TransmittedTsdus = 0; + Connection->ConnectionInfo.TransmissionErrors = 0; + Connection->ConnectionInfo.ReceivedTsdus = 0; + Connection->ConnectionInfo.ReceiveErrors = 0; + + // + // See if we need to do a disassociate now. + // + + if ((Connection->ReferenceCount == 0) && + (Connection->DisassociatePending != NULL)) { + + // + // A disassociate pended, now we complete it. + // + + DisassociatePending = Connection->DisassociatePending; + Connection->DisassociatePending = NULL; + + // + // Set this so nobody else tries to disassociate. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + if (DisassociatePending != (PVOID)-1) { + REQUEST_STATUS(DisassociatePending) = STATUS_SUCCESS; + NbiCompleteRequest (DisassociatePending); + NbiFreeRequest (Device, DisassociatePending); + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + + // + // If a close was pending, complete that. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Connection->ReferenceCount == 0) && + (Connection->ClosePending)) { + + ClosePending = Connection->ClosePending; + Connection->ClosePending = NULL; + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Even if the ref count is zero and we just cleaned up everything, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + if (ExInterlockedAddUlong ( &Connection->ThreadsInHandleConnectionZero, (ULONG)-1, &Device->Lock.Lock) == 1) { + NbiDestroyConnection(Connection); + } + + REQUEST_STATUS(ClosePending) = STATUS_SUCCESS; + NbiCompleteRequest (ClosePending); + NbiFreeRequest (Device, ClosePending); + + } else { + + if ( Connection->ReferenceCount == 0 ) { + Connection->CanBeDestroyed = TRUE; + } + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + Connection->ThreadsInHandleConnectionZero--; + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiHandleConnectionZero */ + diff --git a/private/ntos/tdi/isn/nb/datagram.c b/private/ntos/tdi/isn/nb/datagram.c new file mode 100644 index 000000000..5c599206e --- /dev/null +++ b/private/ntos/tdi/isn/nb/datagram.c @@ -0,0 +1,1089 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + datagram.c + +Abstract: + + This module contains the code to handle datagram reception + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ) + +/*++ + +Routine Description: + + This routine handles datagram indications. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + + Broadcast - TRUE if the frame was a broadcast datagram. + +Return Value: + + None. + +--*/ + +{ + + PADDRESS Address; + NDIS_STATUS NdisStatus; + PUCHAR NetbiosName; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + PDEVICE Device = NbiDevice; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + ULONG DataOffset; + UINT BytesTransferred; + PNDIS_PACKET Packet; + CTELockHandle LockHandle; + + + // + // See if there is an address that might want this. + // + + if (Broadcast) { + NetbiosName = (PVOID)-1; + } else { + NetbiosName = (PUCHAR)Connectionless->Datagram.DestinationName; + if (Device->AddressCounts[NetbiosName[0]] == 0) { + return; + } + } + + DataOffset = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + +#if defined(_PNP_POWER) + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->CurMaxReceiveBufferSize)) { +#else + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->Bind.LineInfo.MaximumPacketSize)) { +#endif _PNP_POWER + + NB_DEBUG (DATAGRAM, ("Datagram length %d discarded\n", PacketSize)); + return; + } + + Address = NbiFindAddress (Device, NetbiosName); + + if (Address == NULL) { + return; + } + + // + // We need to cache the remote name if the packet came across the router. + // This allows this machine to get back to the RAS client which might + // have sent this datagram. We currently dont allow broadcasts to go out + // on the dial-in line. + // Dont cache some of the widely used group names, that would be too much + // to store in cache. + // + +#if 0 + if ( Connectionless->IpxHeader.TransportControl && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x0 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x01 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x1E ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { +#endif + if ( Connectionless->IpxHeader.TransportControl && + ( (Address->NetbiosAddress.NetbiosName[15] == 0x1c ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { + + PNETBIOS_CACHE CacheName; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + if ( FindInNetbiosCacheTable ( Device->NameCache, + Connectionless->Datagram.SourceName, + &CacheName ) != STATUS_SUCCESS ) { + + CacheName = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (CacheName ) { + RtlCopyMemory (CacheName->NetbiosName, Connectionless->Datagram.SourceName, 16); + CacheName->Unique = TRUE; + CacheName->ReferenceCount = 1; + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->NetworksAllocated = 1; + CacheName->NetworksUsed = 1; + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + NB_DEBUG2 (CACHE, ("Alloc new cache from Datagram %lx for <%.16s>\n", + CacheName, CacheName->NetbiosName)); + + CacheName->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + CacheName); + + } + } else if ( CacheName->Unique ) { + // + // We already have an entry for this remote. We should update + // the address. This is so that if the ras client dials-out + // then dials-in again and gets a new address, we dont end up + // caching the old address. + // + if ( !RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12) ) { + + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + + } + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + // + // We need to allocate a packet and buffer for the transfer. + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + + + s = NbiPopReceiveBuffer (Device); + if (s == NULL) { + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveBuffer = CONTAINING_RECORD (s, NB_RECEIVE_BUFFER, PoolLinkage); + + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + ReceiveReserved->u.RR_DG.ReceiveBuffer = ReceiveBuffer; + + + // + // Now that we have a packet and a buffer, set up the transfer. + // The indication to the TDI clients will happen at receive + // complete time. + // + + NdisChainBufferAtFront (Packet, ReceiveBuffer->NdisBuffer); + ReceiveBuffer->Address = Address; + + ReceiveReserved->Type = RECEIVE_TYPE_DATAGRAM; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + TdiCopyLookaheadData( + &ReceiveBuffer->RemoteName, + Connectionless->Datagram.SourceName, + 16, + (MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + DataOffset, + PacketSize - DataOffset, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == PacketSize - DataOffset); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessDatagram */ + + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine indicates a datagram to clients on the specified + address. It is called from NbiReceiveComplete. + +Arguments: + + Address - The address the datagram was sent to. + + RemoteName - The source netbios address of the datagram. + + Data - The data. + + DataLength - The length of the data. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p, q; + PIRP Irp; + ULONG IndicateBytesCopied; + PREQUEST Request; + TA_NETBIOS_ADDRESS SourceName; + PTDI_CONNECTION_INFORMATION RemoteInformation; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // Update our statistics. + // + + ++Device->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + DataLength); + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress (RemoteName, FALSE, &SourceName); + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = AddressFile->ReceiveDatagramQueue.Flink; + q != &AddressFile->ReceiveDatagramQueue; + q = q->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (q); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbiParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + RemoteName, + DatagramAddress->NetbiosName, + 16))) { + continue; + } + break; + } + + if (q != &AddressFile->ReceiveDatagramQueue) { + + RemoveEntryList (q); + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // Do this deref now, we hold another one so it + // will stick around. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IndicateBytesCopied = 0; + + // + // Fall past the else to copy the data. + // + + } else { + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No receive datagram requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE_DATAGRAM]) { + + IndicateBytesCopied = 0; + + if ((*AddressFile->ReceiveDatagramHandler)( + AddressFile->HandlerContexts[TDI_EVENT_RECEIVE_DATAGRAM], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + DataLength, // indicated + DataLength, // available + &IndicateBytesCopied, + Data, + &Irp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + Request = NbiAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + } else { + + // + // The client has nothing posted and no handler, + // go on to the next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + } + + // + // We have a request; copy the actual user data. + // + if ( REQUEST_NDIS_BUFFER (Request) ) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl ( + Data, + IndicateBytesCopied, + DataLength - IndicateBytesCopied, + REQUEST_NDIS_BUFFER (Request), + 0, + &REQUEST_INFORMATION (Request)); + + } else { + // + // No buffer specified in the request + // + REQUEST_INFORMATION (Request) = 0; + // + // If there was any data to be copied, return error o/w success + // + REQUEST_STATUS(Request) = ( (DataLength - IndicateBytesCopied) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ); + } + + // + // Copy the addressing information. + // + + RemoteInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReturnDatagramInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + +} /* NbiIndicateDatagram */ + + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends a datagram on an address. + +Arguments: + + Device - The netbios device. + + Request - The request describing the datagram send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS_FILE AddressFile; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle; + NTSTATUS Status; + + // + // Make sure that the address is valid. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status == STATUS_SUCCESS) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->SendDatagramInformation->RemoteAddress), TRUE); + + + // + // Check that datagram size is less than the maximum allowable + // by the adapters. In the worst case this would be + // 576 - 64 = 512. + // + +#if defined(_PNP_POWER) + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumSendSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumSendSize )); + return STATUS_INVALID_PARAMETER; + } +#else + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumPacketSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumPacketSize )); + return STATUS_INVALID_PARAMETER; + } +#endif _PNP_POWER + + if (RemoteName != NULL) { + + // + // Get a packet to use in this send. + // + + s = NbiPopSendPacket (Device, FALSE); + + if (s != NULL) { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Check on the cache status of this name. + // + + Reserved->u.SR_DG.DatagramRequest = Request; + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.RemoteName = RemoteName; + + REQUEST_INFORMATION (Request) = Parameters->SendLength; + + ++Device->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Parameters->SendLength); + + if (Device->Internet) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + (RemoteName == (PVOID)-1) ? NULL : (PUCHAR)RemoteName->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this datagram + // request and processing will be resumed when + // we get a response. + // + + NB_DEBUG2 (CONNECTION, ("Queueing up datagram %lx on %lx\n", + Request, AddressFile)); + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + InsertTailList( + &Device->WaitingDatagrams, + &Reserved->WaitLinkage); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (CONNECTION, ("Found datagram cached %lx on %lx\n", + Request, AddressFile)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + ++CacheName->ReferenceCount; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } else { + + // + // Only this failure gets passed back up to + // the caller, to avoid confusing the browser. + // + + if (Status != STATUS_DEVICE_DOES_NOT_EXIST) { + + Status = STATUS_SUCCESS; + + } else { + + REQUEST_INFORMATION (Request) = 0; + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + + + + } + + } else { + + // + // We are not in internet mode, so we do not + // need to do the name discovery. + // + + NB_DEBUG2 (CONNECTION, ("Sending datagram direct %lx on %lx\n", + Request, AddressFile)); + + Reserved->u.SR_DG.Cache = NULL; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } + + } else { + + // + // Could not allocate a packet for the datagram. + // + + NB_DEBUG (DATAGRAM, ("Couldn't get packet to send DG %lx\n", Request)); + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + + } else { + + // + // There is no netbios remote address specified. + // + + NB_DEBUG (DATAGRAM, ("No netbios address in DG %lx\n", Request)); + Status = STATUS_BAD_NETWORK_PATH; + + } + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } else { + + NB_DEBUG (DATAGRAM, ("Invalid address file for DG %lx\n", Request)); + + } + + return Status; + +} /* NbiTdiSendDatagram */ + + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine sends a datagram to the next net in the + cache entry for the remote name. + +Arguments: + + Reserved - The reserved section of the packet that has + been allocated for this send. Reserved->u.SR_DG.Cache + will be NULL if Internet mode is off, otherwise it + will contain the cache entry to use when sending + this datagram. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_PACKET Packet; + PNETBIOS_CACHE CacheName; + NB_CONNECTIONLESS UNALIGNED * Header; + ULONG HeaderLength; + ULONG PacketLength; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DATAGRAM; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + if (CacheName == NULL) { + +#if defined(_PNP_POWER) + // + // IPX will send this on all the Nics. + // + TempLocalTarget.NicHandle.NicId = (USHORT)ITERATIVE_NIC_ID; +#else + TempLocalTarget.NicId = 1; +#endif _PNP_POWER + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + + } else { + + if (CacheName->Unique) { + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + } else { + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + } + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + } + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), HeaderLength); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiTransmitDatagram */ + + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Request - Describes this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + CTELockHandle CancelLH; + + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status != STATUS_SUCCESS) { + return Status; + } + + Address = AddressFile->Address; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_INVALID_HANDLE; + } + + + if (Request->Cancel) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_CANCELLED; + } + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, NbiCancelReceiveDatagram); + + NB_DEBUG2 (DATAGRAM, ("RDG posted on %lx\n", AddressFile)); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_RCV_DGRAM); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + return STATUS_PENDING; + +} /* NbiTdiReceiveDatagram */ + + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + NB_DEBUG (DATAGRAM, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest((PDEVICE)DeviceObject, Request); + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* NbiCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/isn/nb/device.c b/private/ntos/tdi/isn/nb/device.c new file mode 100644 index 000000000..d1a9af781 --- /dev/null +++ b/private/ntos/tdi/isn/nb/device.c @@ -0,0 +1,461 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiCreateDevice) +#endif + + +VOID +NbiRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Device->ReferenceCount); + +} /* NbiRefDevice */ + + +VOID +NbiDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + NbiDestroyDevice (Device); + } + +} /* NbiDerefDevice */ + + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + NB_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject; + + NB_DEBUG2 (DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer, Device)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(Device+1); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "NDC1", 4); + RtlCopyMemory(Device->Signature2, "NDC2", 4); +#endif + + // + // BETABUGBUG: Clean this up a bit. + // + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 65535; + Device->Information.MaxConnectionUserData = 0; + Device->Information.MaxDatagramSize = 500; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTION_MODE | TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_MULTICAST_SUPPORTED | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_MESSAGE_MODE; + Device->Information.MinimumLookaheadData = 128; + Device->Information.MaximumLookaheadData = 1500; + Device->Information.NumberOfResources = 0; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + Device->Statistics.MaximumSendWindow = 4; + Device->Statistics.AverageSendWindow = 4; + + // + // Set this so we won't ignore the broadcast name. + // + + Device->AddressCounts['*'] = 1; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock.Lock); + CTEInitLock (&Device->Lock.Lock); + + CTEInitTimer (&Device->FindNameTimer); + + Device->ControlChannelIdentifier = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); + + InitializeListHead (&Device->AddressDatabase); +#if defined(_PNP_POWER) + InitializeListHead (&Device->AdapterAddressDatabase); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingFindNames); + + InitializeListHead (&Device->WaitingConnects); + InitializeListHead (&Device->WaitingDatagrams); + + InitializeListHead (&Device->WaitingAdapterStatus); + InitializeListHead (&Device->ActiveAdapterStatus); + + InitializeListHead (&Device->WaitingNetbiosFindName); + + InitializeListHead (&Device->ReceiveDatagrams); + InitializeListHead (&Device->ConnectIndicationInProgress); + + InitializeListHead (&Device->ListenQueue); + + InitializeListHead (&Device->ReceiveCompletionQueue); + + InitializeListHead (&Device->WaitPacketConnections); + InitializeListHead (&Device->PacketizeConnections); + InitializeListHead (&Device->DataAckConnections); + + Device->MemoryUsage = 0; + + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + InitializeListHead (&Device->ReceiveBufferPoolList); + + ExInitializeSListHead( &Device->SendPacketList ); + ExInitializeSListHead( &Device->ReceivePacketList ); + Device->ReceiveBufferList.Next = NULL; + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + Device->ConnectionHash[i].Connections = NULL; + Device->ConnectionHash[i].ConnectionCount = 0; + Device->ConnectionHash[i].NextConnectionId = 1; + } + + KeQuerySystemTime (&Device->NbiStartTime); + + Device->State = DEVICE_STATE_CLOSED; + + Device->Type = NB_DEVICE_SIGNATURE; + Device->Size - sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* NbiCreateDevice */ + + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PNB_SEND_POOL SendPool; + PNB_SEND_PACKET SendPacket; + UINT SendPoolSize; + PNB_RECEIVE_POOL ReceivePool; + PNB_RECEIVE_PACKET ReceivePacket; + UINT ReceivePoolSize; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + ULONG HeaderLength; + UINT i; + + NB_DEBUG2 (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the connectionless packets out of its pools. + // + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, NB_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + NbiDeinitializeSendPacket (Device, SendPacket, HeaderLength); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", SendPool)); + +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(SendPool->PoolHandle); +#endif + + NbiFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, NB_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + NbiDeinitializeReceivePacket (Device, ReceivePacket); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", ReceivePool)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(ReceivePool->PoolHandle); +#endif + NbiFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } + +#if defined(_PNP_POWER) + NbiDestroyReceiveBufferPools( Device ); + + // + // Destroy adapter address list. + // + while(!IsListEmpty( &Device->AdapterAddressDatabase ) ){ + PADAPTER_ADDRESS AdapterAddress; + AdapterAddress = CONTAINING_RECORD( Device->AdapterAddressDatabase.Flink, ADAPTER_ADDRESS, Linkage ); + NbiDestroyAdapterAddress( AdapterAddress, NULL ); + } +#else + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (Device->Bind.LineInfo.MaximumPacketSize * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Device->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } +#endif _PNP_POWER + + NB_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); + +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (NbiMemoryTag[i].BytesAllocated != 0) { + NB_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, NbiMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + } + +} /* NbiDestroyDevice */ + diff --git a/private/ntos/tdi/isn/nb/dirs b/private/ntos/tdi/isn/nb/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/nb/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/nb/driver.c b/private/ntos/tdi/isn/nb/driver.c new file mode 100644 index 000000000..a22e48a53 --- /dev/null +++ b/private/ntos/tdi/isn/nb/driver.c @@ -0,0 +1,1794 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include +#include +#include + + +PDEVICE NbiDevice = NULL; +DEFINE_LOCK_STRUCTURE(NbiGlobalPoolInterlock); + +#ifdef RSRC_TIMEOUT_DBG + +ULONG NbiGlobalDebugResTimeout = 1; +LARGE_INTEGER NbiGlobalMaxResTimeout; + // the packet is allocated from ndis pool. +NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +UCHAR NbiGlobalDeathPacketHeader[100]; + +VOID +NbiInitDeathPacket() +{ + + NDIS_HANDLE PoolHandle; // poolhandle for sendpacket below when + NTSTATUS Status; + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &PoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + } else { + + if (NbiInitializeSendPacket( + NbiDevice, + PoolHandle, + &NbiGlobalDeathPacket, + NbiGlobalDeathPacketHeader, + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) != STATUS_SUCCESS) { + + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(PoolHandle); + } + } + +} +#endif //RSRC_TIMEOUT_DBG + +#if DBG + +ULONG NbiDebug = 0xffffffff; +ULONG NbiDebug2 = 0x00000000; +ULONG NbiMemoryDebug = 0x0002482c; + +UCHAR NbiTempDebugBuffer[150]; +UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +PUCHAR NbiDebugMemoryLoc = NbiDebugMemory[0]; +PUCHAR NbiDebugMemoryEnd = NbiDebugMemory[NB_MEMORY_LOG_SIZE]; +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (NbiTempDebugBuffer, 150); + ArgLen = vsprintf(NbiTempDebugBuffer,FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (NbiDebugMemoryLoc, 64); + RtlCopyMemory( NbiDebugMemoryLoc, NbiTempDebugBuffer, ArgLen ); + + NbiDebugMemoryLoc += 64; + if (NbiDebugMemoryLoc >= NbiDebugMemoryEnd) { + NbiDebugMemoryLoc = NbiDebugMemory[0]; + } + } + +} /* NbiDebugMemoryLog */ + + +DEFINE_LOCK_STRUCTURE(NbiMemoryInterlock); +MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// +DEFINE_LOCK_STRUCTURE(NbiGlobalInterlock); + + +#ifdef RASAUTODIAL +VOID +NbiAcdBind(); + +VOID +NbiAcdUnbind(); +#endif + +#ifdef NB_PACKET_LOG + +ULONG NbiPacketLogDebug = NB_PACKET_LOG_RCV_OTHER | NB_PACKET_LOG_SEND_OTHER; +USHORT NbiPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(NbiPacketLogLock); +NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +PNB_PACKET_LOG_ENTRY NbiPacketLogLoc = NbiPacketLog; +PNB_PACKET_LOG_ENTRY NbiPacketLogEnd = &NbiPacketLog[NB_PACKET_LOG_LENGTH]; + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PNB_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&NbiPacketLogLock, &LockHandle); + + PacketLog = NbiPacketLogLoc; + + ++NbiPacketLogLoc; + if (NbiPacketLogLoc >= NbiPacketLogEnd) { + NbiPacketLogLoc = NbiPacketLog; + } + *(UNALIGNED ULONG *)NbiPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&NbiPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(NB_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, Length); + } else { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* NbiLogPacket */ + +#endif // NB_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiFreeResources ( + IN PVOID Adapter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + + +// +// These two are used in various places in the driver. +// + +#if defined(_PNP_POWER) +IPX_LOCAL_TARGET BroadcastTarget = { {ITERATIVE_NIC_ID}, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif _PNP_POWER + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +UCHAR NetbiosBroadcastName[16] = { '*', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +ULONG NbiFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the Netbios ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of Netbios's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("Netbios/IPX Transport"); + PDEVICE Device; + PIPX_HEADER IpxHeader; + CTELockHandle LockHandle; + + PCONFIG Config = NULL; + +#if 0 + DbgPrint ("NBI: FailLoad at %lx\n", &NbiFailLoad); + DbgBreakPoint(); + + if (NbiFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + NB_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&NbiGlobalInterlock); + CTEInitLock (&NbiMemoryInterlock); + { + UINT i; + for (i = 0; i < MEMORY_MAX; i++) { + NbiMemoryTag[i].Tag = i; + NbiMemoryTag[i].BytesAllocated = 0; + } + } +#endif +#ifdef NB_PACKET_LOG + CTEInitLock (&NbiPacketLogLock); +#endif + +#if defined(NB_OWN_PACKETS) + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + NB_DEBUG2 (DEVICE, ("ISN Netbios loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = NbiGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + PANIC (" Failed to initialize transport, ISN Netbios initialization failed.\n"); + return status; + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbiDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbiDispatchDeviceControl; + + DriverObject->DriverUnload = NbiUnload; + + + // + // Create the device object which exports our name. + // + + status = NbiCreateDevice (DriverObject, &Config->DeviceName, &Device); + + if (!NT_SUCCESS (status)) { + + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + NbiFreeConfiguration(Config); + return status; + } + + NbiDevice = Device; + + // + // Initialize the global pool interlock + // + CTEInitLock (&NbiGlobalPoolInterlock); + + + // + // Save the relevant configuration parameters. + // + + Device->AckDelayTime = (Config->Parameters[CONFIG_ACK_DELAY_TIME] / SHORT_TIMER_DELTA) + 1; + Device->AckWindow = Config->Parameters[CONFIG_ACK_WINDOW]; + Device->AckWindowThreshold = Config->Parameters[CONFIG_ACK_WINDOW_THRESHOLD]; + Device->EnablePiggyBackAck = Config->Parameters[CONFIG_ENABLE_PIGGYBACK_ACK]; + Device->Extensions = Config->Parameters[CONFIG_EXTENSIONS]; + Device->RcvWindowMax = Config->Parameters[CONFIG_RCV_WINDOW_MAX]; + Device->BroadcastCount = Config->Parameters[CONFIG_BROADCAST_COUNT]; + Device->BroadcastTimeout = Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500; + Device->ConnectionCount = Config->Parameters[CONFIG_CONNECTION_COUNT]; + Device->ConnectionTimeout = Config->Parameters[CONFIG_CONNECTION_TIMEOUT] * 500; + Device->InitPackets = Config->Parameters[CONFIG_INIT_PACKETS]; + Device->MaxPackets = Config->Parameters[CONFIG_MAX_PACKETS]; + Device->InitialRetransmissionTime = Config->Parameters[CONFIG_INIT_RETRANSMIT_TIME]; + Device->Internet = Config->Parameters[CONFIG_INTERNET]; + Device->KeepAliveCount = Config->Parameters[CONFIG_KEEP_ALIVE_COUNT]; + Device->KeepAliveTimeout = Config->Parameters[CONFIG_KEEP_ALIVE_TIMEOUT]; + Device->RetransmitMax = Config->Parameters[CONFIG_RETRANSMIT_MAX]; + Device->RouterMtu = Config->Parameters[CONFIG_ROUTER_MTU]; + + Device->FindNameTimeout = + ((Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500) + (FIND_NAME_GRANULARITY/2)) / + FIND_NAME_GRANULARITY; + + Device->MaxReceiveBuffers = 20; // BUGBUG: Make it configurable? + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + + NbiInitializeTimers (Device); +#endif _PNP_POWER + + // + // Now bind to IPX via the internal interface. + // + + status = NbiBind (Device, Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + +#ifdef RSRC_TIMEOUT_DBG + NbiInitDeathPacket(); + // NbiGlobalMaxResTimeout.QuadPart = 50; // 1*1000*10000; + NbiGlobalMaxResTimeout.QuadPart = 20*60*1000; + NbiGlobalMaxResTimeout.QuadPart *= 10000; +#endif // RSRC_TIMEOUT_DBG + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Create Hash Table to store netbios cache entries + // For server create a big table, for workstation a small one + // + + if ( MmIsThisAnNtAsSystem() ) { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_LARGE ); + } else { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_SMALL ); + } + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NB_FREE_LOCK(&Device->Lock, LockHandle); + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + + // + // Allocate our initial connectionless packet pool. + // + + NbiAllocateSendPool (Device); + + // + // Allocate our initial receive packet pool. + // + + NbiAllocateReceivePool (Device); + + // + // Allocate our initial receive buffer pool. + // + // +#if !defined(_PNP_POWER) + NbiAllocateReceiveBufferPool (Device); +#endif !_PNP_POWER + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == Device->State ) { + Device->State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#if !defined(_PNP_POWER) + // + // Start the timer system. + // + + NbiInitializeTimers (Device); +#endif !_PNP_POWER + + + // + // Fill in the default connnectionless header. + // + + IpxHeader = &Device->ConnectionlessHeader; + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = 0; + IpxHeader->PacketLength[1] = 0; + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = 0; + *(UNALIGNED ULONG *)(IpxHeader->DestinationNetwork) = 0; + RtlCopyMemory(IpxHeader->DestinationNode, BroadcastAddress, 6); + IpxHeader->DestinationSocket = NB_SOCKET; + IpxHeader->SourceSocket = NB_SOCKET; +#if !defined(_PNP_POWER) + RtlCopyMemory(IpxHeader->SourceNetwork, Device->Bind.Network, 4); + RtlCopyMemory(IpxHeader->SourceNode, Device->Bind.Node, 6); + + Device->State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + + NbiFreeConfiguration(Config); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbiAcdBind(); +#endif + + return STATUS_SUCCESS; + +} /* DriverEntry */ + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has Netbios open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbiAcdUnbind(); +#endif + + Device->State = DEVICE_STATE_STOPPING; + + // + // Free the cache of netbios names. + // + + DestroyNetbiosCacheTable( Device->NameCache ); + + // + // Cancel the long timer. + // + + if (CTEStopTimer (&Device->LongTimer)) { + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + + // + // Unbind from the IPX driver. + // + + NbiUnbind (Device); + + // + // This event will get set when the reference count + // drops to 0. + // + + KeInitializeEvent( + &Device->UnloadEvent, + NotificationEvent, + FALSE); + Device->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + NbiDereferenceDevice (Device, DREF_LOADED); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &Device->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} /* NbiUnload */ + + +VOID +NbiFreeResources ( + IN PVOID Adapter + ) +/*++ + +Routine Description: + + This routine is called by Netbios to clean up the data structures associated + with a given Device. When this routine exits, the Device + should be deleted as it no longer has any assocaited resources. + +Arguments: + + Device - Pointer to the Device we wish to clean up. + +Return Value: + + None. + +--*/ +{ +#if 0 + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; +#endif + + +#if 0 + // + // Clean up packet pool. + // + + while ( Device->PacketPool.Next != NULL ) { + s = PopEntryList( &Device->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbiDeallocateSendPacket (Device, packet); + } + + // + // Clean up receive packet pool + // + + while ( Device->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&Device->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbiDeallocateReceivePacket (Device, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( Device->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &Device->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbiDeallocateReceiveBuffer (Device, BufferTag); + } + +#endif + +} /* NbiFreeResources */ + + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPXNB device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST Request; + UINT i; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + +#if !defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenConnection (Device, Request); + break; + } + + } else { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier); + ++Device->ControlChannelIdentifier; + if (Device->ControlChannelIdentifier == 0) { + Device->ControlChannelIdentifier = 1; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONTROL_CHANNEL_FILE; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbiCloseAddressFile (Device, Request); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // We don't call VerifyConnection because the I/O + // system should only give us one close and the file + // object should be valid. This helps avoid a window + // where two threads call HandleConnectionZero at the + // same time. + // + + Status = NbiCloseConnection (Device, Request); + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbiStopAddressFile (AddressFile, AddressFile->Address); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection(Connection); + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + // + // This call releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_CONNECTION + NB_LOCK_HANDLE_ARG (LockHandle1)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + Status = STATUS_SUCCESS; + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchOpenClose */ + + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = NbiDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + + return Status; + +} /* NbiDeviceControl */ + + +NB_TDI_DISPATCH_ROUTINE NbiDispatchInternalTable[] = { + NbiTdiAssociateAddress, + NbiTdiDisassociateAddress, + NbiTdiConnect, + NbiTdiListen, + NbiTdiAccept, + NbiTdiDisconnect, + NbiTdiSend, + NbiTdiReceive, + NbiTdiSendDatagram, + NbiTdiReceiveDatagram, + NbiTdiSetEventHandler, + NbiTdiQueryInformation, + NbiTdiSetInformation, + NbiTdiAction + }; + + +NTSTATUS +NbiDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request; + UCHAR MinorFunction; + + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + + // + // Branch to the appropriate request handler. + // + + MinorFunction = REQUEST_MINOR_FUNCTION(Request) - 1; + + if (MinorFunction <= (TDI_ACTION-1)) { + + Status = (*NbiDispatchInternalTable[MinorFunction]) ( + Device, + Request); + + } else { + + NB_DEBUG (DRIVER, ("Unsupported minor code %d\n", MinorFunction+1)); + if ((MinorFunction+1) == TDI_DISCONNECT) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchInternal */ + + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = NbiDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + NbiPrint1 ("Nbi: Could not allocate %d: limit\n", BytesNeeded); + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' IBN'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + NbiPrint1("Nbi: Could not allocate %d: no pool\n", BytesNeeded); + + if (ChargeDevice) { + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; + +} /* NbipAllocateMemory */ + + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* NbipFreeMemory */ + +#if DBG + + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = NbipAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &NbiMemoryInterlock); + } + + return Memory; + +} /* NbipAllocateTaggedMemory */ + + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &NbiMemoryInterlock); + + NbipFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* NbipFreeTaggedMemory */ + +#endif + + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + INT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteResourceErrorLog */ + + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[8] = L"NwlnkNb"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (Device->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteGeneralErrorLog */ + + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + Device - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteOidErrorLog */ + diff --git a/private/ntos/tdi/isn/nb/event.c b/private/ntos/tdi/isn/nb/event.c new file mode 100644 index 000000000..f6cff7105 --- /dev/null +++ b/private/ntos/tdi/isn/nb/event.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +PVOID TdiDefaultHandlers[6] = { + TdiDefaultConnectHandler, + TdiDefaultDisconnectHandler, + TdiDefaultErrorHandler, + TdiDefaultReceiveHandler, + TdiDefaultRcvDatagramHandler, + TdiDefaultRcvExpeditedHandler + }; + + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Device - The netbios device object. + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + UINT EventType; + + UNREFERENCED_PARAMETER (Device); + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + EventType = (UINT)(Parameters->EventType); + + if (Parameters->EventType > TDI_EVENT_RECEIVE_EXPEDITED) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + if (Parameters->EventHandler == NULL) { + AddressFile->RegisteredHandler[EventType] = FALSE; + AddressFile->Handlers[EventType] = TdiDefaultHandlers[EventType]; + AddressFile->HandlerContexts[EventType] = NULL; + } else { + AddressFile->Handlers[EventType] = Parameters->EventHandler; + AddressFile->HandlerContexts[EventType] = Parameters->EventContext; + AddressFile->RegisteredHandler[EventType] = TRUE; + } + + } + + NB_FREE_LOCK (&AddressFile->Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + return Status; + +} /* NbiTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isn/nb/frame.c b/private/ntos/tdi/isn/nb/frame.c new file mode 100644 index 000000000..49846a177 --- /dev/null +++ b/private/ntos/tdi/isn/nb/frame.c @@ -0,0 +1,1096 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + frame.c + +Abstract: + + This module contains code which creates and sends various + types of frames. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if defined(_PNP_POWER) + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + ReqFrame - If specified, the request frame for which this + response is being sent. The reqframe contains the + destination ipx address and the netbios name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(ReqFrame)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)ReqFrame->IpxHeader.SourceNetwork, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : ReqFrame->NameFrame.Name, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + LocalTarget = &BroadcastTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#else + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + DestAddress - If specified, the destination IPX address to + use for the send (if not, it will be broadcast on net 0). + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.CurrentNicId = 1; + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } else { + Reserved->u.SR_NF.CurrentNicId = 0; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(DestAddress)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)DestAddress, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : NetbiosBroadcastName, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + TempLocalTarget.NicId = 1; // BUGBUG: What if 1 isn't valid? + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#endif _PNP_POWER + + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + sizeof(NB_SESSION_INIT)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Device->Extensions) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = 0xffff; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = sizeof(NB_SESSION_INIT); + Header->Session.Offset = 0; + Header->Session.DataLength = sizeof(NB_SESSION_INIT); + Header->Session.ReceiveSequence = 0; + if (Device->Extensions) { + Header->Session.ReceiveSequenceMax = 1; // low estimate for the moment + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + sizeof(NB_SESSION_INIT), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitialize */ + + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize ack + frame for the specified connection. If extra data was + specified in the session initialize frame it is echoed + back to the remote. + +Arguments: + + Connection - The connection on which the frame is sent. + + ExtraData - Any extra data (after the SESSION_INIT buffer) + in the frame. + + ExtraDataLength - THe length of the extra data. + + LockHandle - If specified, indicates the connection lock + is held and should be released. This is for cases + where the ExtraData is in memory which may be freed + once the connection lock is released. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + ULONG SessionInitBufferLength; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitBufferLength = sizeof(NB_SESSION_INIT) + ExtraDataLength; + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + // + // Save the extra data, now we can free the lock. + // + + if (ExtraDataLength != 0) { + RtlCopyMemory (SessionInitMemory+1, ExtraData, ExtraDataLength); + } + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + SessionInitBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Connection->NewNetbios) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; + } + CTEAssert (Connection->CurrentSend.SendSequence == 0); + CTEAssert (Connection->ReceiveSequence == 1); + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = (USHORT)SessionInitBufferLength; + Header->Session.Offset = 0; + Header->Session.DataLength = (USHORT)SessionInitBufferLength; + Header->Session.ReceiveSequence = 1; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + SessionInitBufferLength, + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitAck */ + + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ) + +/*++ + +Routine Description: + + This routine allocates and sends a data ack frame. + + THIS ROUTINE IS CALLED WITH THE LOCK HANDLE HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection on which the frame is sent. + + AckType - Indicates if this is a query to the remote, + a response to a received probe, or a request to resend. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, try for the connection + // packet. If that's not available, that's OK since data + // acks are connectionless anyway. + // + + if (s == NULL) { + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + } else { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.PacketLength = sizeof(NB_CONNECTION); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + switch (AckType) { + case NbiAckQuery: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_SEND_ACK; break; + case NbiAckResponse: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; break; + case NbiAckResend: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_RESEND; break; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentSend.MessageOffset; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + NbiReferenceConnectionSync(Connection, CREF_FRAME); + + // + // Set this so we will accept a probe from a remote without + // the send ack bit on. However if we receive such a request + // we turn this flag off until we get something else from the + // remote. + // + + Connection->IgnoreNextDosProbe = FALSE; + + Connection->ReceivesWithoutAck = 0; + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + +} /* NbiSendDataAck */ + + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. We don't advance the + // send pointer, since it is the last frame of the session + // and we want it to stay the same in the case of resends. + // + + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + Header->Session.DataStreamType = NB_CMD_SESSION_END; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + NbiReferenceConnection (Connection, CREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEnd */ + + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame. Generally it is sent on a connection but we + are not tied to that, to allow us to respond to + session ends from unknown remotes. + +Arguments: + + RemoteAddress - The remote IPX address. + + LocalTarget - The local target of the remote. + + SessionEnd - The received session end frame. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = NULL; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, (PVOID)RemoteAddress, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->Session.ConnectionControlFlag = 0x00; + Header->Session.DataStreamType = NB_CMD_SESSION_END_ACK; + Header->Session.SourceConnectionId = SessionEnd->DestConnectionId; + Header->Session.DestConnectionId = SessionEnd->SourceConnectionId; + Header->Session.SendSequence = SessionEnd->ReceiveSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + if (SessionEnd->BytesReceived != 0) { // BUGBUG: Will this detect new netbios? + Header->Session.ReceiveSequence = SessionEnd->SendSequence + 1; + Header->Session.ReceiveSequenceMax = SessionEnd->SendSequence + 3; + } else { + Header->Session.ReceiveSequence = SessionEnd->SendSequence; + Header->Session.BytesReceived = 0; + } + + NbiReferenceDevice (Device, DREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEndAck */ + diff --git a/private/ntos/tdi/isn/nb/isnnb.h b/private/ntos/tdi/isn/nb/isnnb.h new file mode 100644 index 000000000..2d142e346 --- /dev/null +++ b/private/ntos/tdi/isn/nb/isnnb.h @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnnb.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#define NB_MAXIMUM_MAC 40 + +#define NB_SOCKET 0x5504 + +#if defined(_PNP_POWER) +#define NB_NETBIOS_NAME_SIZE 16 + +#define LOCK_ACQUIRED TRUE +#define LOCK_NOT_ACQUIRED FALSE +#endif _PNP_POWER + +// +// Defined granularity of find name timeouts in milliseconds -- +// we make this the same as the spec'ed RIP gap to avoid +// flooding routers. +// + +#define FIND_NAME_GRANULARITY 55 + + +// +// Defines the number of milliseconds between expirations of the +// short and long timers. +// + +#define MILLISECONDS 10000 // number of NT time units in one + +#define SHORT_TIMER_DELTA 100 +#define LONG_TIMER_DELTA 2000 + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#include + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of the Netbios header for name frames. +// + +typedef struct _NB_NAME_FRAME { + union { + struct { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + }; + UCHAR RoutingInfo[32]; + }; + UCHAR NameTypeFlag; + UCHAR DataStreamType2; + UCHAR Name[16]; +} NB_NAME_FRAME, *PNB_NAME_FRAME; + +// +// Definition of the Netbios header for directed datagrams. +// + +typedef struct _NB_DATAGRAM { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR SourceName[16]; + UCHAR DestinationName[16]; +} NB_DATAGRAM, *PNB_DATAGRAM; + +// +// Definition of the Netbios header for a status query. +// + +typedef struct _NB_STATUS_QUERY { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR Padding[14]; +} NB_STATUS_QUERY, *PNB_STATUS_QUERY; + +// +// Definition of the Netbios header for a status response +// (this does not include the status buffer itself). +// + +typedef struct _NB_STATUS_RESPONSE { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; +} NB_STATUS_RESPONSE, *PNB_STATUS_RESPONSE; + + +// +// Definition of the general Netbios connectionless header. +// + +typedef struct _NB_CONNECTIONLESS { + IPX_HEADER IpxHeader; + union { + NB_NAME_FRAME NameFrame; + NB_DATAGRAM Datagram; + NB_STATUS_QUERY StatusQuery; + NB_STATUS_RESPONSE StatusResponse; + }; +} NB_CONNECTIONLESS, *PNB_CONNECTIONLESS; + + +// +// Definition of the Netbios session frame. +// + +typedef struct _NB_SESSION { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT SourceConnectionId; + USHORT DestConnectionId; + USHORT SendSequence; + USHORT TotalDataLength; + USHORT Offset; + USHORT DataLength; + USHORT ReceiveSequence; + union { + USHORT BytesReceived; + USHORT ReceiveSequenceMax; + }; +} NB_SESSION, *PNB_SESSION; + + +// +// Definition of the extra fields in a Netbios +// session frame for session init and session init +// ack. +// + +typedef struct _NB_SESSION_INIT { + UCHAR SourceName[16]; + UCHAR DestinationName[16]; + USHORT MaximumDataSize; + USHORT MaximumPacketTime; + USHORT StartTripTime; +} NB_SESSION_INIT, *PNB_SESSION_INIT; + + +// +// Definition of the general Netbios connection-oriented header. +// + +typedef struct _NB_CONNECTION { + IPX_HEADER IpxHeader; + NB_SESSION Session; +} NB_CONNECTION, *PNB_CONNECTION; + + +// +// Definition of a Netbios packet. +// + +typedef union _NB_FRAME { + NB_CONNECTIONLESS Connectionless; + NB_CONNECTION Connection; +} NB_FRAME, *PNB_FRAME; + +#include + + +// +// Definitions for the DataStreamType field, with the +// format used shown in the comment afterward. +// + +#define NB_CMD_FIND_NAME 0x01 // NAME_FRAME +#define NB_CMD_NAME_RECOGNIZED 0x02 // NAME_FRAME +#define NB_CMD_ADD_NAME 0x03 // NAME_FRAME +#define NB_CMD_NAME_IN_USE 0x04 // NAME_FRAME +#define NB_CMD_DELETE_NAME 0x05 // NAME_FRAME +#define NB_CMD_SESSION_DATA 0x06 // SESSION +#define NB_CMD_SESSION_END 0x07 // SESSION +#define NB_CMD_SESSION_END_ACK 0x08 // SESSION +#define NB_CMD_STATUS_QUERY 0x09 // STATUS_QUERY +#define NB_CMD_STATUS_RESPONSE 0x0a // STATUS_RESPONSE +#define NB_CMD_DATAGRAM 0x0b // DATAGRAM +#define NB_CMD_BROADCAST_DATAGRAM 0x0c // BROADCAST_DATAGRAM + +#ifdef RSRC_TIMEOUT_DBG +#define NB_CMD_DEATH_PACKET 0x99 // +#endif // RSRC_TIMEOUT_DBG + +// +// Bit values in the NameTypeFlag of NB_NAME_FRAME frames. +// + +#define NB_NAME_UNIQUE 0x00 +#define NB_NAME_GROUP 0x80 +#define NB_NAME_USED 0x40 +#define NB_NAME_REGISTERED 0x04 +#define NB_NAME_DUPLICATED 0x02 +#define NB_NAME_DEREGISTERED 0x01 + +// +// Bit values in the ConnectionControlFlag. +// + +#define NB_CONTROL_SYSTEM 0x80 +#define NB_CONTROL_SEND_ACK 0x40 +#define NB_CONTROL_ATTENTION 0x20 +#define NB_CONTROL_EOM 0x10 +#define NB_CONTROL_RESEND 0x08 +#define NB_CONTROL_NEW_NB 0x01 + + + +#define NB_DEVICE_SIGNATURE 0x1401 +#if defined(_PNP_POWER) +#define NB_ADAPTER_ADDRESS_SIGNATURE 0x1403 +#endif _PNP_POWER +#define NB_ADDRESS_SIGNATURE 0x1404 +#define NB_ADDRESSFILE_SIGNATURE 0x1405 +#define NB_CONNECTION_SIGNATURE 0x1406 + + +// +// Useful in various places. +// +#if defined(_PNP_POWER) +extern IPX_LOCAL_TARGET BroadcastTarget; +#endif _PNP_POWER +extern UCHAR BroadcastAddress[6]; +extern UCHAR NetbiosBroadcastName[16]; + + +// +// Contains the default handler for each of the TDI event types +// that are supported. +// + +extern PVOID TdiDefaultHandlers[6]; + + +// +// Define a structure that can track lock acquire/release. +// + +typedef struct _NB_LOCK { + CTELock Lock; +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif +} NB_LOCK, *PNB_LOCK; + + + +#if DBG + +extern ULONG NbiDebug; +extern ULONG NbiDebug2; +extern ULONG NbiMemoryDebug; + +#define NB_MEMORY_LOG_SIZE 128 +extern UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +extern PUCHAR NbiDebugMemoryLoc; +extern PUCHAR NbiDebugMemoryEnd; + +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define NB_DEBUG(_Flag, _Print) { \ + if (NbiDebug & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#define NB_DEBUG2(_Flag, _Print) { \ + if (NbiDebug2 & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#else + +#define NB_DEBUG(_Flag, _Print) +#define NB_DEBUG2(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; +typedef struct _REQUEST_LIST_HEAD { + PREQUEST Head; // list is empty if this is NULL + PREQUEST Tail; // undefined if the list is empty. +} REQUEST_LIST_HEAD, *PREQUEST_LIST_HEAD; + + +// +// PREQUEST +// NbiAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define NbiAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// NbiFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define NbiFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// REQUEST_SINGLE_LINKAGE( +// IN PREQUEST Request +// ); +// +// Used to access a single list linkage field in the request. +// + +#define REQUEST_SINGLE_LINKAGE(_Request) \ + (*((PREQUEST *)&((_Request)->Tail.Overlay.ListEntry.Flink))) + + +// +// ULONG +// REQUEST_REFCOUNT( +// IN PREQUEST Request +// ); +// +// Used to access a field in the request which can be used for +// the reference count, as long as it is on a REQUEST_LIST. +// + +#define REQUEST_REFCOUNT(_Request) \ + (*((PULONG)&((_Request)->Tail.Overlay.ListEntry.Blink))) + + +// +// VOID +// REQUEST_LIST_INSERT_TAIL( +// IN PREQUEST_LIST_HEAD Head, +// IN PREQUEST Entry +// ); +// +// Inserts a request into a single list linkage queue. +// + +#define REQUEST_LIST_INSERT_TAIL(_Head,_Entry) { \ + if ((_Head)->Head == NULL) { \ + (_Head)->Head = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } else { \ + REQUEST_SINGLE_LINKAGE((_Head)->Tail) = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } \ +} + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// NbiCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define NbiCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +// +// some utility macros. + +// Minimum of two +// +#define NB_MIN( _a , _b ) ( ( (_a) < (_b) ) ? (_a) : (_b) ) + +// +// Swap the _s1 and _s2 of Type _T +// + +#define NB_SWAP(_s1, _s2, _T) { \ + _T _temp; \ + _temp = (_s1); \ + (_s1) = (_s2); \ + (_s2) = _temp; \ +} + +#define NB_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + +// +// Define our own spinlock routines. +// + +#if DBG + +#define NB_GET_LOCK(_Lock, _LockHandle) { \ + CTEGetLock(&(_Lock)->Lock, _LockHandle); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK(_Lock, _LockHandle) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + CTEFreeLock(&(_Lock)->Lock, _LockHandle); \ +} + +#define NB_GET_LOCK_DPC(_Lock) { \ + ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK_DPC(_Lock) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock); \ +} + +#else + +#define NB_GET_LOCK(_Lock, _LockHandle) CTEGetLock(&(_Lock)->Lock, _LockHandle) +#define NB_FREE_LOCK(_Lock, _LockHandle) CTEFreeLock(&(_Lock)->Lock, _LockHandle) +#define NB_GET_LOCK_DPC(_Lock) ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock) +#define NB_FREE_LOCK_DPC(_Lock) ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock) + +#endif + + +#define NB_GET_CANCEL_LOCK( _LockHandle ) IoAcquireCancelSpinLock( _LockHandle ) + +#define NB_FREE_CANCEL_LOCK( _LockHandle ) IoReleaseCancelSpinLock( _LockHandle ) + + +// +// Routines to optimize for a uni-processor environment. +// + + +#define NB_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define NB_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define NB_ADD_ULONG(_Pulong, _Ulong, _Lock) ExInterlockedAddUlong(_Pulong, _Ulong, &(_Lock)->Lock) + +#define NB_DEFINE_SYNC_CONTEXT(_SyncContext) +#define NB_BEGIN_SYNC(_SyncContext) +#define NB_END_SYNC(_SyncContext) + +#define NB_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; + +// +// BUGBUG: Make these be NB_XXX_LOCK_DPC calls -- then the definitions +// of the NB_SYNC_XXX_LOCK calls can be changed to not need _LockHandle +// and many of the functions won't need that as a parameter. +// + +#define NB_SYNC_GET_LOCK(_Lock, _LockHandle) NB_GET_LOCK(_Lock, _LockHandle) +#define NB_SYNC_FREE_LOCK(_Lock, _LockHandle) NB_FREE_LOCK(_Lock, _LockHandle) + +#define NB_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, &(_Lock)->Lock) +#define NB_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define NB_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, &(_Lock)->Lock) +#define NB_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntryList(_Queue, &(_Lock)->Lock) +#define NB_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntryList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_LOCK_HANDLE_PARAM(_LockHandle) , IN CTELockHandle _LockHandle +#define NB_LOCK_HANDLE_ARG(_LockHandle) , (_LockHandle) + +#define NB_SYNC_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + + +// +// This macro adds a ULONG to a LARGE_INTEGER (should be +// called with a spinlock held). +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define NB_DEBUG_DEVICE 0x00000001 +#define NB_DEBUG_ADDRESS 0x00000004 +#define NB_DEBUG_SEND 0x00000008 +#define NB_DEBUG_RECEIVE 0x00000020 +#define NB_DEBUG_CONFIG 0x00000040 +#define NB_DEBUG_PACKET 0x00000080 +#define NB_DEBUG_BIND 0x00000200 +#define NB_DEBUG_ADDRESS_FRAME 0x00000400 +#define NB_DEBUG_CONNECTION 0x00000800 +#define NB_DEBUG_QUERY 0x00001000 +#define NB_DEBUG_DRIVER 0x00002000 +#define NB_DEBUG_CACHE 0x00004000 +#define NB_DEBUG_DATAGRAM 0x00008000 +#define NB_DEBUG_TIMER 0x00010000 +#define NB_DEBUG_SEND_WINDOW 0x00020000 + + + +// +// NB_GET_NBHDR_BUFF - gets the nb header in the packet. It is always the +// second buffer. +// +#define NB_GET_NBHDR_BUFF(Packet) (NDIS_BUFFER_LINKAGE((Packet)->Private.Head)) + + diff --git a/private/ntos/tdi/isn/nb/mp/makefile b/private/ntos/tdi/isn/nb/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/nb/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/nb/mp/sources b/private/ntos/tdi/isn/nb/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/nb/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/nb/nbcount/makefile b/private/ntos/tdi/isn/nb/nbcount/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbcount/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/nb/nbcount/nbcount.c b/private/ntos/tdi/isn/nb/nbcount/nbcount.c new file mode 100644 index 000000000..74a656f16 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbcount/nbcount.c @@ -0,0 +1,177 @@ +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Compatible Source Routing Daemon for Windows NT +* +* Module: ipx/route/ipxroute.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "errno.h" +#include "tdi.h" +#include "isnkrnl.h" + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + +HANDLE isnnbfd; +wchar_t isnnbname[] = L"\\Device\\NwlnkNb"; +char pgmname[] = "NBCOUNT"; + +/** **/ + +#define INVALID_HANDLE (HANDLE)(-1) + +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen); + +/*page************************************************************* + m a i n + + This is the main routine that gets executed when a NET START + happens. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void _CRTAPI1 main(int argc, char **argv) +{ + UNICODE_STRING FileString; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + NB_ACTION_GET_COUNTS GetCounts; + int rc; + int i; + + /** Open the nwlnknb driver **/ + + RtlInitUnicodeString (&FileString, isnnbname); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnnbfd, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + isnnbfd = INVALID_HANDLE; + printf("Could not open transport\n"); + } + + if (isnnbfd == INVALID_HANDLE) { + exit(1); + } + + rc = do_isnnbioctl(isnnbfd, (I_MIPX | 351), (char *)&GetCounts, sizeof(NB_ACTION_GET_COUNTS)); + if (rc == 0) { + + printf("NB NIC count: %d\n", GetCounts.MaximumNicId); + for (i = 1; i <= GetCounts.MaximumNicId; i++) { + printf("NIC %d: %d sessions\n", i, GetCounts.NicIdCounts[i]); + } + } +} + + +/*page*************************************************************** + d o _ i s n i p x i o c t l + + Do the equivalent of a stream ioctl to isnnb + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen) +{ + NTSTATUS Status; + UCHAR buffer[300]; + PNWLINK_ACTION action; + IO_STATUS_BLOCK IoStatusBlock; + int rc; + + /** Fill out the structure **/ + + action = (PNWLINK_ACTION)buffer; + + action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; + action->OptionType = NWLINK_OPTION_CONTROL; + action->BufferLength = sizeof(ULONG) + dlen; + action->Option = cmd; + RtlMoveMemory(action->Data, datap, dlen); + + /** Issue the ioctl **/ + + Status = NtDeviceIoControlFile( + fd, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_TDI_ACTION, + NULL, + 0, + action, + FIELD_OFFSET(NWLINK_ACTION,Data) + dlen); + + if (Status != STATUS_SUCCESS) { + if (Status == STATUS_INVALID_PARAMETER) { + rc = ERANGE; + } else { + rc = EINVAL; + } + } else { + if (dlen > 0) { + RtlMoveMemory (datap, action->Data, dlen); + } + rc = 0; + } + + return rc; + +} + diff --git a/private/ntos/tdi/isn/nb/nbcount/nbcount.rc b/private/ntos/tdi/isn/nb/nbcount/nbcount.rc new file mode 100644 index 000000000..ada219b24 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbcount/nbcount.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLink Netbios Session Count Application" +#define VER_INTERNALNAME_STR "nbcount.exe" +#define VER_ORIGINALFILENAME_STR "nbcount.exe" + +#include "common.ver" diff --git a/private/ntos/tdi/isn/nb/nbcount/sources b/private/ntos/tdi/isn/nb/nbcount/sources new file mode 100644 index 000000000..f9dfe3561 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbcount/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc. + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=nbcount + +TARGETNAME=nbcount +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=UMAPPL_NOLIB + +USE_CRTDLL=1 + +C_DEFINES=$(C_DEFINES) + +!IF 0 +INCLUDES=..\h;..\..\..\..\..\inc;..\..\..\..\inc;..\..\..\inc +!ELSE +INCLUDES=..\h;$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;$(BASEDIR)\private\ntos\streams\inc +!ENDIF + +SOURCES= nbcount.c nbcount.rc + +UMTYPE=console +UMAPPL=$(TARGETNAME) +UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + diff --git a/private/ntos/tdi/isn/nb/nbiprocs.h b/private/ntos/tdi/isn/nb/nbiprocs.h new file mode 100644 index 000000000..64fd8f3a6 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbiprocs.h @@ -0,0 +1,1533 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbiprocs.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbiPrint0(fmt) DbgPrint(fmt) +#define NbiPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbiPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbiPrint0(fmt) +#define NbiPrint1(fmt,v0) +#define NbiPrint2(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define NB_PACKET_LOG 1 +#endif + +#ifdef NB_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _NB_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER NbiHeader; + UCHAR Data[14]; +} NB_PACKET_LOG_ENTRY, *PNB_PACKET_LOG_ENTRY; + +#define NB_PACKET_LOG_LENGTH 128 +extern ULONG NbiPacketLogDebug; +extern USHORT NbiPacketLogSocket; +EXTERNAL_LOCK(NbiPacketLogLock); +extern NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogLoc; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogEnd; + +// +// Bit fields in NbiPacketLogDebug +// + +#define NB_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to NbiPacketLogSocket +#define NB_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-NB) + +#define NB_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from NbiPacketLogSocket + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (NbiPacketLogDebug & (_Bit)) + +#else // NB_PACKET_LOG + +#define NbiLogPacket(_MacHeader,_Length,_NbiHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // NB_PACKET_LOG + + +#if DBG + +#define NbiReferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefDevice (_Device) + +#define NbiDereferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefDevice (_Device) + + +#define NbiReferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddress (_Address) + +#define NbiReferenceAddressLock(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressLock (_Address) + +#define NbiDereferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFile (_AddressFile) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFileLock (_AddressFile) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddressFile (_AddressFile) + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + + +#define NbiReferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnection (_Connection) + +#define NbiReferenceConnectionLock(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionLock (_Connection) + +#define NbiReferenceConnectionSync(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionSync (_Connection) + +#define NbiDereferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefConnection (_Connection) + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + +#else // DBG + +#define NbiReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define NbiDereferenceDevice(_Device, _Type) \ + NbiDerefDevice (_Device) + + + +#define NbiReferenceAddress(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiDereferenceAddress(_Address, _Type) \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + if ( !InterlockedDecrement(&(_AddressFile)->ReferenceCount )) { \ + NbiDestroyAddressFile (_AddressFile); \ + } + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + + +#define NbiReferenceConnection(_Connection, _Type) { \ + (VOID)ExInterlockedAddUlong( \ + &(_Connection)->ReferenceCount, \ + 1, \ + &(_Connection)->DeviceLock->Lock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionLock(_Connection, _Type) { \ + ++(_Connection)->ReferenceCount; \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionSync(_Connection, _Type) { \ + (VOID)NB_ADD_ULONG( \ + &(_Connection)->ReferenceCount, \ + 1, \ + (_Connection)->DeviceLock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiDereferenceConnection(_Connection, _Type) { \ + CTELockHandle _LockHandle; \ + NB_GET_LOCK( (_Connection)->DeviceLock, &_LockHandle ); \ + if ( !(--(_Connection)->ReferenceCount) ) { \ + (_Connection)->ThreadsInHandleConnectionZero++; \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + NbiHandleConnectionZero (_Connection); \ + } else { \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + } \ +} + + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// Definition of the callback routine where an NdisTransferData +// call is not needed. +// + +typedef VOID +(*NB_CALLBACK_NO_TRANSFER) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + + +// +// This routine compares two node addresses. +// + +#define NB_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define NB_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + + +// +// Definition of the routine to handler a particular minor +// code for an IOCTL_MJ_INTERNAL_DEVICE_CONTROL IRP. +// + +typedef NTSTATUS +(*NB_TDI_DISPATCH_ROUTINE) ( + IN PDEVICE Device, + IN PREQUEST Request + ); + + + +// +// Routines in action.c +// + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in address.c +// + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ); + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStartRegistration( + IN PADDRESS Address + ); + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ); + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +NbiRefAddress( + IN PADDRESS Address + ); + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +NbiDerefAddress( + IN PADDRESS Address + ); + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +#endif + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if !defined(_PNP_POWER) +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); +#endif !_PNP_POWER + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ); + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ); + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + +#if defined(_PNP_POWER) +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ); + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ); + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ); +#endif _PNP_POWER + + +// +// Routines in bind.c +// + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ); + +VOID +NbiUnbind( + IN PDEVICE Device + ); + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ); + +VOID +NbiLineDown( + IN USHORT NicId, + IN ULONG FwdAdapterCtx + ); + + +// +// Routines in cache.c +// + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +); + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ); + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ); + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ); + +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ); + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ); + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ); + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ); + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +// +// Routines in connect.c +// + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ); + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ); + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ); + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ); + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnectionSync( + IN PCONNECTION Connection + ); +#endif + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ); + + +// +// Routines in datagram.c +// + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ); + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ); + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ); + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in device.c +// + +VOID +NbiRefDevice( + IN PDEVICE Device + ); + +VOID +NbiDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ); + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + + +// +// Routines in event.c +// + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in frame.c +// + +VOID +NbiSendNameFrame( + IN PADDRESS Address, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, +#if defined(_PNP_POWER) + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL +#else + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL +#endif _PNP_POWER + ); + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ); + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ); + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ); + + +// +// Routines in packet.c +// + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ); + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ); + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ); + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ); + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ); + +#if defined(_PNP_POWER) +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ); + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ); + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ); + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); +#else +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ); +#endif _PNP_POWER + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ); + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ); + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ); + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ); + + +// +// Routines in query.c +// + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ); + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ); + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ); + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + + +// +// Routines in receive.c +// + + +BOOLEAN +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterCtx, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl + ); + +VOID +NbiReceiveComplete( + IN USHORT NicId + ); + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in send.c +// + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ); + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ); + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ); + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ); + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ); + + +// +// Routines in session.c +// + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + +// +// Routines in timer.c +// + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ); + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ); + +#else + +#define NbiStopRetransmit(_Connection) \ + (_Connection)->Retransmit = 0; + +#define NbiStopWatchdog(_Connection) \ + (_Connection)->Watchdog = 0; + +#endif + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ); + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ); + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ); + diff --git a/private/ntos/tdi/isn/nb/nbitypes.h b/private/ntos/tdi/isn/nb/nbitypes.h new file mode 100644 index 000000000..604df5fb5 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nbitypes.h @@ -0,0 +1,1511 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbitypes.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// For find name requests, defines the current status (SR_FN.Status). +// + +typedef enum { + FNStatusNoResponse, // no response has been received + FNStatusResponseUnique, // response received, is a unique name + FNStatusResponseGroup // response received, is a group name +}; + +// +// Defines the results we can get from sending a series of find +// names to locate a netbios name. +// + +typedef enum _NETBIOS_NAME_RESULT { + NetbiosNameFound, // name was located + NetbiosNameNotFoundNormal, // name not found, no response received + NetbiosNameNotFoundWanDown // name not found, all lines were down +} NETBIOS_NAME_RESULT, *PNETBIOS_NAME_RESULT; + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _NB_SEND_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN SendInProgress; // used in an NdisSend + UCHAR Type; // what to do on completion + BOOLEAN OwnedByConnection; // if this is a connection's one packet +#if defined(_PNP_POWER) + PVOID Reserved[SEND_RESERVED_COMMON_SIZE]; // used by ipx for even-padding and local target etc. +#else + PVOID Reserved[2]; // used by ipx for even-padding +#endif _PNP_POWER + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY WaitLinkage; // when waiting on other queues +#ifdef NB_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + union { + struct { + UCHAR NetbiosName[16]; // name being searched for + UCHAR StatusAndSentOnUpLine; // low nibble: look at FNStatusXXX enum + // high nibble: TRUE if while sending, found lan or up wan line + UCHAR RetryCount; // number of times sent + USHORT SendTime; // based on Device->FindNameTime +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // current nic id it is being sent on + USHORT MaximumNicId; // highest one it will be sent on +#endif !_PNP_POWER + struct _NETBIOS_CACHE * NewCache; // new cache entry for group names + } SR_FN; + struct { + struct _ADDRESS * Address; // that owns this packet, if one does + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // non-zero for frames that go to all +#endif !_PNP_POWER + UCHAR NameTypeFlag; // save these two values for frames + UCHAR DataStreamType; // that need to be sent to all nic id's + } SR_NF; + struct { + PREQUEST DatagramRequest; // holds the passed-in request + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; // will be -1 for broadcast + struct _ADDRESS_FILE * AddressFile; // that the datagram was sent on + struct _NETBIOS_CACHE * Cache; // how to route to the netbios address + ULONG CurrentNetwork; // within the cache entry + } SR_DG; + struct { + struct _CONNECTION * Connection; // that this frame was sent on. + PREQUEST Request; // that this frame was sent for. + ULONG PacketLength; // total packet length. + BOOLEAN NoNdisBuffer; // none allocate for send + } SR_CO; + struct { + ULONG ActualBufferLength; // real length of allocated buffer. + } SR_AS; + } u; + PUCHAR Header; // points to the MAC/IPX/NB header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header +} NB_SEND_RESERVED, *PNB_SEND_RESERVED; + +// +// Values for Type. +// + +#define SEND_TYPE_NAME_FRAME 1 +#define SEND_TYPE_SESSION_INIT 2 +#define SEND_TYPE_FIND_NAME 3 +#define SEND_TYPE_DATAGRAM 4 +#define SEND_TYPE_SESSION_NO_DATA 5 +#define SEND_TYPE_SESSION_DATA 6 +#define SEND_TYPE_STATUS_QUERY 7 +#define SEND_TYPE_STATUS_RESPONSE 8 + +#ifdef RSRC_TIMEOUT_DBG +#define SEND_TYPE_DEATH_PACKET 9 +#endif //RSRC_TIMEOUT_DBG + +// +// Macros to access StatusAndSentOnUpLine. +// + +#define NB_GET_SR_FN_STATUS(_Reserved) \ + ((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) + +#define NB_SET_SR_FN_STATUS(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) | (_Value)); + +#define NB_GET_SR_FN_SENT_ON_UP_LINE(_Reserved) \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) != 0) + +#define NB_SET_SR_FN_SENT_ON_UP_LINE(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) | ((_Value) << 4)); + + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _NB_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + UCHAR Type; // what to do on completion +#if defined(_PNP_POWER) + PVOID Pool; // send pool it was allocated from +#else + +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + +#endif _PNP_POWER + union { + struct { + struct _CONNECTION * Connection; // that the transfer is for + BOOLEAN EndOfMessage; // this was the last part of a message + BOOLEAN CompleteReceive; // receive should be completed + BOOLEAN NoNdisBuffer; // user's mdl chain was used + BOOLEAN PartialReceive; // (new nb) don't ack this packet + } RR_CO; + struct { + struct _NB_RECEIVE_BUFFER * ReceiveBuffer; // datagram receive buffer + } RR_DG; + struct { + PREQUEST Request; // for this request + } RR_AS; + } u; + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +} NB_RECEIVE_RESERVED, *PNB_RECEIVE_RESERVED; + +// +// Values for Type. +// + +#define RECEIVE_TYPE_DATAGRAM 1 +#define RECEIVE_TYPE_DATA 2 +#define RECEIVE_TYPE_ADAPTER_STATUS 3 + + + +typedef struct _NB_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#if defined(_PNP_POWER) + PVOID Pool; // receive buffer pool was allocated from +#else +#ifdef NB_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif +#endif _PNP_POWER + struct _ADDRESS * Address; // that the datagram is for + SINGLE_LIST_ENTRY PoolLinkage; // when in free pool + LIST_ENTRY WaitLinkage; // when in ReceiveDatagrams queue + PNDIS_BUFFER NdisBuffer; // describes the data + UCHAR RemoteName[16]; // datagram was received from + ULONG DataLength; // length for current one, not allocated + PUCHAR Data; // points to data to hold packet +} NB_RECEIVE_BUFFER, *PNB_RECEIVE_BUFFER; + + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +//#define NB_OWN_PACKETS 1 + +#ifdef NB_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _NB_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_SEND_RESERVED)]; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_RECEIVE_RESERVED)]; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_SendPacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiAllocateReceivePacket(_Device,_PoolHandle, _ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_ReceivePacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiFreeSendPacket(_Device,_Packet) + +#define NbiFreeReceivePacket(_Device,_Packet) + + +#else // NB_OWN_PACKETS + +typedef struct _NB_SEND_PACKET { + PNDIS_PACKET Packet; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + PNDIS_PACKET Packet; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_PACKET_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, _PoolHandle); \ +} + +#define NbiAllocateReceivePacket(_Device, _PoolHandle, _ReceivePacket,_Status) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, _PoolHandle); \ +} + +#define NbiFreeSendPacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#define NbiFreeReceivePacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#endif // NB_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PNB_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PNB_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + + +typedef struct _NB_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; +#if defined(_PNP_POWER) + UINT BufferDataSize; // allocation size of each buffer data +#endif _PNP_POWER + NB_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} NB_RECEIVE_BUFFER_POOL, *PNB_RECEIVE_BUFFER_POOL; + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_CACHE 4 +#define MEMORY_CONNECTION 5 +#define MEMORY_STATUS 6 +#define MEMORY_QUERY 7 +#if defined(_PNP_POWER) +#define MEMORY_WORK_ITEM 8 +#define MEMORY_ADAPTER_ADDRESS 9 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +#define MEMORY_MAX 10 +#else +#define MEMORY_MAX 8 +#endif _PNP_POWER + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(NbiMemoryInterlock); +extern MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif + + + +// +// This structure holds a single remote network which a +// Netbios name exists on. +// + +typedef struct _NETBIOS_NETWORK { + ULONG Network; + IPX_LOCAL_TARGET LocalTarget; +} NETBIOS_NETWORK, *PNETBIOS_NETWORK; + +// +// This defines a netbios cache entry for a given name. +// + +typedef struct _NETBIOS_CACHE { + UCHAR NetbiosName[16]; + BOOLEAN Unique; + BOOLEAN FailedOnDownWan; // if NetworksUsed == 0, was it due to down wan lines? + USHORT TimeStamp; // in seconds - CacheTimeStamp when inserted + ULONG ReferenceCount; + LIST_ENTRY Linkage; + TDI_ADDRESS_IPX FirstResponse; + USHORT NetworksAllocated; + USHORT NetworksUsed; + NETBIOS_NETWORK Networks[1]; // may be more than one of these +} NETBIOS_CACHE, *PNETBIOS_CACHE; + +typedef struct _NETBIOS_CACHE_TABLE { + USHORT MaxHashIndex; + USHORT CurrentEntries; + LIST_ENTRY Bucket[1]; +} NETBIOS_CACHE_TABLE, *PNETBIOS_CACHE_TABLE; + +#define NB_NETBIOS_CACHE_TABLE_LARGE 26 // for server +#define NB_NETBIOS_CACHE_TABLE_SMALL 8 // for workstation +#define NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET 8 + +// +// This defines the different kind of requests that can be made +// to CacheFindName(). +// + +typedef enum _FIND_NAME_TYPE { + FindNameConnect, + FindNameNetbiosFindName, + FindNameOther +} FIND_NAME_TYPE, *PFIND_NAME_TYPE; + + +// +// The number of hash entries in the non-inactive connection +// database. +// + +#define CONNECTION_HASH_COUNT 8 + +// +// Mask and shift to retrieve the hash number from a connection +// ID. +// + +#define CONNECTION_HASH_MASK 0xe000 +#define CONNECTION_HASH_SHIFT 13 + +// +// The maximum connection ID we can assign, not counting the +// shifted-over hash id (which occupies the top 3 bits of the +// real id we use on the wire). We can use all the bits except +// the top one, to prevent an ID of 0xffff being used. +// + +#define CONNECTION_MAXIMUM_ID (USHORT)(~CONNECTION_HASH_MASK & ~1) + +// +// A single connection hash bucket. +// + +typedef struct _CONNECTION_HASH { + struct _CONNECTION * Connections; + USHORT ConnectionCount; + USHORT NextConnectionId; +} CONNECTION_HASH, *PCONNECTION_HASH; + + +// +// These are queued in the ConnectIndicationInProgress +// queue to track indications to TDI clients. +// + +typedef struct _CONNECT_INDICATION { + LIST_ENTRY Linkage; + UCHAR NetbiosName[16]; + TDI_ADDRESS_IPX RemoteAddress; + USHORT ConnectionId; +} CONNECT_INDICATION, *PCONNECT_INDICATION; + +// +// This structure defines the per-device structure for NB +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_CONNECTION 4 +#define DREF_FN_TIMER 5 +#define DREF_FIND_NAME 6 +#define DREF_SESSION_INIT 7 +#define DREF_NAME_FRAME 8 +#define DREF_FRAME 9 +#define DREF_SHORT_TIMER 10 +#define DREF_LONG_TIMER 11 +#define DREF_STATUS_QUERY 12 +#define DREF_STATUS_RESPONSE 13 +#define DREF_STATUS_FRAME 14 +#define DREF_NB_FIND_NAME 15 + +#define DREF_TOTAL 16 + +typedef struct _DEVICE { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + NB_LOCK Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + NB_LOCK Lock; + LONG ReferenceCount; // activity count/this provider. + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; +#if defined(_PNP_POWER) + USHORT DeviceNameLength; +#else + ULONG DeviceNameLength; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + + // + // All send packet pools are chained on this list. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + LIST_ENTRY ReceiveBufferPoolList; + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + SINGLE_LIST_ENTRY ReceiveBufferList; + + // + // Receive requests waiting to be completed. + // + + LIST_ENTRY ReceiveCompletionQueue; + + // + // Connections waiting for send packets. + // + + LIST_ENTRY WaitPacketConnections; + + // + // Connections waiting to packetize. + // + + LIST_ENTRY PacketizeConnections; + + // + // Connections waiting to send a data ack. + // + + LIST_ENTRY DataAckConnections; + + // + // The list changed while we were processing it. + // + + BOOLEAN DataAckQueueChanged; + + // + // Information to manage the Netbios name cache. + // + + LIST_ENTRY WaitingConnects; // connect requests waiting for a name + LIST_ENTRY WaitingDatagrams; // datagram requests waiting for a name + LIST_ENTRY WaitingAdapterStatus; // adapter status requests waiting for a name + LIST_ENTRY WaitingNetbiosFindName; // netbios find name requests waiting for a name + + // + // Holds adapter status request which have a name and + // are waiting for a response. The long timeout aborts + // these after a couple of expirations (BUGBUG: currently we + // do not do resends). + // + + LIST_ENTRY ActiveAdapterStatus; + + // + // Receive datagrams waiting to be indicated. + // + + LIST_ENTRY ReceiveDatagrams; + + // + // In-progress connect indications (used to make + // sure we don't indicate the same packet twice). + // + + LIST_ENTRY ConnectIndicationInProgress; + + // + // Listens that have been posted to connections. + // + + LIST_ENTRY ListenQueue; + + UCHAR State; + + // + // The following fields control the timer system. + // The short timer is used for retransmission and + // delayed acks, and the long timer is used for + // watchdog timeouts. + // + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckActive; // DataAckConnections is not empty. + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. +#if defined(_PNP_POWER) + BOOLEAN LongTimerRunning; // True if the long timer is running. +#endif _PNP_POWER + LARGE_INTEGER ShortTimerStart; // tick count when the short timer was set. + CTETimer ShortTimer; // controls the short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + CTETimer LongTimer; // kernel DPC object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + NB_LOCK TimerLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of waiting connections + LIST_ENTRY LongList; // list of waiting connections + + + // + // Hash table of non-inactive connections. + // + + CONNECTION_HASH ConnectionHash[CONNECTION_HASH_COUNT]; + + // + // Control the queue of waiting find names. + // + + USHORT FindNameTime; // incremented each time the timer runs + BOOLEAN FindNameTimerActive; // TRUE if the timer is queued + CTETimer FindNameTimer; // runs every FIND_NAME_GRANULARITY + ULONG FindNameTimeout; // the retry count in timer ticks + + ULONG FindNamePacketCount; // Count of packets on the queue + LIST_ENTRY WaitingFindNames; // FIND_NAME frames waiting to go out + + // + // The cache of NETBIOS_CACHE entries. + // + + PNETBIOS_CACHE_TABLE NameCache; + + // + // The current time stamp, incremented every second. + // + + USHORT CacheTimeStamp; + + // + // Maximum valid NIC ID we can use. + // + + USHORT MaximumNicId; + + + // + // Handle for our binding to the IPX driver. + // + + HANDLE BindHandle; + + // + // Holds the output from binding to IPX. + // + union { + IPX_INTERNAL_BIND_OUTPUT Bind; + IPX_INTERNAL_BIND_INPUT BindInput; + }; + + // + // Holds our reserved netbios name, which is 10 bytes + // of zeros followed by our node address. + // +#if !defined(_PNP_POWER) + UCHAR ReservedNetbiosName[16]; +#endif !_PNP_POWER + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many packets have been allocated. + // + + ULONG AllocatedSendPackets; + ULONG AllocatedReceivePackets; + ULONG AllocatedReceiveBuffers; + +#if defined(_PNP_POWER) + // + // This is the size of each buffer in the receive buffer pool. + // We reallocate buffer pool when the LineInfo.MaxPacketSize changes(increases) + // from IPX because of a new adapter. The LineInfo.MaxPacketSize could + // also change(decrease) when a adapter disappears but our buffer pool size + // will stay at this value. + // + ULONG CurMaxReceiveBufferSize; +#endif _PNP_POWER + + // + // Other configuration parameters. + // + + ULONG AckDelayTime; // converted to short timeouts, rounded up + ULONG AckWindow; + ULONG AckWindowThreshold; + ULONG EnablePiggyBackAck; + ULONG Extensions; + ULONG RcvWindowMax; + ULONG BroadcastCount; + ULONG BroadcastTimeout; + ULONG ConnectionCount; + ULONG ConnectionTimeout; + ULONG InitPackets; + ULONG MaxPackets; + ULONG InitialRetransmissionTime; + ULONG Internet; + ULONG KeepAliveCount; + ULONG KeepAliveTimeout; + ULONG RetransmitMax; + ULONG RouterMtu; + + ULONG MaxReceiveBuffers; + + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // The following field is a head of a list of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. +#if defined(_PNP_POWER) + LIST_ENTRY AdapterAddressDatabase; // list of netbios names made from adapter addresses. +#endif _PNP_POWER + + ULONG AddressCount; // number of addresses in the database. + + NDIS_HANDLE NdisBufferPoolHandle; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // This structure holds a pre-built IPX header which is used + // to quickly fill in common fields of outgoing connectionless + // frames. + // + + IPX_HEADER ConnectionlessHeader; + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + +#if defined(_PNP_POWER) + HANDLE TdiRegistrationHandle; +#endif _PNP_POWER + + // + // Counters for most of the statistics that NB maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempFrameBytesSent; + ULONG TempFramesSent; + ULONG TempFrameBytesReceived; + ULONG TempFramesReceived; + + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbiStartTime; + + // + // This array is used to quickly dismiss connectionless frames + // that are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + +} DEVICE, * PDEVICE; + + +extern PDEVICE NbiDevice; +EXTERNAL_LOCK(NbiGlobalPoolInterlock); + +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// + +EXTERNAL_LOCK(NbiGlobalInterlock); + + +// +// device state definitions +// +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +#define NB_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 +#define AFREF_TIMEOUT 5 +#define AFREF_CONNECTION 6 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + PNB_LOCK AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST OpenRequest; // the request used for open + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + LIST_ENTRY ConnectionDatabase; // associated with this address. + + LIST_ENTRY ReceiveDatagramQueue; // posted by the client. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // Handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredHandler[6]; + + // + // This is a list of handlers for a given event. They can be + // accessed using the explicit names for type-checking, or the + // array (indexed by the event type) for speed. + // + + union { + struct { + PTDI_IND_CONNECT ConnectionHandler; + PTDI_IND_DISCONNECT DisconnectHandler; + PTDI_IND_ERROR ErrorHandler; + PTDI_IND_RECEIVE ReceiveHandler; + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + }; + PVOID Handlers[6]; + }; + + PVOID HandlerContexts[6]; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +typedef struct _NBI_NETBIOS_ADDRESS { + UCHAR NetbiosName[16]; + USHORT NetbiosNameType; + BOOLEAN Broadcast; +} NBI_NETBIOS_ADDRESS, *PNBI_NETBIOS_ADDRESS; + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 +#define AREF_NAME_FRAME 3 +#define AREF_TIMER 4 +#define AREF_FIND 5 + +#define AREF_TOTAL 8 + + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + NB_LOCK Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + UCHAR NameTypeFlag; // NB_NAME_UNIQUE or NB_NAME_GROUP + + NBI_NETBIOS_ADDRESS NetbiosAddress; // our netbios name. + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + ULONG State; // current state of the address. + struct _DEVICE *Device; // device context to which we are attached. + PNB_LOCK DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + UCHAR SendPacketHeader[NB_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + + // + // This timer is used for registering the name. + // + + CTETimer RegistrationTimer; + + // + // Number of times an add name frame has been sent. + // + + ULONG RegistrationCount; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbiDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +// +// Values for Flags +// + +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000002 +#if defined(_PNP_POWER) +#define ADDRESS_FLAGS_CONFLICT 0x00000010 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// this booleans are passed to nbiverifyaddressfile calls. +// +#define CONFLICT_IS_OK TRUE +#define CONFLICT_IS_NOT_OK FALSE +#endif _PNP_POWER + +// +// Values for State +// + +#define ADDRESS_STATE_REGISTERING 1 +#define ADDRESS_STATE_OPEN 2 +#define ADDRESS_STATE_STOPPING 3 + +#if defined(_PNP_POWER) +// +// This holds the adapters names i.e netbios names which are +// created from adater node address to support adapter status +// queries using adapter node addresses. +// +typedef struct _ADAPTER_ADDRESS { + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + NIC_HANDLE NicHandle; // NicHandle corresponding to this address. + UCHAR NetbiosName[16]; +} ADAPTER_ADDRESS, *PADAPTER_ADDRESS; +#endif _PNP_POWER + +// +// This defines the types of probe packets we can send. +// + +typedef enum _NB_ACK_TYPE { + NbiAckQuery, + NbiAckResponse, + NbiAckResend +} NB_ACK_TYPE, *PNB_ACK_TYPE; + + +// +// This defines the a packetizing location in a +// send. +// + +typedef struct _SEND_POINTER { + ULONG MessageOffset; // up count, bytes sent this message. + PREQUEST Request; // current send request in chain. + PNDIS_BUFFER Buffer; // current buffer in send chain. + ULONG BufferOffset; // current byte offset in current buffer. + USHORT SendSequence; +} SEND_POINTER, *PSEND_POINTER; + +// +// This defines the current location in a receive. +// + +typedef struct _RECEIVE_POINTER { + ULONG MessageOffset; // up count, bytes received this message. + ULONG Offset; // up count, bytes received this request. + PNDIS_BUFFER Buffer; // current buffer in receive request. + ULONG BufferOffset; // current byte offset in current buffer. +} RECEIVE_POINTER, *PRECEIVE_POINTER; + + +// +// This structure defines a connection, which controls a +// session with a remote. +// + +#define CREF_VERIFY 0 +#define CREF_LISTEN 1 +#define CREF_CONNECT 2 +#define CREF_WAIT_CACHE 3 +#define CREF_TIMER 4 +#define CREF_INDICATE 5 +#define CREF_ACTIVE 6 +#define CREF_FRAME 7 +#define CREF_BY_CONTEXT 8 +#define CREF_W_ACCEPT 9 +#define CREF_SEND 10 +#define CREF_RECEIVE 11 +#define CREF_PACKETIZE 12 +#define CREF_DISASSOC 13 +#define CREF_W_PACKET 14 +#define CREF_CANCEL 15 +#define CREF_NDIS_SEND 16 +#define CREF_SHORT_D_ACK 17 +#define CREF_LONG_D_ACK 18 +#define CREF_FIND_ROUTE 19 +#define CREF_ACCEPT 20 + +#define CREF_TOTAL 24 + +typedef struct _CONNECTION { + +#if DBG + ULONG RefTypes[CREF_TOTAL]; +#endif + + CSHORT Type; + USHORT Size; + + NB_LOCK Lock; + PNB_LOCK DeviceLock; + + ULONG ReferenceCount; // number of references to this object. + + CONNECTION_CONTEXT Context; // client-specified value. + + ULONG State; + ULONG SubState; + ULONG ReceiveState; // SubState tracks sends when active. + ULONG NewNetbios; // 1 if we negotiated this. + + REQUEST_LIST_HEAD SendQueue; + REQUEST_LIST_HEAD ReceiveQueue; + + USHORT ReceiveSequence; + + USHORT LocalRcvSequenceMax; // we advertise to him (will be near SendSequence) + USHORT RemoteRcvSequenceMax; // he advertises to us (will be near ReceiveSequence) + USHORT SendWindowSequenceLimit; // when this send window ends (may send past it however) + + // + // RemoteRcvSequenceMax is the largest frame number that he expects to + // receive, while SendWindowSequenceLimit is one more than the max + // we can send. I.e. if he is advertising a window of 4 and we think + // the window should be 2, and the current send sequence is 7, + // RemoteRcvSequenceMax is 10 and SendWindowSequenceLimit is 9. + // + + USHORT ReceiveWindowSize; // when it is open, how big to make it + USHORT SendWindowSize; // what we'll send, may be less than what he advertises + USHORT MaxSendWindowSize; // maximum we allow it to grow to + + USHORT IncreaseWindowFailures; // how many windows after increase have had retransmits + BOOLEAN RetransmitThisWindow; // we had to retransmit in this send window + BOOLEAN SendWindowIncrease; // send window was just increased. + BOOLEAN ResponseTimeout; // we hit timeout in SEND_W or REMOTE_W + + BOOLEAN SendBufferInUse; // current send's already queued on packet + + ULONG Retries; + + // + // Tracks the current send. + // + + SEND_POINTER CurrentSend; + + // + // Tracks the unacked point in the send. + // + + SEND_POINTER UnAckedSend; + + PREQUEST FirstMessageRequest; // first one in the message. + PREQUEST LastMessageRequest; // last one in the message. + + ULONG CurrentMessageLength; // total length of current message. + + // + // Tracks the current receive. + // + + RECEIVE_POINTER CurrentReceive; // where to receive next data + RECEIVE_POINTER PreviousReceive; // stores it while transfer in progress + + PREQUEST ReceiveRequest; // current one; not in ReceiveQueue + ULONG ReceiveLength; // length of ReceiveRequest + + ULONG ReceiveUnaccepted; // by client...only indicate when == 0 + + ULONG CurrentIndicateOffset; // if previous frame was partially accepted. + + IPX_LINE_INFO LineInfo; // for the adapter this connect is on. + ULONG MaximumPacketSize; // as negotiated during session init/ack + + // + // Links us in the non-inactive connection hash bucket. + // + + struct _CONNECTION * NextConnection; + + // + // These are used to determine when to piggyback and when not to. + // + + BOOLEAN NoPiggybackHeuristic; // we have reason to assume it would be bad. + BOOLEAN PiggybackAckTimeout; // we got a timeout last time we tried. + ULONG ReceivesWithoutAck; // used to do an auto ack. + + // + // The following field is used as linkage in the device's + // PacketizeConnections queue. + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device's + // WaitPacketConnections queue. + // + + LIST_ENTRY WaitPacketLinkage; + + // + // The following field is used as linkage in the device's + // DataAckConnections queue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if we are on these queues. + // + + BOOLEAN OnPacketizeQueue; + BOOLEAN OnWaitPacketQueue; + BOOLEAN OnDataAckQueue; + + // + // TRUE if we have a piggyback ack pending. + // + + BOOLEAN DataAckPending; + + // + // TRUE if the current receive does not allow piggyback acks. + // + + BOOLEAN CurrentReceiveNoPiggyback; + + // + // Number of short timer expirations with the data ack queued. + // + + ULONG DataAckTimeouts; + + // + // Used to queue sends so that no two are outstanding at once. + // + + ULONG NdisSendsInProgress; + LIST_ENTRY NdisSendQueue; + + // + // This pointer is valid when NdisSendsInProgress is non-zero; + // it holds a pointer to a location on the stack of the thread + // which is inside NbiAssignSequenceAndSend. If this location + // is set to TRUE, it means the connection was stopped by another + // thread and a reference was added to keep the connection around. + // + + PBOOLEAN NdisSendReference; + + // + // These are used for timeouts. + // + + ULONG BaseRetransmitTimeout; // config # of short timeouts we wait. + ULONG CurrentRetransmitTimeout; // possibly backed-off number + ULONG WatchdogTimeout; // how many long timeouts we wait. + ULONG Retransmit; // timer; based on Device->ShortAbsoluteTime + ULONG Watchdog; // timer; based on Device->LongAbsoluteTime + USHORT TickCount; // 18.21/second, # for 576-byte packet. + USHORT HopCount; // As returned by ipx on find route. + BOOLEAN OnShortList; // are we inserted in the list + BOOLEAN OnLongList; // are we inserted in the list + LIST_ENTRY ShortList; // queues us on Device->ShortList + LIST_ENTRY LongList; // queues us on Device->LongList + + // + // These are valid when we have a connection established; + // + + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + + PREQUEST DisassociatePending; // guarded by device lock. + PREQUEST ClosePending; + + PREQUEST ConnectRequest; + PREQUEST ListenRequest; + PREQUEST AcceptRequest; + PREQUEST DisconnectRequest; + PREQUEST DisconnectWaitRequest; + + ULONG CanBeDestroyed; // FALSE if reference is non-zero + ULONG ThreadsInHandleConnectionZero; // # of threads in HandleConnectionZero + + // + // These are used to hold extra data that was sent on a session + // init, for use in sending the ack. Generally will be NULL and 0. + // + + PUCHAR SessionInitAckData; + ULONG SessionInitAckDataLength; + + IPX_LOCAL_TARGET LocalTarget; // for the remote when active. + IPX_HEADER RemoteHeader; + + CTETimer Timer; + + PADDRESS_FILE AddressFile; // guarded by device lock if associated. + LIST_ENTRY AddressFileLinkage; // guarded by device lock + ULONG AddressFileLinked; // TRUE if queued using AddressFileLinkage + + PDEVICE Device; +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + + CHAR RemoteName[16]; // for an active connection. + + IPX_FIND_ROUTE_REQUEST FindRouteRequest; // use this to verify route. + + TDI_CONNECTION_INFO ConnectionInfo; // can be queried from above. + + BOOLEAN FindRouteInProgress; // we have a request pending. + + BOOLEAN SendPacketInUse; // put this here to align packet/header. + BOOLEAN IgnoreNextDosProbe; + + NTSTATUS Status; // status code for connection rundown. + +#ifdef RSRC_TIMEOUT_DBG + LARGE_INTEGER FirstMessageRequestTime; +#endif //RSRC_TIMEOUT_DBG + + NDIS_HANDLE SendPacketPoolHandle; // poolhandle for sendpacket below when + // the packet is allocated from ndis pool. + + NB_SEND_PACKET SendPacket; // try to use this first for sends + + ULONG Flags; // miscellaneous connection flags + + UCHAR SendPacketHeader[1]; // connection is extended to include this + + // + // NOTE: This is variable length structure! + // Do not add fields below this comment. + // +} CONNECTION, *PCONNECTION; + + +#define CONNECTION_STATE_INACTIVE 1 +#define CONNECTION_STATE_CONNECTING 2 +#define CONNECTION_STATE_LISTENING 3 +#define CONNECTION_STATE_ACTIVE 4 +#define CONNECTION_STATE_DISCONNECT 5 +#define CONNECTION_STATE_CLOSING 6 + + +#define CONNECTION_SUBSTATE_L_WAITING 1 // queued by a listen +#define CONNECTION_SUBSTATE_L_W_ACCEPT 2 // waiting for user to accept +#define CONNECTION_SUBSTATE_L_W_ROUTE 3 // waiting for rip response + +#define CONNECTION_SUBSTATE_C_FIND_NAME 1 // waiting for cache response +#define CONNECTION_SUBSTATE_C_W_ACK 2 // waiting for session init ack +#define CONNECTION_SUBSTATE_C_W_ROUTE 3 // waiting for rip response +#define CONNECTION_SUBSTATE_C_DISCONN 4 // disconnect was issued + +#define CONNECTION_SUBSTATE_A_IDLE 1 // no sends in progress +#define CONNECTION_SUBSTATE_A_PACKETIZE 2 // packetizing a send +#define CONNECTION_SUBSTATE_A_W_ACK 3 // waiting for an ack +#define CONNECTION_SUBSTATE_A_W_PACKET 4 // waiting for a packet +#define CONNECTION_SUBSTATE_A_W_EOR 5 // waiting for eor to start packetizing +#define CONNECTION_SUBSTATE_A_W_PROBE 6 // waiting for a keep-alive response +#define CONNECTION_SUBSTATE_A_REMOTE_W 7 // remote shut down our window + +#define CONNECTION_RECEIVE_IDLE 1 // no receives queued +#define CONNECTION_RECEIVE_ACTIVE 2 // receive is queued +#define CONNECTION_RECEIVE_W_RCV 3 // waiting for receive to be posted +#define CONNECTION_RECEIVE_INDICATE 4 // indication in progress +#define CONNECTION_RECEIVE_TRANSFER 5 // transfer is in progress +#define CONNECTION_RECEIVE_PENDING 6 // last request is queued for completion + +#define CONNECTION_SUBSTATE_D_W_ACK 1 +#define CONNECTION_SUBSTATE_D_GOT_ACK 2 + +// +// Bit values for Flags field in +// the CONNECTION structure. +// +#define CONNECTION_FLAGS_AUTOCONNECTING 0x00000001 // RAS autodial in progress +#define CONNECTION_FLAGS_AUTOCONNECTED 0x00000002 // RAS autodial connected + +#ifdef RSRC_TIMEOUT_DBG +extern ULONG NbiGlobalDebugResTimeout; +extern LARGE_INTEGER NbiGlobalMaxResTimeout; +extern NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +#endif //RSRC_TIMEOUT_DBG diff --git a/private/ntos/tdi/isn/nb/nwlnknb.ini b/private/ntos/tdi/isn/nb/nwlnknb.ini new file mode 100644 index 000000000..e2df88f54 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nwlnknb.ini @@ -0,0 +1,43 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkNb + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnknb.sys + DisplayName = NWLINK2 IPX Netbios Protocol + Group = TDI + DependOnService = REG_MULTI_SZ "NwlnkIpx" + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\NwlnkIpx" + Export = REG_MULTI_SZ "\Device\NwlnkNb" + Route = REG_MULTI_SZ ""NwlnkIpx"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + Parameters + AckDelayTime = REG_DWORD 0x000000fa + AckWindow = REG_DWORD 0x00000002 + AckWindowThreshold = REG_DWORD 0x000001f4 + EnablePiggyBackAck = REG_DWORD 0x00000001 + Extensions = REG_DWORD 0x00000001 + RcvWindowMax = REG_DWORD 0x00000004 + BroadcastCount = REG_DWORD 0x00000003 + BroadcastTimeout = REG_DWORD 0x00000001 + ConnectionCount = REG_DWORD 0x00000005 + ConnectionTimeout = REG_DWORD 0x00000002 + InitPackets= REG_DWORD 0x00000005 + MaxPackets = REG_DWORD 0x0000001e + InitialRetransmissionTime = REG_DWORD 0x000001f4 + Internet = REG_DWORD 0x00000001 + KeepAliveCount = REG_DWORD 0x00000008 + KeepAliveTimeout = REG_DWORD 0x0000003c + RetransmitMax = REG_DWORD 0x00000008 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkNb + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isn/nb/nwlnknb.rc b/private/ntos/tdi/isn/nb/nwlnknb.rc new file mode 100644 index 000000000..1b0163cf3 --- /dev/null +++ b/private/ntos/tdi/isn/nb/nwlnknb.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Netbios Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnknb.sys" +#define VER_ORIGINALFILENAME_STR "nwlnknb.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/nb/packet.c b/private/ntos/tdi/isn/nb/packet.c new file mode 100644 index 000000000..7e22534a8 --- /dev/null +++ b/private/ntos/tdi/isn/nb/packet.c @@ -0,0 +1,1482 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local Function Protos +// +#if defined(_PNP_POWER) +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ); +#endif _PNP_POWER + + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + + Header - Points to storage for the header. + + HeaderLength - The length of the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisNbBuffer; + PNB_SEND_RESERVED Reserved; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + NbiAllocateSendPacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } +// DbgPrint("NbiInitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + + // + // allocate the mac header. + // + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + Header, + MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisBuffer); + +// DbgPrint("NbiInitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + // + // Allocate the nb header + // + NdisAllocateBuffer( + &NdisStatus, + &NdisNbBuffer, + Device->NdisBufferPoolHandle, + Header + MacHeaderNeeded, + HeaderLength - MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + // DbgPrint("NbiInitializeSendPacket: IPX header address is (%x)\n", NdisNbBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisNbBuffer); + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->SendInProgress = FALSE; + Reserved->OwnedByConnection = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisBuffer; + + Reserved->Reserved[0] = NULL; + Reserved->Reserved[1] = NULL; + + InsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeSendPacket */ + + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PNB_RECEIVE_RESERVED Reserved; + + NbiAllocateReceivePacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->TransferInProgress = FALSE; + + InsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceivePacket */ + + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + InsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceiveBuffer */ + + + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + + HeaderLength - The length of the first buffer on the packet. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNB_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + CTEAssert(HeaderLength > MacHeaderNeeded); + Reserved = SEND_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Free the mac header + // + // DbgPrint("NbiDeinitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // Free the nb header + // + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: IPX header address is (%x)\n", NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, HeaderLength - MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // free the packet + // + NbiFreeSendPacket (Device, Packet); + +} /* NbiDeinitializeSendPacket */ + + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PNB_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeReceivePacket (Device, Packet); + +} /* NbiDeinitializeReceivePacket */ + + + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + +Return Value: + + None. + + THIS ROUTINE SHOULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ + +{ +#if defined(_PNP_POWER) + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); +#else + CTELockHandle LockHandle; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER + + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* NbiDeinitializeReceiveBuffer */ + + + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PNB_SEND_PACKET Packet; + PNB_SEND_RESERVED Reserved; + PUCHAR Header; + ULONG HeaderLength; + NTSTATUS Status; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + SendPool = (PNB_SEND_POOL)NbiAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + RtlZeroMemory (SendPool, SendPoolSize); + + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &SendPool->PoolHandle, Device->InitPackets, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( SendPool, SendPoolSize, MEMORY_PACKET, "Send Pool Freed"); + return; + } +#endif + + NB_DEBUG2 (PACKET, ("Initializing send pool %lx, %d packets, header %d\n", + SendPool, Device->InitPackets, HeaderLength)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitPackets]); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (NbiInitializeSendPacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + SendPool->PoolHandle, +#endif + Packet, + Header, + HeaderLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->u.SR_NF.Address = NULL; +#ifdef NB_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += HeaderLength; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedSendPackets += SendPool->PacketCount; + +} /* NbiAllocateSendPool */ + + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 5 receive packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PNB_RECEIVE_PACKET Packet; + PNB_RECEIVE_RESERVED Reserved; + NTSTATUS Status; + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + ReceivePool = (PNB_RECEIVE_POOL)NbiAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + RtlZeroMemory (ReceivePool, ReceivePoolSize); + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &ReceivePool->PoolHandle, Device->InitPackets, sizeof(NB_RECEIVE_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( ReceivePool, ReceivePoolSize, MEMORY_PACKET, "Receive Pool Freed"); + return; + } +#endif NB_OWN_PACKETS + + NB_DEBUG2 (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitPackets)); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (NbiInitializeReceivePacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + ReceivePool->PoolHandle, +#endif + Packet) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); +#ifdef NB_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); +// PushEntryList (&Device->ReceivePacketList, &Reserved->PoolLinkage); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + +} /* NbiAllocateReceivePool */ + + +#if defined(_PNP_POWER) + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + DataLength - Max length of the data in each buffer. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PUCHAR Data; + + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + + ReceiveBuffer->Pool = ReceiveBufferPool; + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + ReceiveBufferPool->BufferDataSize = DataLength; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + Device->CurMaxReceiveBufferSize = DataLength; + +} /* NbiAllocateReceiveBufferPool */ +#else + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + UINT DataLength; + PUCHAR Data; + + DataLength = Device->Bind.LineInfo.MaximumPacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef NB_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + +} /* NbiAllocateReceiveBufferPool */ +#endif _PNP_POWER + +#if defined(_PNP_POWER) + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ) + +/*++ + +Routine Description: + + This routines destroys all the existing Buffer Pools and creates + new one using the larger packet size given to us by IPX because + a new card was inserted with a larger packet size. + +Arguments: + + WorkItem - The work item that was allocated for this. + +Return Value: + + None. + +--*/ +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + + NB_GET_LOCK ( &Device->Lock, &LockHandle ); + + if ( Device->Bind.LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("Reallocating new pools due to new maxpacketsize\n"); +#endif + NbiDestroyReceiveBufferPools( Device ); + NbiAllocateReceiveBufferPool( Device, Device->Bind.LineInfo.MaximumPacketSize ); + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + NbiFreeMemory( WorkItem, sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buff Work Item freed"); +} + +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ) + +/*++ + +Routine Description: + + This routine frees the +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ +{ + PDEVICE Device = NbiDevice; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize,i; + + CTEAssert( ReceiveBufferPool->BufferDataSize ); + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (ReceiveBufferPool->BufferDataSize * Device->InitPackets); + + // + // Check if we can free this pool + // + CTEAssert(ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + RemoveEntryList( &ReceiveBufferPool->Linkage ); + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + +} + + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routines walks the ReceiveBufferPoolList and destroys the + pool which does not have any buffer in use. + +Arguments: + +Return Value: + + None. + + THIS ROUTINE COULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine is also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ +{ + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY Unused; + + + // + // Clean up this list before we call NbiFreeReceiveBufferPool bcoz that will + // simply destroy all the buffer which might be queue here on this list. + // At the end of this routine we must start with a fresh ReceiveBufferList. + // + do { + Unused = PopEntryList( &Device->ReceiveBufferList ); + } while( Unused ); + + // + // Now destroy each individual ReceiveBufferPool. + // + for ( p = Device->ReceiveBufferPoolList.Flink; + p != &Device->ReceiveBufferPoolList; + ) { + + + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + p = p->Flink; + + // + // This will destroy and unlink this Pool if none of its buffer is + // in use currently. + // + + if ( ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } else { + // + // When the device is stopping we must succeed in freeing the pool. + CTEAssert( Device->State != DEVICE_STATE_STOPPING ); + } + + } + +} + + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine returns the receive buffer back to the free list. + It checks the size of this buffer. If it is smaller than the + the CurMaxReceiveBufferSize, then it does not return this back + to the free list, instead it destroys it and possibly also + destroys the pool associated with it. O/w it simply returns this + to the free list. + +Arguments: + + ReceiveBuffer - Pointer to the buffer to be returned to the free list. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; +#if defined(DBG) + ULONG BufLen = 0; +#endif + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + +#if defined(DBG) + NdisQueryBuffer( ReceiveBuffer->NdisBuffer, NULL, &BufLen ); + CTEAssert( BufLen == ReceiveBufferPool->BufferDataSize ); +#endif + + // + // This is an old buffer which was in use when we changed + // the CurMaxReceiveBufferSize due to new adapter. We must not + // return this buffer back to free list. Infact, if the pool + // associated with this buffer does not have any other buffers + // in use, we should free the pool also. + CTEAssert( ReceiveBufferPool->BufferFree < ReceiveBufferPool->BufferCount ); + ReceiveBufferPool->BufferFree++; + + if ( ReceiveBufferPool->BufferDataSize < Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("ReceiveBuffer %lx, not returned to pool %lx( Free %d)\n", ReceiveBuffer, ReceiveBufferPool, ReceiveBufferPool->BufferFree); +#endif + + + if ( ReceiveBufferPool->BufferFree == ReceiveBufferPool->BufferCount ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } + } else { + + PushEntryList( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage ); + + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); +} +#endif _PNP_POWER + + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + + LockAcquired - TRUE if Device->Lock is acquired. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (!LockAcquired) { + NB_GET_LOCK (&Device->Lock, &LockHandle); + } + + if (Device->AllocatedSendPackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateSendPool (Device); + + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + return s; + } else { + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return NULL; + } + +} /* NbiPopSendPacket */ + + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine frees a packet back to the device context's pool. + If there are connections waiting for packets, it removes + one from the list and inserts it on the packetize queue. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // BUGBUG: Make this a function. Optimize for + // UP by not doing two checks? + // + + if (!IsListEmpty (&Device->WaitPacketConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = RemoveHeadList (&Device->WaitPacketConnections); + + // + // Take a connection off the WaitPacketQueue and put it + // on the PacketizeQueue. We don't worry about if the + // connection has stopped, that will get checked when + // the PacketizeQueue is run down. + // + // Since this is in send completion, we may not get + // a receive complete. We guard against this by calling + // NbiReceiveComplete from the long timer timeout. + // + + if (p != &Device->WaitPacketConnections) { + + Connection = CONTAINING_RECORD (p, CONNECTION, WaitPacketLinkage); + + CTEAssert (Connection->OnWaitPacketQueue); + Connection->OnWaitPacketQueue = FALSE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } + +} /* NbiPushSendPacket */ + + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine checks if a connection is on the wait packet + queue and if so takes it off and queues it to be packetized. + It is meant to be called when the connection's packet has + been freed. + +Arguments: + + Connection - The connection to check. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (Connection->OnWaitPacketQueue) { + + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + InsertTailList( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage); + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + return; + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiCheckForWaitPacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceivePool (Device); + NB_FREE_LOCK (&Device->Lock, LockHandle); + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + + return s; + + } else { + + return NULL; + + } + +} /* NbiPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the device context's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ +#if defined(_PNP_POWER) + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + CTELockHandle LockHandle; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + s = PopEntryList( &Device->ReceiveBufferList ); + + + if ( !s ) { + + // + // No buffer in the pool, see if we can allocate more. + // + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateReceiveBufferPool (Device, Device->CurMaxReceiveBufferSize ); + s = PopEntryList(&Device->ReceiveBufferList); + } + } + + if ( s ) { + + + // + // Decrement the BufferFree count on the corresponding ReceiveBufferPool. + // so that we know that + ReceiveBuffer = CONTAINING_RECORD( s, NB_RECEIVE_BUFFER, PoolLinkage ); + + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + + CTEAssert( ReceiveBufferPool->BufferFree && ( ReceiveBufferPool->BufferFree <= ReceiveBufferPool->BufferCount ) ); + CTEAssert( ReceiveBufferPool->BufferDataSize == Device->CurMaxReceiveBufferSize ); + + ReceiveBufferPool->BufferFree--; + + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; +#else + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntryList( + &Device->ReceiveBufferList, + &Device->Lock.Lock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceiveBufferPool (Device); + s = PopEntryList(&Device->ReceiveBufferList); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; + + } else { + + return NULL; + + } +#endif _PNP_POWER +} /* NbiPopReceiveBuffer */ + diff --git a/private/ntos/tdi/isn/nb/precomp.h b/private/ntos/tdi/isn/nb/precomp.h new file mode 100644 index 000000000..a024f2d3d --- /dev/null +++ b/private/ntos/tdi/isn/nb/precomp.h @@ -0,0 +1,42 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include +#include +#include +#include +#include +#include "isnnb.h" +#include "config.h" +#include "nbitypes.h" +#include "nbiprocs.h" +#include "zwapi.h" diff --git a/private/ntos/tdi/isn/nb/query.c b/private/ntos/tdi/isn/nb/query.c new file mode 100644 index 000000000..6ee33adf3 --- /dev/null +++ b/private/ntos/tdi/isn/nb/query.c @@ -0,0 +1,1817 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Remove the warning -- this is defined in windef also. +// + +#ifdef FAR +#undef FAR +#endif + +#include +#include + + +// +// Useful macro to obtain the total length of a buffer chain. +// BUGBUG: Make this use NDIS macros. +// + +#define NbiGetBufferChainLength(Buffer, Length) { \ + PNDIS_BUFFER _Buffer = (Buffer); \ + *(Length) = 0; \ + while (_Buffer) { \ + *(Length) += MmGetMdlByteCount(_Buffer); \ + _Buffer = _Buffer->Next; \ + } \ +} + + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + The status of operation. + +--*/ + +{ + NTSTATUS Status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PCONNECTION Connection; + union { + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS NbiAddress; + } AddressInfo; + TA_NETBIOS_ADDRESS BroadcastAddress; + TDI_ADDRESS_IPX IpxAddress; + TDI_DATAGRAM_INFO DatagramInfo; + struct { + FIND_NAME_HEADER Header; + FIND_NAME_BUFFER Buffer; + } FindNameInfo; + } TempBuffer; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + PADAPTER_STATUS AdapterStatus; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG TargetBufferLength; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PNETBIOS_CACHE CacheName; + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + NTSTATUS QueryStatus; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN UsedConnection; + UINT i; + + + // + // what type of status do we want? + // + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (Query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = FALSE; + + } else if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE) { + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = TRUE; + + AddressFile = Connection->AddressFile; + + } else { + + Status = STATUS_INVALID_ADDRESS; + break; + + } + + Address = AddressFile->Address; + + NB_DEBUG2 (QUERY, ("Query address info on %lx\n", AddressFile)); + + TempBuffer.AddressInfo.ActivityCount = 0; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + if (CONTAINING_RECORD (p, ADDRESS_FILE, Linkage)->State == ADDRESSFILE_STATE_OPEN) { + ++TempBuffer.AddressInfo.ActivityCount; + } + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + TdiBuildNetbiosAddress( + AddressFile->Address->NetbiosAddress.NetbiosName, + (BOOLEAN)(AddressFile->Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempBuffer.AddressInfo.NbiAddress); + + Status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + if (UsedConnection) { + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + } else { + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Status = STATUS_INVALID_CONNECTION; + + } else { + + // + // Assume 50 ms of delay for every hop after the + // first. The delay is returned as a negative number. + // + + if (Connection->HopCount > 1) { + Connection->ConnectionInfo.Delay.HighPart = (ULONG)-1; + Connection->ConnectionInfo.Delay.LowPart = + -((Connection->HopCount-1) * 50 * MILLISECONDS); + } else { + Connection->ConnectionInfo.Delay.HighPart = 0; + Connection->ConnectionInfo.Delay.LowPart = 0; + } + + // + // We have tick count; to convert to bytes/second we do: + // + // packet 576 bytes 18.21 ticks + // ---------------- * --------- * ----------- + // tick_count ticks packet seconds + // + // to get 10489/tick_count = bytes/second. We + // double this because routers tend to + // overestimate it. + // + // Since tick_count has such a low granularity, + // a tick count of 1 gives us a throughput of + // only 84 kbps, which is much too low. In + // that case we return twice the link speed + // which is in 100 bps units; that corresponds + // to about 1/6 of our bandwidth in bytes/sec. + // + + if (Connection->TickCount <= Connection->HopCount) { + + Connection->ConnectionInfo.Throughput.QuadPart = + UInt32x32To64 (Connection->LineInfo.LinkSpeed, 2); + + } else { + + Connection->ConnectionInfo.Throughput.HighPart = 0; + Connection->ConnectionInfo.Throughput.LowPart = + 20978 / (Connection->TickCount - Connection->HopCount); + + } + + Connection->ConnectionInfo.Unreliable = FALSE; + + Status = TdiCopyBufferToMdl ( + &Connection->ConnectionInfo, + 0, + sizeof(TDI_CONNECTION_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + break; + + case TDI_QUERY_PROVIDER_INFO: + + NB_DEBUG2 (QUERY, ("Query provider info\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Information, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + NB_DEBUG2 (QUERY, ("Query broadcast address\n")); + + TempBuffer.BroadcastAddress.TAAddressCount = 1; + TempBuffer.BroadcastAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + TempBuffer.BroadcastAddress.Address[0].AddressLength = 0; + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.BroadcastAddress, + 0L, + sizeof (TempBuffer.BroadcastAddress.TAAddressCount) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressType) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressLength), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // Determine if this is a local or remote query. + // + + RemoteAdapterStatus = FALSE; + + if (Query->RequestConnectionInformation != NULL) { + + RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (RemoteAddress == NULL) { + return STATUS_BAD_NETWORK_PATH; + } + +#if defined(_PNP_POWER) + if ( !NbiFindAdapterAddress( + RemoteAddress->NetbiosName, + LOCK_NOT_ACQUIRED ) ) { + + RemoteAdapterStatus = TRUE; + } +#else + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + Device->ReservedNetbiosName, + 16)) { + + RemoteAdapterStatus = TRUE; + + } +#endif _PNP_POWER + + } + + if (RemoteAdapterStatus) { + + // + // See if we have this name cached. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this status + // request and processing will be resumed when + // we get a response. + // + // The status field in the request will hold + // the cache entry for the remote. The information + // field will hold the remote netbios name while + // it is in the WaitingAdapterStatus queue, and + // will hold a timeout value while we it is in + // the ActiveAdapterStatus queue. + // + + NB_DEBUG2 (QUERY, ("Queueing up adapter status %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + InsertTailList( + &Device->WaitingAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found adapter status cached %lx\n", Request)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + REQUEST_STATUS(Request) = (NTSTATUS)CacheName; + ++CacheName->ReferenceCount; + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = 0; + + InsertTailList( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiSendStatusQuery (Request); + + Status = STATUS_PENDING; + + } else { + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // Local adapter status. + // + + NbiGetBufferChainLength (REQUEST_NDIS_BUFFER(Request), &TargetBufferLength); + + Status = NbiStoreAdapterStatus( + TargetBufferLength, + 1, // NIC ID, was 0, changed to 1 for Bug #18026 + // because for NicId = 0, Ipx returns virtual + // address. Netbios uses that to register the + // name (00...01) and fails. + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + + // + // This should succeed since we know the length + // will fit. + // + + (VOID)TdiCopyBufferToMdl( + AdapterStatus, + 0, + ValidStatusLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + + } + + } + + break; + + case TDI_QUERY_FIND_NAME: + + // + // Check that there is a valid Netbios remote address. + // + + if ((Query->RequestConnectionInformation == NULL) || + ((RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE)) == NULL)) { + + return STATUS_BAD_NETWORK_PATH; + } + + // + // We assume the entire request buffer is in the first + // piece of the MDL chain (BUGBUG: Can we do this?). + // Make sure there is room for at least the header. + // + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER)) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // See if we have this name cached. We specify that this is + // a netbios name query, so this will only succeed if this is a + // unique name -- for a group name it will queue up a find + // name query and when we get the response we will fill in + // the request's buffer based on it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameNetbiosFindName, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this find + // name request and processing will be resumed when + // we get a response. + // + // The information field will hold the remote + // netbios name while it is in the WaitingNetbiosFindName + // queue. The status will hold the current status -- + // initially failure, then success, then overflow + // if the buffer is too small. + // + + NB_DEBUG2 (QUERY, ("Queueing up find name %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_NB_FIND_NAME); + + FindNameHeader->node_count = 0; + FindNameHeader->reserved = 0; + FindNameHeader->unique_group = 0; + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + // + // Assume it fails, we update the status to + // SUCCESS or BUFFER_OVERFLOW if needed. + // + + REQUEST_STATUS (Request) = STATUS_IO_TIMEOUT; + + InsertTailList( + &Device->WaitingNetbiosFindName, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found find name cached %lx\n", Request)); + + // + // We don't need to reference the cache entry since + // we only use it here with the lock still held. + // + + // + // Query the local address, which we will return as + // the destination address of this query. Since we + // use TempBuffer.IpxAddress for this query, we have + // to immediately copy it to its correct place in + // TempBuffer.FindNameInfo.Buffer. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + &CacheName->Networks[0].LocalTarget.NicHandle, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicHandle.NicId )); + + goto QueryFindNameFailed; + } +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicId, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + RtlMoveMemory (TempBuffer.FindNameInfo.Buffer.destination_addr, TempBuffer.IpxAddress.NodeAddress, 6); + TempBuffer.FindNameInfo.Buffer.access_control = 0x10; // standard token-ring values + TempBuffer.FindNameInfo.Buffer.frame_control = 0x40; + RtlCopyMemory (TempBuffer.FindNameInfo.Buffer.source_addr, CacheName->FirstResponse.NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, CacheName->FirstResponse.NodeAddress, 6); + + QueryStatus = (*Device->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + &CacheName->Networks[0].LocalTarget.NicHandle, +#else + CacheName->Networks[0].LocalTarget.NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(TempBuffer.FindNameInfo.Buffer.routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + TempBuffer.FindNameInfo.Buffer.routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + TempBuffer.FindNameInfo.Buffer.length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + TempBuffer.FindNameInfo.Header.node_count = 1; + TempBuffer.FindNameInfo.Header.reserved = 0; + TempBuffer.FindNameInfo.Header.unique_group = 0; // unique + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.FindNameInfo, + 0, + sizeof(FIND_NAME_HEADER) + 33, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + +#if defined(_PNP_POWER) +QueryFindNameFailed: +#endif _PNP_POWER + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BETABUGBUG: Keep track of more of these. + // + + NB_DEBUG2 (QUERY, ("Query provider statistics\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + NB_DEBUG2 (QUERY, ("Query datagram info\n")); + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + Status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS:{ +#if defined(_PNP_POWER) + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS + ? IPX_QUERY_DATA_LINK_ADDRESS + : IPX_QUERY_NETWORK_ADDRESS ), + NULL, + Request, + 0, + NULL); +#else + ULONG TransportAddressAllocSize; + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + +// TransportAddress = CTEAllocMem(sizeof(int) + (ElementSize * Device->MaximumNicId)); + TransportAddressAllocSize = sizeof(int) + ( ElementSize * Device->MaximumNicId); + TransportAddress = NbiAllocateMemory( TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + if (TransportAddress == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; + + for (i = 1; i <= Device->MaximumNicId; i++) { + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + (USHORT)i, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + if (Status != STATUS_SUCCESS) { + continue; + } + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = TDI_ADDRESS_TYPE_UNSPEC; + RtlCopyMemory (CurAddress->Address, TempBuffer.IpxAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + + Status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + +// CTEFreeMem (TransportAddress); + NbiFreeMemory( TransportAddress, TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + } +#endif _PNP_POWER + break; + } + default: + + NB_DEBUG (QUERY, ("Invalid query type %d\n", Query->QueryType)); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; + +} /* NbiTdiQueryInformation */ + + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ) + +/*++ + +Routine Description: + + This routine allocates an ADAPTER_STATUS buffer and + fills it in. The buffer will be allocated at most + MaximumLength size. The caller is responsible for + freeing the buffer. + +Arguments: + + MaximumLength - The maximum length to allocate. + + NicId - The NIC ID the query was received on, or 0 for + a local query. + + StatusBuffer - Returns the allocated buffer. + + StatusBufferLength - Returns the length of the buffer. + + ValidBufferLength - Returns the length of the buffer which + contains valid adapter status data. + +Return Value: + + STATUS_SUCCESS - The buffer was written successfully. + STATUS_BUFFER_OVERFLOW - The buffer was written but not all + data could fit in MaximumLength bytes. + STATUS_INSUFFICIENT_RESOURCES - The buffer could not be allocated. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus; + PNAME_BUFFER NameBuffer; + ADAPTER_STATUS TempAdapterStatus; +#if !defined(_PNP_POWER) + TDI_ADDRESS_IPX IpxAddress; +#endif !_PNP_POWER + PDEVICE Device = NbiDevice; + PADDRESS Address; + UCHAR NameCount; + ULONG LengthNeeded; + ULONG BytesWritten; + NTSTATUS Status; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + // + // First fill in the basic adapter status structure, to make + // it easier to copy over if the target buffer is really short. + // + + RtlZeroMemory ((PVOID)&TempAdapterStatus, sizeof(ADAPTER_STATUS)); + +#if defined(_PNP_POWER) + RtlCopyMemory (TempAdapterStatus.adapter_address, Device->Bind.Node, 6); +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + RtlCopyMemory (TempAdapterStatus.adapter_address, IpxAddress.NodeAddress, 6); +#endif _PNP_POWER + + + // + // Some of the fields mean different things for Novell Netbios, + // as described in the comments. + // + + TempAdapterStatus.rev_major = 0; // Jumpers + TempAdapterStatus.reserved0 = 0; // SelfTest + TempAdapterStatus.adapter_type = 0; // MajorVersion + TempAdapterStatus.rev_minor = 0; // MinorVersion + + TempAdapterStatus.duration = 0; // ReportingPeriod + TempAdapterStatus.frmr_recv = 0; // ReceiveCRCErrors + TempAdapterStatus.frmr_xmit = 0; // ReceiveAlignErrors + + TempAdapterStatus.iframe_recv_err = 0; // XmitCollisions + TempAdapterStatus.xmit_aborts = 0; // XmitAbort + + TempAdapterStatus.xmit_success = Device->Statistics.DataFramesSent; // SuccessfulXmits + TempAdapterStatus.recv_success = Device->Statistics.DataFramesReceived; // SuccessfulReceive + + TempAdapterStatus.iframe_xmit_err = (WORD)Device->Statistics.DataFramesResent; // XmitRetries + TempAdapterStatus.recv_buff_unavail = (WORD)Device->Statistics.DataFramesRejected; // ExhaustedResource + + // t1_timeouts, ti_timeouts, and reserved1 are unused. + + TempAdapterStatus.free_ncbs = 0xffff; // FreeBlocks + TempAdapterStatus.max_cfg_ncbs = 0xffff; // ConfiguredNCB + TempAdapterStatus.max_ncbs = 0xffff; // MaxNCB + + // xmit_bug_unavail and max_dgram_size are unused. + + TempAdapterStatus.pending_sess = (WORD)Device->Statistics.OpenConnections; // CurrentSessions + TempAdapterStatus.max_cfg_sess = 0xffff; // MaxSessionConfigured + TempAdapterStatus.max_sess = 0xffff; // MaxSessionPossible + TempAdapterStatus.max_sess_pkt_size = + Device->Bind.LineInfo.MaximumSendSize - sizeof(NB_CONNECTION); // MaxSessionPacketSize + + TempAdapterStatus.name_count = 0; + + + // + // Do a quick estimate of how many names we need room for. + // This includes stopping addresses and the broadcast + // address, for the moment. BUGBUG: Fix this? + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LengthNeeded = sizeof(ADAPTER_STATUS) + (Device->AddressCount * sizeof(NAME_BUFFER)); + + if (LengthNeeded > MaximumLength) { + LengthNeeded = MaximumLength; + } + + AdapterStatus = NbiAllocateMemory(LengthNeeded, MEMORY_STATUS, "Adapter Status"); + if (AdapterStatus == NULL) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + *StatusBuffer = AdapterStatus; + *StatusBufferLength = LengthNeeded; + + if (LengthNeeded < sizeof(ADAPTER_STATUS)) { + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, LengthNeeded); + *ValidBufferLength = LengthNeeded; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, sizeof(ADAPTER_STATUS)); + + BytesWritten = sizeof(ADAPTER_STATUS); + NameBuffer = (PNAME_BUFFER)(AdapterStatus+1); + NameCount = 0; + + // + // Scan through the device's address database, filling in + // the NAME_BUFFERs. + // + + Status = STATUS_SUCCESS; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + +#if defined(_PNP_POWER) + if ((Address->State != ADDRESS_STATE_OPEN) || + (Address->Flags & ADDRESS_FLAGS_CONFLICT)) { + continue; + } +#else + if ((Address->State != ADDRESS_STATE_OPEN) != 0) { + continue; + } +#endif _PNP_POWER + + // + // Ignore the broadcast address. + // + + if (Address->NetbiosAddress.Broadcast) { + continue; + } + + // + // Ignore our reserved address. + // +#if defined(_PNP_POWER) + if ( NbiFindAdapterAddress( + Address->NetbiosAddress.NetbiosName, + LOCK_ACQUIRED + )) { + continue; + } +#else + if (RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + Device->ReservedNetbiosName, + 16)) { + continue; + } + +#endif _PNP_POWER + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > LengthNeeded) { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory( + NameBuffer->name, + Address->NetbiosAddress.NetbiosName, + 16); + + ++NameCount; + NameBuffer->name_num = NameCount; + + NameBuffer->name_flags = REGISTERED; + if (Address->NameTypeFlag == NB_NAME_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // + // BUGBUG: name_flags should be done more accurately. + // + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + AdapterStatus->name_count = (WORD)NameCount; + *ValidBufferLength = BytesWritten; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Status; + +} /* NbiStoreAdapterStatus */ + + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ) + +/*++ + +Routine Description: + + This routine updates the find name request with the + new information received. It updates the status in + the request if needed. + +Arguments: + + Request - The netbios find name request. + + NicId - The NIC ID the response was received on. + + RemoteIpxAddress - The IPX address of the remote. + + Unique - TRUE if the name is unique. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + FIND_NAME_BUFFER UNALIGNED * FindNameBuffer; + UINT FindNameBufferLength; + TDI_ADDRESS_IPX LocalIpxAddress; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + NTSTATUS QueryStatus; + UINT i; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // Scan through the names saved so far and see if this one + // is there. + // + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *)(FindNameHeader+1); + + for (i = 0; i < FindNameHeader->node_count; i++) { + + if (RtlEqualMemory( + FindNameBuffer->source_addr, + RemoteIpxAddress->NodeAddress, + 6)) { + + // + // This remote already responded, ignore it. + // + + return; + + } + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *) + (((PUCHAR)FindNameBuffer) + 33); + + } + + // + // Make sure there is room for this new node. 33 is + // sizeof(FIND_NAME_BUFFER) without padding. + // + + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER) + ((FindNameHeader->node_count+1) * 33)) { + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + return; + } + + // + // Query the local address, which we will return as + // the destination address of this query. + // + +#if defined(_PNP_POWER) + if( (*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicHandle, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + // + // Ignore this response if the query fails. maybe the NicHandle + // is bad or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + NicHandle->NicId )); + return; + } +#else + (VOID)(*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + FindNameBuffer->access_control = 0x10; // standard token-ring values + FindNameBuffer->frame_control = 0x40; + RtlMoveMemory (FindNameBuffer->destination_addr, LocalIpxAddress.NodeAddress, 6); + RtlCopyMemory (FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, RemoteIpxAddress->NodeAddress, 6); + + QueryStatus = (*NbiDevice->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + NicHandle, +#else + NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(FindNameBuffer->routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + FindNameBuffer->routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + FindNameBuffer->length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + ++FindNameHeader->node_count; + if (!Unique) { + FindNameHeader->unique_group = 1; // group + } + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + +} /* NbiUpdateNetbiosFindName */ + + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sets the REQUEST_INFORMATION field to the right + value based on the number of responses recorded in the netbios + find name request's buffer. + +Arguments: + + Request - The netbios find name request. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + REQUEST_INFORMATION(Request) = sizeof(FIND_NAME_HEADER) + (FindNameHeader->node_count * 33); + +} /* NbiSetNetbiosFindNameInformation */ + + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* NbiTdiSetInformation */ + + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_QUERY frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LINE_INFO LineInfo; + ULONG ResponseSize; + NTSTATUS Status; + PNDIS_BUFFER AdapterStatusBuffer; + PADAPTER_STATUS AdapterStatus; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + PDEVICE Device = NbiDevice; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + + + // + // The old stack does not include the 14 bytes of padding in + // the 802.3 or IPX length of the packet. + // + + if (PacketSize < (sizeof(IPX_HEADER) + 2)) { + return; + } + + // + // Get the maximum size we can send. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + &RemoteAddress->NicHandle, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL) != STATUS_SUCCESS ) { + // + // Bad NicHandle or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_LINE_INFO, + RemoteAddress->NicHandle.NicId )); + + return; + } + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } +#else + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + // + // Get the maximum size we can send. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + RemoteAddress->NicId, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL); +#endif _PNP_POWER + + ResponseSize = LineInfo.MaximumSendSize - sizeof(IPX_HEADER) - sizeof(NB_STATUS_RESPONSE); + + // + // Get the local adapter status (this allocates a buffer). + // + + Status = NbiStoreAdapterStatus( + ResponseSize, +#if defined(_PNP_POWER) + RemoteAddress->NicHandle.NicId, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &AdapterStatusBuffer, + Device->NdisBufferPoolHandle, + AdapterStatus, + ValidStatusLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + NB_DEBUG2 (QUERY, ("Reply to AdapterStatus from %lx %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(UNALIGNED ULONG *)Connectionless->IpxHeader.SourceNetwork, + Connectionless->IpxHeader.SourceNode[0], + Connectionless->IpxHeader.SourceNode[1], + Connectionless->IpxHeader.SourceNode[2], + Connectionless->IpxHeader.SourceNode[3], + Connectionless->IpxHeader.SourceNode[4], + Connectionless->IpxHeader.SourceNode[5])); + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_RESPONSE; + Reserved->u.SR_AS.ActualBufferLength = AdapterStatusLength; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, Connectionless->IpxHeader.SourceNetwork, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_RESPONSE; + + NbiReferenceDevice (Device, DREF_STATUS_RESPONSE); + + NdisChainBufferAtBack (Packet, AdapterStatusBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + RemoteAddress, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + ValidStatusLength, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends NB_CMD_STATUS_QUERY frames. + +Arguments: + + Request - Holds the request describing the remote adapter + status query. REQUEST_STATUS(Request) points + to the netbios cache entry for the remote name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNETBIOS_CACHE CacheName; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_QUERY; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(Request); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + LocalTarget = &CacheName->Networks[0].LocalTarget; + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_QUERY; + + NbiReferenceDevice (Device, DREF_STATUS_FRAME); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY), + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_RESPONSE frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + PREQUEST AdapterStatusRequest; + PNETBIOS_CACHE CacheName; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNDIS_BUFFER TargetBuffer; + ULONG TargetBufferLength, BytesToTransfer; + ULONG BytesTransferred; + NDIS_STATUS NdisStatus; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + BOOLEAN Found; + PNAME_BUFFER NameBuffer; + UINT i,NameCount = 0; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) { + return; + } + + // + // Find out how many names are there. + // + NameBuffer = (PNAME_BUFFER)(LookaheadBuffer + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)); + if ( LookaheadBufferSize > sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS) ) { + NameCount = (LookaheadBufferSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)) ) / + sizeof(NAME_BUFFER); + } + // + // Find a request queued to this remote. If there are + // multiple requests outstanding for the same name we + // should get multiple responses, so we only need to + // find one. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Found = FALSE; + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if ( CacheName->Unique ) { + if (RtlEqualMemory( + &CacheName->FirstResponse, + Connectionless->IpxHeader.SourceNetwork, + 12)) { + Found = TRUE; + break; + } + } else if ( RtlEqualMemory( CacheName->NetbiosName,NetbiosBroadcastName,16)){ + // + // It's a broadcast name. Any response is fine. + // + Found = TRUE; + break; + } else { + // + // It's group name. Make sure that this remote + // has this group name registered with him. + // + for (i =0;iNetbiosName, + NameBuffer[i].name, + 16)) && + + (NameBuffer[i].name_flags & GROUP_NAME) ) { + + Found = TRUE; + break; + } + } + } + + } + + if (!Found) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (QUERY, ("Got response to AdapterStatus %lx\n", AdapterStatusRequest)); + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + REQUEST_STATUS (AdapterStatusRequest) = STATUS_INSUFFICIENT_RESOURCES; + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->Type = RECEIVE_TYPE_ADAPTER_STATUS; + ReceiveReserved->u.RR_AS.Request = AdapterStatusRequest; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_SUCCESS; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // Now that we have a packet and a buffer, set up the transfer. + // We will complete the request when the transfer completes. + // + + TargetBuffer = REQUEST_NDIS_BUFFER (AdapterStatusRequest); + + NdisChainBufferAtFront (Packet, TargetBuffer); + + NbiGetBufferChainLength (TargetBuffer, &TargetBufferLength); + BytesToTransfer = PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if (TargetBufferLength < BytesToTransfer) { + BytesToTransfer = TargetBufferLength; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_BUFFER_OVERFLOW; + } + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)), + BytesToTransfer, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessStatusResponse */ + diff --git a/private/ntos/tdi/isn/nb/receive.c b/private/ntos/tdi/isn/nb/receive.c new file mode 100644 index 000000000..721209d68 --- /dev/null +++ b/private/ntos/tdi/isn/nb/receive.c @@ -0,0 +1,1307 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains the code to handle receive indication + and posted receives for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This routine is a no-op to put in the NbiCallbacks table so +// we can avoid checking for runt session frames (this is because +// of how the if is structure below). +// + +VOID +NbiProcessSessionRunt( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) +{ + return; +} + +NB_CALLBACK_NO_TRANSFER NbiCallbacksNoTransfer[] = { + NbiProcessFindName, + NbiProcessNameRecognized, + NbiProcessAddName, + NbiProcessAddName, // processes name in use frames also + NbiProcessDeleteName, + NbiProcessSessionRunt, // in case get a short session packet + NbiProcessSessionEnd, + NbiProcessSessionEndAck, + NbiProcessStatusQuery + }; + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiProcessDeathPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + DbgPrint("******Received death packet - connid %x\n",Sess->DestConnectionId); + + if ( !NbiGlobalDebugResTimeout ) { + return; + } + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + DbgPrint("********No Connection found with %x id\n",Sess->DestConnectionId); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + DbgPrint("******Received death packet on conn %lx from <%.16s>\n",Connection,Connection->RemoteName); + DbgBreakPoint(); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } +} +#endif //RSRC_TIMEOUT_DBG + + +BOOLEAN +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterCtx, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl + ) + +/*++ + +Routine Description: + + This routine handles receive indications from IPX. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + TRUE - receivepacket taken, will return later with NdisReturnPacket. + Currently, we always return FALSE. + +--*/ + +{ + PNB_FRAME NbFrame = (PNB_FRAME)LookaheadBuffer; + UCHAR DataStreamType; + + // + // We know that this is a frame with a valid IPX header + // because IPX would not give it to use otherwise. However, + // it does not check the source socket. + // + + if (NbFrame->Connectionless.IpxHeader.SourceSocket != NB_SOCKET) { + return FALSE; + } + + ++NbiDevice->Statistics.PacketsReceived; + + // First assume that the DataStreamType is at the normal place i.e 2nd byte + // + + // Now see if this is a name frame. + // + if ( PacketSize == sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME) ) { + // In the internet mode, the DataStreamType2 becomes DataStreamType + if (NbFrame->Connectionless.IpxHeader.PacketType == 0x14 ) { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType2; + } else { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + } + + // Is this a name frame? + // NB_CMD_FIND_NAME = 1 .... NB_CMD_DELETE_NAME = 5 + // + if ((DataStreamType >= NB_CMD_FIND_NAME) && (DataStreamType <= NB_CMD_DELETE_NAME)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + return FALSE; + } + + } + +#ifdef RSRC_TIMEOUT_DBG + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_DEATH_PACKET)) { + + NbiProcessDeathPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } +#endif //RSRC_TIMEOUT_DBG + + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_SESSION_DATA)) { + + NbiProcessSessionData( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else { + + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + // Handle NB_CMD_SESSION_END = 7 ... NB_CMD_STATUS_QUERY = 9 + // + if ((DataStreamType >= NB_CMD_SESSION_END ) && (DataStreamType <= NB_CMD_STATUS_QUERY)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + + } else if (DataStreamType == NB_CMD_STATUS_RESPONSE) { + + NbiProcessStatusResponse( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else if ((DataStreamType == NB_CMD_DATAGRAM) || + (DataStreamType == NB_CMD_BROADCAST_DATAGRAM)) { + + NbiProcessDatagram( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize, + (BOOLEAN)(DataStreamType == NB_CMD_BROADCAST_DATAGRAM)); + + } + + } + + return FALSE; +} /* NbiReceive */ + + +VOID +NbiReceiveComplete( + IN USHORT NicId + ) + +/*++ + +Routine Description: + + This routine handles receive complete indications from IPX. + +Arguments: + + NicId - The NIC ID on which a receive was previously indicated. + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS Address; + PREQUEST Request; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PDEVICE Device = NbiDevice; + LIST_ENTRY LocalList; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + // + // Complete any pending receive requests. + // + + + if (!IsListEmpty (&Device->ReceiveCompletionQueue)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveCompletionQueue, p)) { + + Request = LIST_ENTRY_TO_REQUEST (p); + + // + // BUGBUG: Cache the connection somewhere easier + // to retrieve? + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + } + + } + + + // + // Indicate any datagrams to clients. + // + + if (!IsListEmpty (&Device->ReceiveDatagrams)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveDatagrams, p)) { + + ReceiveBuffer = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER, WaitLinkage); + Address = ReceiveBuffer->Address; + + NbiIndicateDatagram( + Address, + ReceiveBuffer->RemoteName, + ReceiveBuffer->Data, + ReceiveBuffer->DataLength); + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + } + } + + + // + // Start packetizing connections. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Check again because it may just have become + // empty, and the code below depends on it being + // non-empty. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + // + // We copy the list locally, in case someone gets + // put back on it. We have to hack the end so + // it points to LocalList instead of PacketizeConnections. + // + + LocalList = Device->PacketizeConnections; + LocalList.Flink->Blink = &LocalList; + LocalList.Blink->Flink = &LocalList; + + InitializeListHead (&Device->PacketizeConnections); + + // + // Set all these connections to not be on the list, so + // NbiStopConnection won't try to take them off. + // + + for (p = LocalList.Flink; p != &LocalList; p = p->Flink) { + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + CTEAssert (Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = FALSE; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + while (TRUE) { + + p = RemoveHeadList (&LocalList); + if (p == &LocalList) { + break; + } + + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + } + } + +} /* NbiReceiveComplete */ + + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine handles a transfer data complete indication from + IPX, indicating that a previously issued NdisTransferData + call has completed. + +Arguments: + + Packet - The packet associated with the transfer. + + Status - The status of the transfer. + + BytesTransferred - The number of bytes transferred. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PADDRESS Address; + PCONNECTION Connection; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PREQUEST AdapterStatusRequest; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + ReceiveReserved = (PNB_RECEIVE_RESERVED)(Packet->ProtocolReserved); + + switch (ReceiveReserved->Type) { + + case RECEIVE_TYPE_DATA: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + Connection = ReceiveReserved->u.RR_CO.Connection; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Status != NDIS_STATUS_SUCCESS) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive = Connection->PreviousReceive; + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // BUGBUG: Send a resend ack? + // + + } else { + + // + // This aborts the current receive and + // releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } else { + + + Connection->CurrentReceive.Offset += BytesTransferred; + Connection->CurrentReceive.MessageOffset += BytesTransferred; + + if (ReceiveReserved->u.RR_CO.CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (ReceiveReserved->u.RR_CO.EndOfMessage) { + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (ReceiveReserved->u.RR_CO.PartialReceive) { + Connection->CurrentIndicateOffset += BytesTransferred; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + } + + // + // Free the NDIS buffer chain if we allocated one. + // + + if (!ReceiveReserved->u.RR_CO.NoNdisBuffer) { + + NdisQueryPacket (Packet, NULL, NULL, &CurBuffer, NULL); + + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + break; + + case RECEIVE_TYPE_DATAGRAM: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + ReceiveBuffer = ReceiveReserved->u.RR_DG.ReceiveBuffer; + + // + // Free the packet used for the transfer. + // + + ReceiveReserved->u.RR_DG.ReceiveBuffer = NULL; + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // If it succeeded then queue it for indication, + // otherwise free the receive buffer also. + // + + if (Status == STATUS_SUCCESS) { + + ReceiveBuffer->DataLength = BytesTransferred; + NB_INSERT_HEAD_LIST( + &Device->ReceiveDatagrams, + &ReceiveBuffer->WaitLinkage, + &Device->Lock); + + } else { + + Address = ReceiveBuffer->Address; + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + break; + + case RECEIVE_TYPE_ADAPTER_STATUS: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + AdapterStatusRequest = ReceiveReserved->u.RR_AS.Request; + + // + // Free the packet used for the transfer. + // + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // Complete the request. + // + + if (Status == STATUS_SUCCESS) { + + // + // REQUEST_STATUS() is already to set to SUCCESS or + // BUFFER_OVERFLOW based on whether the buffer was + // big enough. + // + + REQUEST_INFORMATION(AdapterStatusRequest) = BytesTransferred; + + } else { + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_UNEXPECTED_NETWORK_ERROR; + + } + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + break; + + } + +} /* NbiTransferDataComplete */ + + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when a receive needs to be acked to + the remote. It either sends a data ack or queues up a piggyback + ack request. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - Pointer to the connection. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + + if (Connection->NewNetbios) { + + // + // CurrentReceiveNoPiggyback is based on the bits he + // set in his frame, NoPiggybackHeuristic is based on + // guesses about the traffic pattern, it is set to + // TRUE if we think we should not piggyback. + // + + if ((!Device->EnablePiggyBackAck) || + (Connection->CurrentReceiveNoPiggyback) || + (Connection->PiggybackAckTimeout) || + (Connection->NoPiggybackHeuristic)) { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (!Connection->DataAckPending) { + + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + // + // Some stacks can have multiple messages + // outstanding, so we may already have an + // ack queued. + // + + Connection->DataAckTimeouts = 0; + Connection->DataAckPending = TRUE; + + ++Device->Statistics.PiggybackAckQueued; + + if (!Connection->OnDataAckQueue) { + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle1); + + if (!Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = TRUE; + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + } + + if (!Device->DataAckActive) { + NbiStartShortTimer (Device); + Device->DataAckActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle1); + } + + // + // Clear this, since a message ack resets the count. + // + + Connection->ReceivesWithoutAck = 0; + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + +} + + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have filled up a receive request + and need to complete it. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + + THIS ROUTINE ALSO HOLDS CANCEL SPIN LOCK WHEN IT IS CALLED + AND RELEASES IT WHEN IT RETURNS. +Arguments: + + Connection - Pointer to the connection. + + EndOfMessage - BOOLEAN set to true if the message end was received. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PDEVICE Device = NbiDevice; + + // + // Complete the current receive request. If the connection + // has shut down then we complete it right here, otherwise + // we queue it for completion in the receive complete + // handler. + // + + Request = Connection->ReceiveRequest; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveRequest = NULL; // StopConnection won't do this + + REQUEST_STATUS(Request) = Connection->Status; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + ++Connection->ConnectionInfo.ReceiveErrors; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } else { + + REQUEST_INFORMATION (Request) = Connection->CurrentReceive.Offset; + + if (EndOfMessage) { + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveUnaccepted) { + NB_DEBUG2 (RECEIVE, ("Moving Unaccepted %d down by %d\n", + Connection->ReceiveUnaccepted, Connection->CurrentReceive.Offset)); + if (Connection->CurrentReceive.Offset >= Connection->ReceiveUnaccepted) { + Connection->ReceiveUnaccepted = 0; + } else { + Connection->ReceiveUnaccepted -= Connection->CurrentReceive.Offset; + } + } + + // + // BUGBUG: Check whether to activate another receive? + // + + Connection->ReceiveState = CONNECTION_RECEIVE_PENDING; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + if (EndOfMessage) { + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (Connection->CurrentIndicateOffset != 0) { + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + } + + } else { + + NbiSendDataAck( + Connection, + EndOfMessage ? NbiAckResponse : NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // This will complete the request inside ReceiveComplete, + // dereference the connection, and set the state to IDLE. + // + + NB_INSERT_TAIL_LIST( + &Device->ReceiveCompletionQueue, + REQUEST_LINKAGE (Request), + &Device->Lock); + + } + +} /* NbiCompleteReceive */ + + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a receive on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the receive. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PCONNECTION Connection; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelReceive); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + // + // Insert this in our queue, then see if we need + // to wake up the remote. + // + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->ReceiveQueue, Request); + + if (Connection->ReceiveState != CONNECTION_RECEIVE_W_RCV) { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx idle\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx awakened\n", Request, Connection)); + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = (USHORT) + (Connection->ReceiveSequence + Connection->ReceiveWindowSize - 1); + + } + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (RECEIVE, ("Receive connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiReceive */ + + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The request is found on the connection's receive queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + + // + // Just stop the connection, that will tear down any + // receives. + // + // BUGBUG: Do we care about cancelling non-active + // receives without stopping the connection?? + // + // BUGBUG: This routine is the same as NbiCancelSend, + // so if we don't make it more specific, merge the two. + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelReceive */ + diff --git a/private/ntos/tdi/isn/nb/send.c b/private/ntos/tdi/isn/nb/send.c new file mode 100644 index 000000000..a4443c73c --- /dev/null +++ b/private/ntos/tdi/isn/nb/send.c @@ -0,0 +1,2886 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains the send routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +) + +/*++ + +Routine Description: + + This routine handles a send completion call from IPX. + +Arguments: + + Packet - The packet which has been completed. + + Status - The status of the send. + +Return Value: + + None. + +--*/ + + + +{ + PDEVICE Device = NbiDevice; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST DatagramRequest; + PREQUEST SendRequest, TmpRequest; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PNETBIOS_CACHE CacheName; + PNDIS_BUFFER SecondBuffer; + PVOID SecondBufferMemory; + UINT SecondBufferLength; + ULONG oldvalue; + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + CTELockHandle CancelLH; +#if defined(_PNP_POWER) + CTELockHandle LockHandle; +#endif _PNP_POWER + + // + // We jump back here if we re-call send from inside this + // function and it doesn't pend (to avoid stack overflow). + // + +FunctionStart:; + + ++Device->Statistics.PacketsSent; + + switch (Reserved->Type) { + + case SEND_TYPE_SESSION_DATA: + + // + // This was a send on a session. This references the + // IRP. + // + + NB_DEBUG2 (SEND, ("Complete NDIS packet %lx\n", Reserved)); + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + SendRequest = Reserved->u.SR_CO.Request; + + if (!Reserved->u.SR_CO.NoNdisBuffer) { + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + // + // If NoNdisBuffer is TRUE, then we could set + // Connection->SendBufferInUse to FALSE here. The + // problem is that a new send might be in progress + // by the time this completes and it may have + // used the user buffer, then if we need to + // retransmit that packet we would use the buffer + // twice. We instead rely on the fact that whenever + // we make a new send active we set SendBufferInUse + // to FALSE. The net effect is that the user's buffer + // can be used the first time a send is packetize + // but not on resends. + // + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + +#if DBG + if (REQUEST_REFCOUNT(SendRequest) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, SendRequest); + DbgBreakPoint(); + } +#endif + +#if defined(__PNP) + NB_GET_LOCK( &Connection->Lock, &LockHandle ); + oldvalue = REQUEST_REFCOUNT(SendRequest)--; + if ( DEVICE_NETWORK_PATH_NOT_FOUND == Status ) { + Connection->LocalTarget = Reserved->LocalTarget; + } + NB_FREE_LOCK( &Connection->Lock, LockHandle ); +#else + oldvalue = NB_ADD_ULONG( + &REQUEST_REFCOUNT (SendRequest), + (ULONG)-1, + &Connection->Lock); +#endif __PNP + + if (oldvalue == 1) { + + // + // If the refcount on this request is now zero then + // we already got the ack for it, which means + // that the ack-processing code has unlinked the + // request from Connection->SendQueue. So we + // can just run the queue of connections here + // and complete them. + // + // We dereference the connection for all but one + // of the requests, we hang on to that until a bit + // later so everything stays around. + // + + while (TRUE) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + NB_DEBUG2 (SEND, ("Completing request %lx from send complete\n", SendRequest)); + REQUEST_STATUS (SendRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + ++Connection->ConnectionInfo.TransmittedTsdus; + SendRequest = TmpRequest; + + if (SendRequest == NULL) { + break; + } + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } + + if (Reserved->OwnedByConnection) { + + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (oldvalue == 1) { + NbiDereferenceConnection (Connection, CREF_SEND); + } + + break; + + case SEND_TYPE_NAME_FRAME: + + // + // The frame is an add name/delete name; put it back in + // the pool and deref the address. + // + + CTEAssert (Reserved->SendInProgress); + + Address = Reserved->u.SR_NF.Address; + +#if !defined(_PNP_POWER) + if ((Reserved->u.SR_NF.CurrentNicId) && + (Reserved->u.SR_NF.CurrentNicId < Device->MaximumNicId)) { + + NB_CONNECTIONLESS UNALIGNED * Header; + IPX_LOCAL_TARGET TempLocalTarget; + + // + // This is a name frame being sent to every address, so + // resent it to the next NIC ID. We hold the address + // reference through this send. + // + + CTEAssert (Address != NULL); + + ++Reserved->u.SR_NF.CurrentNicId; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = Reserved->u.SR_NF.DataStreamType; + Header->NameFrame.NameTypeFlag = Reserved->u.SR_NF.NameTypeFlag; + + // + // This is not a name in use frame so DataStreamType2 + // is the same as DataStreamType. + // + + Header->NameFrame.DataStreamType2 = Reserved->u.SR_NF.DataStreamType; + + RtlCopyMemory( + Header->NameFrame.Name, + Address->NetbiosAddress.NetbiosName, + 16); + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + TempLocalTarget.NicId = Reserved->u.SR_NF.CurrentNicId; + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)); + if ((Status = + (*Device->Bind.SendHandler)( + &TempLocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + goto FunctionStart; + + } + + return; + + } +#endif !_PNP_POWER + + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + if (Address) { + NbiDereferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_NAME_FRAME); + } + + break; + + case SEND_TYPE_SESSION_INIT: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT)); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_SESSION_INIT); + + break; + + case SEND_TYPE_SESSION_NO_DATA: + + // + // This is a frame which was sent on a connection but + // has no data (ack, session end, session end ack). + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + + if (Reserved->OwnedByConnection) { + + CTEAssert (Connection != NULL); + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (Connection != NULL) { + NbiDereferenceConnection (Connection, CREF_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_FRAME); + } + + break; + + case SEND_TYPE_FIND_NAME: + + // + // The frame is a find name; just set SendInProgress to + // FALSE and FindNameTimeout will clean it up. + // +#if defined(_PNP_POWER) + NB_GET_LOCK( &Device->Lock, &LockHandle); + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + if ( STATUS_SUCCESS == Status ) { + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } else { + NB_DEBUG( CACHE, ("Send complete of find name with failure %lx\n",Status )); + } + NB_FREE_LOCK(&Device->Lock, LockHandle); +#else + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +#endif _PNP_POWER + break; + + case SEND_TYPE_DATAGRAM: + + // + // If there are any more networks to send this on then + // do so, otherwise put it back in the pool and complete + // the request. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + if ((Reserved->u.SR_DG.Cache == NULL) || + (++Reserved->u.SR_DG.CurrentNetwork >= + Reserved->u.SR_DG.Cache->NetworksUsed)) { + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Completing datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // Remove any user buffers chained on this packet. + // + + NdisReinitializePacket (Packet); + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisChainBufferAtFront (Packet, Reserved->HeaderBuffer); + + // + // Complete the request. + // + + REQUEST_STATUS(DatagramRequest) = Status; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + CacheName = Reserved->u.SR_DG.Cache; + + NbiPushSendPacket (Reserved); + + // + // Since we are no longer referencing the cache + // name, see if we should delete it (this will + // happen if the cache entry was aged out while + // the datagram was being processed). + // + + if (CacheName != NULL) { + + oldvalue = NB_ADD_ULONG( + &CacheName->ReferenceCount, + (ULONG)-1, + &Device->Lock); + + if (oldvalue == 1) { + + NB_DEBUG2 (CACHE, ("Free aged cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free old cache"); + + } + } + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + } else { + + NB_CONNECTIONLESS UNALIGNED * Header; + PIPX_LOCAL_TARGET LocalTarget; + ULONG HeaderLength; + ULONG PacketLength; + + // send the datagram on the next net. + CTEAssert (!Reserved->u.SR_DG.Cache->Unique); + Reserved->SendInProgress = TRUE; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + if ((Status = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + goto FunctionStart; + } + + } + + break; + + case SEND_TYPE_STATUS_QUERY: + + // + // This is an adapter status query, which is a simple + // packet. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_FRAME); + + break; + + case SEND_TYPE_STATUS_RESPONSE: + + // + // This is an adapter status response, we have to free the + // second buffer. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS, "Adapter Status"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_RESPONSE); + + break; + +#ifdef RSRC_TIMEOUT_DBG + case SEND_TYPE_DEATH_PACKET: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + DbgPrint("********Death packet send completed status %lx\n",Status); + DbgBreakPoint(); + break; +#endif //RSRC_TIMEOUT_DBG + + default: + + CTEAssert (FALSE); + break; + + } + +} /* NbiSendComplete */ + +#if 0 +ULONG NbiLoudSendQueue = 1; +#endif + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of the lower-level + send handler; after assigning the receive sequence number it locks out + other sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD, AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection the send is on. + + Packet - The packet to send. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNB_SEND_RESERVED Reserved; + PLIST_ENTRY p; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + BOOLEAN NdisSendReference; + ULONG result; + + + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + CTEAssert (Connection->State == CONNECTION_STATE_ACTIVE); + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Connection->NdisSendsInProgress > 0) { + + NB_DEBUG2 (SEND, ("Queueing send packet %lx on %lx\n", Reserved, Connection)); + InsertTailList (&Connection->NdisSendQueue, &Reserved->WaitLinkage); + ++Connection->NdisSendsInProgress; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence fields in the packet. + // + + Connection->NdisSendsInProgress = 1; + NdisSendReference = FALSE; + Connection->NdisSendReference = &NdisSendReference; + + while (TRUE) { + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; + } + + // + // Since we are acking as much as we know, we can clear + // this flag. The connection will eventually get removed + // from the queue by the long timeout. + // + + Connection->DataAckPending = FALSE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + NdisStatus = (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + Reserved->u.SR_CO.PacketLength, + sizeof(NB_CONNECTION)); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = NB_ADD_ULONG( + &Connection->NdisSendsInProgress, + (ULONG)-1, + &Connection->Lock); + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. If the connection was stopped while we were sending, + // a special reference was added which we remove (NbiStopConnection + // sets NdisSendReference to TRUE, using the pointer saved + // in Connection->NdisSendReference). + // + + if (result == 1) { + if (NdisSendReference) { + NB_DEBUG2 (SEND, ("Remove CREF_NDIS_SEND from %lx\n", Connection)); + NbiDereferenceConnection (Connection, CREF_NDIS_SEND); + } + return; + } + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + p = RemoveHeadList(&Connection->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Connection->NdisSendQueue); + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } // while loop + + // + // We should never reach here. + // + + CTEAssert (FALSE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiAssignSequenceAndSend */ + + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + PTDI_REQUEST_KERNEL_SEND Parameters; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + + // + // For old netbios, don't allow sends greater than 64K-1. + // + + if ((Connection->NewNetbios) || + (Parameters->SendLength <= 0xffff)) { + + IoSetCancelRoutine (Request, NbiCancelSend); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_INFORMATION (Request) = Parameters->SendLength; // assume it succeeds. + + REQUEST_REFCOUNT (Request) = 1; // refcount starts at 1. + NbiReferenceConnectionSync (Connection, CREF_SEND); + + // + // NOTE: The connection send queue is managed such + // that the current send being packetized is not on + // the queue. For multiple-request messages, the + // first one is not on the queue, but its linkage + // field points to the next request in the message + // (which will be on the head of the queue). + // + + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a final send. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx idle\n", Request, Connection)); + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have been collecting partial sends waiting + // for a final one, which we have now received, + // so start packetizing. + // + // We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx got eor\n", Request, Connection)); + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + + Connection->UnAckedSend = Connection->CurrentSend; +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + // + // The state is PACKETIZE, W_ACK, or W_PACKET. + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx busy\n", Request, Connection)); + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // This is a partial send. We queue them up without + // packetizing until we get a final (this is because + // we have to put a correct Connection->CurrentMessageLength + // in the frames. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + // + // Start collecting partial sends. NOTE: Partial sends + // are always inserted in the send queue + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have got another partial send to add to our + // list. We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } else { + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, too long for connection %lx (%d)\n", Request, Connection, Parameters->SendLength)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_PARAMETER; + + } + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiSend */ + + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + PNB_SEND_RESERVED Reserved; + PDEVICE Device = NbiDevice; + NB_CONNECTION UNALIGNED * Header; + ULONG PacketLength; + ULONG PacketSize; + ULONG DesiredLength; + ULONG ActualLength; + NTSTATUS Status; + PSINGLE_LIST_ENTRY s; + USHORT ThisSendSequence; + USHORT ThisOffset; + BOOLEAN ExitAfterSend; + UCHAR ConnectionControlFlag; + CTELockHandle DeviceLockHandle; + + // + // We jump back here if we are talking new Netbios and it + // is OK to packetize another send. + // + +SendAnotherPacket: + + // + // If we decide to packetize another send after this, we + // change ExitAfterSend to FALSE and SubState to PACKETIZE. + // Right now we don't change SubState in case it is W_PACKET. + // + + ExitAfterSend = TRUE; + + CTEAssert (Connection->CurrentSend.Request != NULL); + + if (Connection->NewNetbios) { + + // + // Check that we have send window, both that advertised + // by the remote and our own locally-decided window which + // may be smaller. + // + + if (((USHORT)(Connection->CurrentSend.SendSequence-1) == Connection->RemoteRcvSequenceMax) || + (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize)) { + + // + // Keep track of whether we are waiting because of his window + // or because of our local window. If it is because of our local + // window then we may want to adjust it after this window + // is acked. + // + + if ((USHORT)(Connection->CurrentSend.SendSequence-1) != Connection->RemoteRcvSequenceMax) { + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + NB_DEBUG2 (SEND, ("Connection %lx local shut down at %lx, %lx\n", Connection, Connection->CurrentSend.SendSequence, Connection->UnAckedSend.SendSequence)); + } else { + Connection->SubState = CONNECTION_SUBSTATE_A_REMOTE_W; + NB_DEBUG2 (SEND, ("Connection %lx remote shut down at %lx\n", Connection, Connection->CurrentSend.SendSequence)); + } + + // + // Start the timer so we will keep bugging him about + // this. BUGBUG: What if he doesn't get a receive down + // quickly -- but this is better than losing his ack + // and then dying. We won't really back off our timer + // because we will keep getting acks, and resetting it. + // + + NbiStartRetransmit (Connection); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + + } + + Request = Connection->CurrentSend.Request; + + // + // If we are in this routine then we know that + // we are coming out of IDLE, W_ACK, or W_PACKET + // and we still have the lock held. We also know + // that there is a send request in progress. If + // an ack for none or part of the last packet was + // received, then our send pointers have been + // adjusted to reflect that. + // + + // + // First get a packet for the current send. + // + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s == NULL) { + + // + // This function tries to allocate another packet pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + if (s == NULL) { + + // + // It is possible to come in here and already be in + // W_PACKET state -- this is because we may packetize + // when in that state, and rather than always be + // checking that we weren't in W_PACKET, we go + // ahead and check again here. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PACKET) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_PACKET; + + NB_GET_LOCK (&Device->Lock, &DeviceLockHandle); + if (!Connection->OnWaitPacketQueue) { + + NbiReferenceConnectionLock (Connection, CREF_W_PACKET); + + Connection->OnWaitPacketQueue = TRUE; + + InsertTailList( + &Device->WaitPacketConnections, + &Connection->WaitPacketLinkage + ); + +// NB_INSERT_TAIL_LIST( +// &Device->WaitPacketConnections, +// &Connection->WaitPacketLinkage, +// &Device->Lock); + + } + NB_FREE_LOCK (&Device->Lock, DeviceLockHandle); + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + // + // Set this now, we will change it later if needed. + // + + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + + + // + // Save these since they go in this next packet. + // + + ThisSendSequence = Connection->CurrentSend.SendSequence; + ThisOffset = (USHORT)Connection->CurrentSend.MessageOffset; + + + // + // Now see if we need to copy the buffer chain. + // + + PacketSize = Connection->MaximumPacketSize; + + if (Connection->CurrentSend.MessageOffset + PacketSize >= Connection->CurrentMessageLength) { + + PacketSize = Connection->CurrentMessageLength - Connection->CurrentSend.MessageOffset; + + if ((Connection->CurrentSend.MessageOffset == 0) && + (!Connection->SendBufferInUse)) { + + // + // If the entire send remaining fits in one packet, + // and this is also the first packet in the send, + // then the entire send fits in one packet and + // we don't need to build a duplicate buffer chain. + // + + BufferChain = Connection->CurrentSend.Buffer; + Reserved->u.SR_CO.NoNdisBuffer = TRUE; + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + Connection->CurrentSend.MessageOffset = Connection->CurrentMessageLength; + Connection->CurrentSend.Request = NULL; + ++Connection->CurrentSend.SendSequence; + Connection->SendBufferInUse = TRUE; + if (Connection->NewNetbios) { + if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + } else { + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + } else { + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + + if (BufferChain != NULL) { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), user buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + NdisChainBufferAtBack (Packet, BufferChain); + } else { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + } + + goto GotBufferChain; + + } + + } + + // + // We need to build a partial buffer chain. In the case + // where the current request is a partial one, we may + // build this from the ndis buffer chains of several + // requests. + // + + if (PacketSize > 0) { + + DesiredLength = PacketSize; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), allocate buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + while (TRUE) { + + Status = NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentSend.Buffer, + Connection->CurrentSend.BufferOffset, + DesiredLength, + &BufferChain, + &Connection->CurrentSend.Buffer, + &Connection->CurrentSend.BufferOffset, + &ActualLength); + + if (Status != STATUS_SUCCESS) { + + PNDIS_BUFFER CurBuffer, TmpBuffer; + + NB_DEBUG2 (SEND, ("Allocate buffer chain failed for packet %lx\n", Reserved)); + + // + // We could not allocate resources for this send. + // We'll put the connection on the packetize + // queue and hope we get more resources later. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + // + // Connection->CurrentSend can stay where it is. + // + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Free any buffers we have allocated on previous calls + // to BuildBufferChain inside this same while(TRUE) loop, + // then free the packet. + // + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + + if (Reserved->OwnedByConnection) { + Connection->SendPacketInUse = FALSE; + } else { + NbiPushSendPacket(Reserved); + } + + return; + + } + + NdisChainBufferAtBack (Packet, BufferChain); + Connection->CurrentSend.MessageOffset += ActualLength; + + DesiredLength -= ActualLength; + + if (DesiredLength == 0) { + + // + // We have gotten enough data for our packet. + // + + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + Connection->CurrentSend.Request = NULL; + } + break; + } + + // + // We ran out of buffer chain on this send, which means + // that we must have another one behind it (since we + // don't start packetizing partial sends until all of + // them are queued). + // + + Request = REQUEST_SINGLE_LINKAGE(Request); + if (Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + + } + + } else { + + // + // This is a zero-length send (in general we will go + // through the code before the if that uses the user's + // buffer, but not on a resend). + // + + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + CTEAssert (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength); + Connection->CurrentSend.Request = NULL; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no alloc buf\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + } + + Reserved->u.SR_CO.NoNdisBuffer = FALSE; + + if (Connection->NewNetbios) { + + ++Connection->CurrentSend.SendSequence; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + + if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + + } else if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else if (ThisSendSequence == Connection->RemoteRcvSequenceMax) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = 0; + ExitAfterSend = FALSE; + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } + + } else { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + ++Connection->CurrentSend.SendSequence; + } + } + +GotBufferChain: + + // + // We have a packet and a buffer chain, there are + // no other resources required for a send so we can + // fill in the header and go. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.Request = Connection->FirstMessageRequest; + + PacketLength = PacketSize + sizeof(NB_CONNECTION); + Reserved->u.SR_CO.PacketLength = PacketLength; + + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. BUGBUG: Put this in + // a contiguous buffer in the connection. + // + + Header->Session.ConnectionControlFlag = ConnectionControlFlag; + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = ThisSendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentMessageLength; + Header->Session.Offset = ThisOffset; + Header->Session.DataLength = (USHORT)PacketSize; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + // + // Reference the request to account for this send. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + ++REQUEST_REFCOUNT (Request); + + ++Device->TempFramesSent; + Device->TempFrameBytesSent += PacketSize; + + // + // Start the timer. + // + + NbiStartRetransmit (Connection); + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + + if (!ExitAfterSend) { + + // + // BUGBUG: Did we need to reference the connection until we + // get the lock back?? + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + // + // Jump back to the beginning of the function to + // repacketize. + + goto SendAnotherPacket; + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + +} /* NbiPacketizeSend */ + + +VOID +NbiAdjustSendWindow( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine adjusts a connection's send window if needed. It is + assumed that we just got an ack for a full send window. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + + if (Connection->RetransmitThisWindow) { + + // + // Move it down. Check if this keeps happening. + // + + if (Connection->SendWindowSize > 2) { + --Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lower window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + if (Connection->SendWindowIncrease) { + + // + // We just increased the window. + // + + ++Connection->IncreaseWindowFailures; + NB_DEBUG2 (SEND_WINDOW, ("%d consecutive increase failues on %lx (%lx)\n", Connection->IncreaseWindowFailures, Connection, Connection->CurrentSend.SendSequence)); + + if (Connection->IncreaseWindowFailures >= 2) { + + if (Connection->MaxSendWindowSize > 2) { + + // + // Lock ourselves at a smaller window. + // + + Connection->MaxSendWindowSize = Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lock send window at %d on %lx (%lx)\n", Connection->MaxSendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + Connection->IncreaseWindowFailures = 0; + } + + Connection->SendWindowIncrease = FALSE; + } + + } else { + + // + // Increase it if allowed, and make a note + // in case this increase causes problems in + // the next window. + // + + if (Connection->SendWindowSize < Connection->MaxSendWindowSize) { + + ++Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Raise window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + Connection->SendWindowIncrease = TRUE; + + } else { + + if (Connection->SendWindowIncrease) { + + // + // We just increased it and nothing failed, + // which is good. + // + + Connection->SendWindowIncrease = FALSE; + Connection->IncreaseWindowFailures = 0; + NB_DEBUG2 (SEND_WINDOW, ("Raised window OK on %lx (%lx)\n", Connection, Connection->CurrentSend.SendSequence)); + } + } + } + + + // + // This controls when we'll check this again. + // + + Connection->SendWindowSequenceLimit += Connection->SendWindowSize; + +} /* NbiAdjustSendWindow */ + + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have gotten an ack + for some data. It completes any sends that have + been acked, and if needed modifies the current send + pointer and queues the connection for repacketizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + ReceiveSequence - The receive sequence from the remote. + + BytesReceived - The number of bytes received in this message. + + Resend - If it is OK to resend based on this packet. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + PREQUEST RequestToComplete; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + + + // + // BUGBUG: We should change to stop the timer + // only if we go idle, since otherwise we still + // want it running, or will restart it when we + // packetize. + // + + // + // See how much is acked here. + // + + if ((Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) && + (ReceiveSequence == (USHORT)(Connection->CurrentSend.SendSequence)) && + (Connection->FirstMessageRequest != NULL)) { + + // Special check for 0 length send which was not accepted by the remote. + // In this case it will pass the above 3 conditions yet, nothing + // is acked. BUG#10395 + if (!Connection->CurrentSend.MessageOffset && Connection->CurrentSend.SendSequence == Connection->UnAckedSend.SendSequence ) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // This acks the entire message. + // + + NB_DEBUG2 (SEND, ("Got ack for entire message on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + Connection->CurrentSend.MessageOffset = 0; // BUGBUG: Needed? + Connection->UnAckedSend.MessageOffset = 0; + + // + // We don't adjust the send window since we likely stopped + // packetizing before we hit it. + // + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + if (Connection->NewNetbios) { + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + + } + + Connection->RetransmitThisWindow = FALSE; + + Request = Connection->FirstMessageRequest; + + // + // We dequeue these requests from the connection's + // send queue. + // + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + RequestToComplete = Request; + + } else { + + // + // There are still sends pending, this will get + // completed when the last send completes. Since + // we have already unlinked the request from the + // connection's send queue we can do this without + // any locks. + // + + RequestToComplete = NULL; + + } + + // + // Now see if there is a send to activate. + // + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Now complete any requests we need to. + // + + while (RequestToComplete != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (RequestToComplete); + REQUEST_STATUS (RequestToComplete) = STATUS_SUCCESS; + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + ++Connection->ConnectionInfo.TransmittedTsdus; + RequestToComplete = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } else if ((ReceiveSequence == Connection->CurrentSend.SendSequence) && + (Connection->NewNetbios || (BytesReceived == Connection->CurrentSend.MessageOffset)) && + (Connection->CurrentSend.Request != NULL)) { + + // + // This acks whatever we sent last time, and we are + // not done packetizing this send, so we can repacketize. + // BUGBUG: With SendSequence changing as it does now, + // don't need the CurrentSend.Request check??? + // + + NB_DEBUG2 (SEND, ("Got full ack on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + if (Connection->NewNetbios) { + + // + // If we are waiting for a window, and this does not open it + // anymore, then we don't reset our timers/retries. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) { + + if (Connection->RemoteRcvSequenceMax != BytesReceived) { + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + } + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + Connection->RetransmitThisWindow = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + // + // We may be on if this ack is duplicated. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if( Connection->FirstMessageRequest ) { + + // + // This acked part of the current send. If the + // remote is requesting a resend then we advance + // the current send location by the amount + // acked and resend from there. If he does + // not want a resend, just ignore this. + // + // We repacketize immediately because we have + // backed up the pointer, and this would + // cause us to ignore an ack for the amount + // sent. Since we don't release the lock + // until we have packetized, the current + // pointer will be advanced past there. + // + // BUGBUG: If he is acking more than we sent, we + // ignore this -- the remote is confused and there + // is nothing much we can do. + // + + if (Resend) { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RetransmitThisWindow = TRUE; + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else if (!Connection->NewNetbios && + ((ReceiveSequence == Connection->UnAckedSend.SendSequence) && + (BytesReceived <= Connection->CurrentSend.MessageOffset))) { + + ULONG BytesAcked = + BytesReceived - Connection->UnAckedSend.MessageOffset; + + // + // Old netbios. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedByBytes( + Connection, + BytesAcked); + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, reset the retry count + // + if ( BytesAcked ) { + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. We advance + // the back of our send window, but we don't repacketize. + // + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if ((Connection->CurrentSend.Request != NULL) && + (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } + } + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +} /* NbiReframeConnection */ + + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when have gotten an ack for + a full message, or received a response to a watchdog + probe, and need to check if the connection should + start packetizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + ULONG TempCount; + PTDI_REQUEST_KERNEL_SEND Parameters; + PDEVICE Device = NbiDevice; + + // + // See if there is a send to activate. + // + + if (Connection->SendQueue.Head != NULL) { + + // + // Take the first send off the queue and make + // it current. + // + + Request = Connection->SendQueue.Head; + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Request); + + // + // BUGBUG: Cache the information about being EOM + // in a more easily accessible location? + // + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a one-request message. + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } else { + + // + // This is a multiple-request message. We scan + // to see if we have the end of message received + // yet. + // + + TempCount = Parameters->SendLength; + TmpRequest = Request; + Request = REQUEST_SINGLE_LINKAGE(Request); + + while (Request != NULL) { + + TempCount += Parameters->SendLength; + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + Connection->CurrentSend.Request = TmpRequest; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (TmpRequest); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = TmpRequest; + Connection->LastMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = TempCount; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + break; + + } + + Request = REQUEST_SINGLE_LINKAGE(Request); + + } + + if (Request == NULL) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } + + } + + } else { + + Connection->FirstMessageRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + + NbiStartWatchdog (Connection); + + } + +} /* NbiRestartConnection */ + + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer by the specified number of bytes. It + assumes that there are enough send requests to + handle the number specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + + BytesAcked - The number of bytes acked. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + ULONG CurSendBufferLength; + ULONG BytesLeft = BytesAcked; + ULONG TempBytes; + + while (BytesLeft > 0) { + + NdisQueryBuffer (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength); + + // + // See if bytes acked ends within the current buffer. + // + + if (Connection->UnAckedSend.BufferOffset + BytesLeft < + CurSendBufferLength) { + + Connection->UnAckedSend.BufferOffset += BytesLeft; + Connection->UnAckedSend.MessageOffset += BytesLeft; + break; + + } else { + + TempBytes = CurSendBufferLength - Connection->UnAckedSend.BufferOffset; + BytesLeft -= TempBytes; + Connection->UnAckedSend.MessageOffset += TempBytes; + + // + // No, so advance the buffer. + // + + Connection->UnAckedSend.BufferOffset = 0; + Connection->UnAckedSend.Buffer = + NDIS_BUFFER_LINKAGE (Connection->UnAckedSend.Buffer); + + // + // Is there a next buffer in this request? + // + + if (Connection->UnAckedSend.Buffer == NULL) { + + // + // No, so advance the request unless we are done. + // + + if (BytesLeft == 0) { + return; + } + + Connection->UnAckedSend.Request = + REQUEST_SINGLE_LINKAGE(Connection->UnAckedSend.Request); + + if (Connection->UnAckedSend.Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->UnAckedSend.Buffer = + REQUEST_NDIS_BUFFER (Connection->UnAckedSend.Request); + + } + } + } + +} /* NbiAdvanceUnAckedByBytes */ + + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer so that the next packet to send will be + the correct one for ReceiveSequence. UnAckedSend + must point to a known valid combination. It + assumes that there are enough send requests to + handle the sequence specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT PacketsAcked; + + // + // BUGBUG: Fix this to account for partial sends, where + // we might not have used the max. for all packets. + // + + PacketsAcked = ReceiveSequence - Connection->UnAckedSend.SendSequence; + + NbiAdvanceUnAckedByBytes( + Connection, + PacketsAcked * Connection->MaximumPacketSize); + + Connection->UnAckedSend.SendSequence += PacketsAcked; + +} /* NbiAdvanceUnAckedBySequence */ + + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send + The request is found on the connection's send queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_SEND)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // Just stop the connection, that will tear down any + // sends. + // + // BUGBUG: Do we care about cancelling non-active + // sends without stopping the connection?? + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelSend */ + + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source + NDIS_BUFFER chain and offset into it. We assume we don't know the + length of the source Mdl chain, and we must allocate the NDIS_BUFFERs + for the destination chain, which we do from the NDIS buffer pool. + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. + +Environment: + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentSourceBuffer - Points to the start of the NDIS_BUFFER chain + from which to draw the packet. + + CurrentByteOffset - Offset within this NDIS_BUFFER to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + DestinationBuffer - returned pointer to the NDIS_BUFFER chain describing + the packet. + + NewSourceBuffer - returned pointer to the NDIS_BUFFER that would + be used for the next byte of packet. NULL if the source NDIS_BUFFER + chain was exhausted. + + NewByteOffset - returned offset into the NewSourceBuffer for the next byte + of packet. NULL if the source NDIS_BUFFER chain was exhausted. + + ActualLength - The actual length of the data copied. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded + and was the correct length. + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while + building the destination chain. + +--*/ +{ + ULONG AvailableBytes; + ULONG CurrentByteCount; + ULONG BytesCopied; + PNDIS_BUFFER OldNdisBuffer; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + + OldNdisBuffer = CurrentSourceBuffer; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + AvailableBytes = CurrentByteCount - CurrentByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + BufferPoolHandle, + OldNdisBuffer, + CurrentByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *DestinationBuffer = NewNdisBuffer; + BytesCopied = AvailableBytes; + + // + // Was the first NDIS_BUFFER enough data. + // + + if (BytesCopied == DesiredLength) { + if (CurrentByteOffset + AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = CurrentSourceBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset + AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + if (CurrentSourceBuffer->Next == NULL) { + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + while (OldNdisBuffer != NULL) { + + AvailableBytes = DesiredLength - BytesCopied; + if (AvailableBytes > CurrentByteCount) { + AvailableBytes = CurrentByteCount; + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + BufferPoolHandle, + OldNdisBuffer, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*DestinationBuffer != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*DestinationBuffer); + NdisFreeBuffer (*DestinationBuffer); + *DestinationBuffer = NewNdisBuffer; + } + + *NewByteOffset = CurrentByteOffset; + *NewSourceBuffer = CurrentSourceBuffer; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + BytesCopied += AvailableBytes; + + if (BytesCopied == DesiredLength) { + if (AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = OldNdisBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = OldNdisBuffer; + *NewByteOffset = AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + } + + // + // We ran out of source buffer chain. + // + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + +} /* NbiBuildBufferChainFromBufferChain */ + diff --git a/private/ntos/tdi/isn/nb/session.c b/private/ntos/tdi/isn/nb/session.c new file mode 100644 index 000000000..fe998feb2 --- /dev/null +++ b/private/ntos/tdi/isn/nb/session.c @@ -0,0 +1,2450 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + session.c + +Abstract: + + This module contains the code to handle session frames + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#ifdef RASAUTODIAL +#include +#include +#endif // RASAUTODIAL +#pragma hdrstop + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +VOID +NbiNoteNewConnection( + PCONNECTION pConnection + ); +#endif + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiSendDeathPacket( + IN PCONNECTION Connection, + IN CTELockHandle LockHandle + ) +{ + PNDIS_PACKET Packet = PACKET(&NbiGlobalDeathPacket); + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + NDIS_STATUS NdisStatus; + + if ( Reserved->SendInProgress ) { + DbgPrint("***Could not send death packet - in use\n"); + NB_FREE_LOCK(&Connection->Lock, LockHandle); + return; + } + + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DEATH_PACKET; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + Header->Session.ConnectionControlFlag = 0; + Header->Session.DataStreamType = NB_CMD_DEATH_PACKET; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + + + NB_FREE_LOCK(&Connection->Lock, LockHandle); + + DbgPrint("*****Death packet is being sent for connection %lx, to <%.16s>\n",Connection, Connection->RemoteName); + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = NbiDevice; + ULONG Hash; + ULONG ReceiveFlags; + ULONG IndicateBytesTransferred; + ULONG DataAvailable, DataIndicated; + ULONG DestBytes, BytesToTransfer; + PUCHAR DataHeader; + BOOLEAN Last, CompleteReceive, EndOfMessage, PartialReceive, CopyLookahead; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + ULONG NdisBytesTransferred; + PIRP ReceiveIrp; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + ULONG BufferChainLength; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + +#ifdef RSRC_TIMEOUT_DBG + if ( Connection->FirstMessageRequest && NbiGlobalDebugResTimeout ) { + LARGE_INTEGER CurrentTime, ElapsedTime; + KeQuerySystemTime(&CurrentTime); + ElapsedTime.QuadPart = CurrentTime.QuadPart - Connection->FirstMessageRequestTime.QuadPart; +// DbgPrint("*****Elapsed %lx.%lx time\n",ElapsedTime.HighPart,ElapsedTime.LowPart); + if ( ElapsedTime.QuadPart > NbiGlobalMaxResTimeout.QuadPart ) { + + DbgPrint("*****Connection %lx is not copleting irp %lx for %lx.%lx time\n",Connection, Connection->FirstMessageRequest, + ElapsedTime.HighPart,ElapsedTime.LowPart); + DbgPrint("************irp arrived at %lx.%lx current time %lx.%lx\n", + Connection->FirstMessageRequestTime.HighPart,Connection->FirstMessageRequestTime.LowPart, + CurrentTime.HighPart, CurrentTime.LowPart); + + NbiSendDeathPacket( Connection, LockHandle ); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + } + } +#endif //RSRC_TIMEOUT_DBG + + // + // The connection is up, see if this is data should + // be received. + // + + if (Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) { + + // + // This is an ack. This call releases + // the lock. BUGBUG: Does this need to + // be a function? + // + + NbiProcessDataAck( + Connection, + Sess, + RemoteAddress + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // See if there is any piggyback ack here. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. Even the old netbios sometimes piggyback + // acks (and doesn't send the explicit ack). + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if ((Connection->NewNetbios) && + (Connection->CurrentSend.SendSequence != Connection->UnAckedSend.SendSequence)) { + + // + // For the new netbios, even if we are not waiting + // for an ack he may have acked something with this + // send and we should check, since it may allow + // us to open our send window. + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } + + // + // This is data on the connection. First make sure + // it is the data we expect next. + // + + if (Connection->NewNetbios) { + + if (Sess->SendSequence != Connection->ReceiveSequence) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp data on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving a packet we have already seen, just + // send a normal ack, otherwise force a resend. This test + // we do is equivalent to + // Sess->SendSequence < Connection->ReceiveSequence + // but rearranged so it works when the numbers wrap. + // + + if ((SHORT)(Sess->SendSequence - Connection->ReceiveSequence) < 0) { + + // + // Since this is a resend, check if the local + // target has changed. + // +#if defined(_PNP_POWER) + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, sizeof(IPX_LOCAL_TARGET))) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx, (%d,%d)\n", Connection, + Connection->LocalTarget.NicHandle.NicId, RemoteAddress->NicHandle.NicId); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#else + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#endif _PNP_POWER + AckType = NbiAckResponse; + + } else { + + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // Old netbios. + // + + if ((Sess->SendSequence != Connection->ReceiveSequence) || + (Sess->Offset != Connection->CurrentReceive.MessageOffset)) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving the last packet again, just + // send a normal ack, otherwise force a resend. + // + + if (((Sess->SendSequence == Connection->ReceiveSequence) && + ((ULONG)(Sess->Offset + Sess->DataLength) == Connection->CurrentReceive.MessageOffset)) || + (Sess->SendSequence == (USHORT)(Connection->ReceiveSequence-1))) { + AckType = NbiAckResponse; + } else { + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + + IndicateBytesTransferred = 0; + DataAvailable = PacketSize - sizeof(NB_CONNECTION); + DataIndicated = LookaheadBufferSize - sizeof(NB_CONNECTION); + DataHeader = LookaheadBuffer + sizeof(NB_CONNECTION); + + ++Device->TempFramesReceived; + Device->TempFrameBytesReceived += DataAvailable; + + if (Connection->CurrentIndicateOffset) { + CTEAssert (DataAvailable >= Connection->CurrentIndicateOffset); + DataAvailable -= Connection->CurrentIndicateOffset; + if (DataIndicated >= Connection->CurrentIndicateOffset) { + DataIndicated -= Connection->CurrentIndicateOffset; + } else { + DataIndicated = 0; + } + DataHeader += Connection->CurrentIndicateOffset; + } + + CopyLookahead = (BOOLEAN)(MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA); + + if (Connection->NewNetbios) { + Last = (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_EOM) != 0); + } else { + Last = (BOOLEAN)(Sess->Offset + Sess->DataLength == Sess->TotalDataLength); + } + + Connection->CurrentReceiveNoPiggyback = + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) != 0); + + if (Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) { + + // + // We don't have a receive posted, so see if we can + // get one from the queue or our client. + // + + if (Connection->ReceiveQueue.Head != NULL) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Request = Connection->ReceiveQueue.Head; + Connection->ReceiveQueue.Head = REQUEST_SINGLE_LINKAGE(Request); + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NB_DEBUG2 (RECEIVE, ("Activated receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + if ((Connection->ReceiveUnaccepted == 0) && + (Connection->AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE])) { + + Connection->ReceiveState = CONNECTION_RECEIVE_INDICATE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + Status = (*Connection->AddressFile->ReceiveHandler)( + Connection->AddressFile->HandlerContexts[TDI_EVENT_RECEIVE], + Connection->Context, + ReceiveFlags, + DataIndicated, + DataAvailable, + &IndicateBytesTransferred, + DataHeader, + &ReceiveIrp); + + if (Status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // We got an IRP, activate it. + // + + Request = NbiAllocateRequest (Device, ReceiveIrp); + + IF_NOT_ALLOCATED(Request) { + + ReceiveIrp->IoStatus.Information = 0; + ReceiveIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (ReceiveIrp, IO_NETWORK_INCREMENT); + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + CTEAssert (REQUEST_OPEN_CONTEXT(Request) == Connection); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + Connection->ReceiveUnaccepted = DataAvailable - IndicateBytesTransferred; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + NB_DEBUG2 (RECEIVE, ("Indicate got receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + // + // The connection has been stopped. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // He accepted some or all of the data. + // + + NB_DEBUG2 (RECEIVE, ("Indicate took receive data %lx (%d)\n", Connection, Connection->ReceiveSequence)); + + if ( (IndicateBytesTransferred >= DataAvailable)) { + + CTEAssert (IndicateBytesTransferred == DataAvailable); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentIndicateOffset = 0; + if ( Last ) { + Connection->CurrentReceive.MessageOffset = 0; + } else { + Connection->CurrentReceive.MessageOffset+= IndicateBytesTransferred; + } + + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + Connection->NoPiggybackHeuristic = (BOOLEAN) + (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // We will do the easiest thing here, which + // is to send an ack for the amount he + // took, and force a retransmit on the + // remote. For net netbios we make a note + // of how many bytes were taken and ask + // for a resend. + // + // BUGBUG: Handle this better?? + // + +#if DBG + DbgPrint ("NBI: Client took partial indicate data\n"); +#endif + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive.MessageOffset += + IndicateBytesTransferred; + Connection->ReceiveUnaccepted = + DataAvailable - IndicateBytesTransferred; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + Connection->CurrentIndicateOffset = IndicateBytesTransferred; + // + // NOTE: We don't advance ReceiveSequence + // + } + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + Connection->NewNetbios ? + NbiAckResend : NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } else { + + // + // No IRP returned. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveUnaccepted = DataAvailable; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_DEBUG (RECEIVE, ("Indicate got no receive on %lx (%lx)\n", Connection, Status)); + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // No receive handler. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + if (Connection->ReceiveUnaccepted == 0) { + NB_DEBUG (RECEIVE, ("No receive, no handler on %lx\n", Connection)); + } else { + NB_DEBUG (RECEIVE, ("No receive, ReceiveUnaccepted %d on %lx\n", + Connection->ReceiveUnaccepted, Connection)); + } + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + } else if (Connection->ReceiveState != CONNECTION_RECEIVE_ACTIVE) { + + // + // If we have a transfer in progress, or are waiting for + // a receive to be posted, then ignore this frame. + // + + NB_DEBUG2 (RECEIVE, ("Got data on %lx, state %d (%d)\n", Connection, Connection->ReceiveState, Connection->ReceiveSequence)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + // + // At this point we have a receive and it is set to + // the correct current location. + // + + DestBytes = Connection->ReceiveLength - Connection->CurrentReceive.Offset; + BytesToTransfer = DataAvailable - IndicateBytesTransferred; + + if (DestBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceive = TRUE; + PartialReceive = TRUE; + BytesToTransfer = DestBytes; + + } else if (DestBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceive = TRUE; + PartialReceive = FALSE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceive = Last; + PartialReceive = FALSE; + + } + + // + // If we can copy the data directly, then update our + // pointers, send an ack, and do the copy. + // + + if ((BytesToTransfer > 0) && + (IndicateBytesTransferred + BytesToTransfer <= DataIndicated)) { + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PNDIS_BUFFER CurBuffer; + ULONG CurByteOffset; + + NB_DEBUG2 (RECEIVE, ("Direct copy of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + CurBuffer = Connection->CurrentReceive.Buffer; + CurByteOffset = Connection->CurrentReceive.BufferOffset; + + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurTarget += CurByteOffset; + CurTargetLen -= CurByteOffset; + + CurSource = DataHeader + IndicateBytesTransferred; + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurBuffer = CurBuffer->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + CTEAssert (BytesLeft == BytesNow); + break; + } + + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->CurrentReceive.Buffer = CurBuffer; + Connection->CurrentReceive.BufferOffset = CurByteOffset; + + Connection->CurrentReceive.Offset += BytesToTransfer; + Connection->CurrentReceive.MessageOffset += BytesToTransfer; + + if (CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (EndOfMessage) { + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (PartialReceive) { + Connection->CurrentIndicateOffset += BytesToTransfer; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // and CANCEL Lock. + // + + NbiCompleteReceive( + Connection, + EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + // + // CompleteReceive is FALSE, so EndOfMessage is FALSE. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + + // + // We have to set up a call to transfer data and send + // the ack after it completes (if it succeeds). + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + DataAvailable); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->u.RR_CO.Connection = Connection; + ReceiveReserved->u.RR_CO.EndOfMessage = EndOfMessage; + ReceiveReserved->u.RR_CO.CompleteReceive = CompleteReceive; + ReceiveReserved->u.RR_CO.PartialReceive = PartialReceive; + + ReceiveReserved->Type = RECEIVE_TYPE_DATA; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("TransferData of 0 bytes %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiTransferDataComplete( + Packet, + NDIS_STATUS_SUCCESS, + 0); + + return; + } + + // + // If needed, build a buffer chain to describe this + // to NDIS. + // + + Connection->PreviousReceive = Connection->CurrentReceive; + + if ((Connection->CurrentReceive.Offset == 0) && + CompleteReceive) { + + BufferChain = Connection->CurrentReceive.Buffer; + BufferChainLength = BytesToTransfer; + Connection->CurrentReceive.Buffer = NULL; + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + + } else { + + if (NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentReceive.Buffer, + Connection->CurrentReceive.BufferOffset, + BytesToTransfer, + &BufferChain, + &Connection->CurrentReceive.Buffer, + &Connection->CurrentReceive.BufferOffset, + &BufferChainLength) != NDIS_STATUS_SUCCESS) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("Could not build receive buffer chain %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + ReceiveReserved->u.RR_CO.NoNdisBuffer = FALSE; + + } + + + NdisChainBufferAtFront (Packet, BufferChain); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("TransferData of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + sizeof(NB_CONNECTION) + + Connection->CurrentIndicateOffset + IndicateBytesTransferred, + BytesToTransfer, + Packet, + (PUINT)&NdisBytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (NdisBytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete ( + Packet, + NdisStatus, + NdisBytesTransferred); + + } + + return; + + } + + } else if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + // + // If this is the ack for the session initialize, then + // complete the pending connects. This routine releases + // the connection lock. + // + + NbiProcessSessionInitAck( + Connection, + Sess + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + // + // This is a session initialize frame. + // + // BUGBUG: If there is more data than in the lookahead + // buffer, we won't be able to echo it back in the + // response. + // + + NbiProcessSessionInitialize( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + + } + +} /* NbiProcessSessionData */ + + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine processes an ack on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The session frame. + + RemoteAddress - The local target this packet was received from. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN Resend; + + // + // Make sure we expect an ack right now. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (((Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W)) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We are waiting for an ack (because we completed + // packetizing a send, or ran out of receive window). + // + // This will complete any sends that are acked by + // this receive, and if necessary readjust the + // send pointer and requeue the connection for + // packetizing. It release the connection lock. + // + + if (Connection->ResponseTimeout) { + Resend = TRUE; + Connection->ResponseTimeout = FALSE; + } else { + Resend = (BOOLEAN) + ((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0); + } + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + Resend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We had a probe outstanding and got a response. Restart + // the connection if needed (a send may have just been + // posted while the probe was outstanding). + // + // BUGBUG: We should check that the response is really + // correct. + // + + if (Connection->NewNetbios) { + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + } + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + if (Connection->NewNetbios) { + + // + // We are packetizing, reframe. In the unlikely + // event that this acks everything we may packetize + // in this call, but that is OK (the other thread + // will exit if we finish up). More normally we + // will just advance UnAcked send a bit. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0) + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +#if 0 + + // + // BUGBUG: Should handle this case (i.e. may be in W_PACKET). + // + + } else if ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0) { + + DbgPrint ("NWLNKNB: Ignoring ack, state is %d\n", Connection->SubState); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); +#endif + + } else { + + // + // We got a probe from the remote. Some old DOS clients + // send probes that do not have the send ack bit on, + // so we respond to any probe if none of the conditions + // above are true. This call releases the lock. + // + // We use the IgnoreNextDosProbe flag to ignore every + // second probe of this nature, to avoid a data ack + // war between two machines who each think they are + // responding to the other. This flag is set to FALSE + // whenever we send an ack or a probe. + // + + if (!Connection->IgnoreNextDosProbe) { + + // + // Since this is a probe, check if the local + // target has changed. + // + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + Connection->IgnoreNextDosProbe = TRUE; + + } else { + + Connection->IgnoreNextDosProbe = FALSE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + +} /* NbiProcessDataAck */ + + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION frames which have + a remote connection ID of 0xffff -- these are session + initialize frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + PacketBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + CONNECT_INDICATION TempConnInd; + PCONNECT_INDICATION ConnInd; + PCONNECTION Connection; + PADDRESS Address; + PREQUEST Request, ListenRequest, AcceptRequest; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + ULONG Hash; + TA_NETBIOS_ADDRESS SourceName; + PIRP AcceptIrp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS AcceptStatus; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_REQUEST_KERNEL_LISTEN ListenParameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + PTDI_CONNECTION_INFORMATION RemoteInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + // + // Verify that the whole packet is there. + // + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT))) { +#if DBG + DbgPrint ("NBI: Got short session initialize, %d/%d\n", PacketSize, + sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); +#endif + return; + } + + // + // Verify that MaximumDataSize that remote can support is > 0 + // Bug # 19405 + // + if ( SessInit->MaximumDataSize == 0 ) { + NB_DEBUG(CONNECTION, ("Connect request with MaximumDataSize == 0\n" +)); + return; + } + + // + // Make sure this is for an address we care about. + // + + if (Device->AddressCounts[SessInit->DestinationName[0]] == 0) { + return; + } + + Address = NbiFindAddress (Device, (PUCHAR)SessInit->DestinationName); + + if (Address == NULL) { + return; + } + + // + // First see if we have a session to this remote. We check + // this in case our ack of the session initialize was dropped, + // we don't want to reindicate our client. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + for (Hash = 0; Hash < CONNECTION_HASH_COUNT; Hash++) { + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if ((RtlEqualMemory (&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12)) && + (Connection->RemoteConnectionId == Sess->SourceConnectionId) && + (Connection->State != CONNECTION_STATE_DISCONNECT)) { + + // + // Yes, we are talking to this remote, if it is active then + // respond, otherwise we are in the process of connecting + // and we will respond eventually. + // + +#if DBG + DbgPrint ("NBI: Got connect request on active connection %lx\n", Connection); +#endif + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + NbiSendSessionInitAck( + Connection, + (PUCHAR)(SessInit+1), + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)), + NULL); // lock is not held + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + Connection = Connection->NextConnection; + } + } + + + TdiBuildNetbiosAddress ((PUCHAR)SessInit->SourceName, FALSE, &SourceName); + + // + // Scan the queue of listens to see if there is one that + // satisfies this request. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ListenQueue.Flink; + p != &Device->ListenQueue; + p = p->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->AddressFile->Address != Address) { + continue; + } + + // + // Check that this listen is not specific to a different + // netbios name. + // + + ListenParameters = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(Request); + ListenInformation = ListenParameters->RequestConnectionInformation; + + if (ListenInformation && + (ListenInformation->RemoteAddress) && + (ListenAddress = NbiParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + SessInit->SourceName, + ListenAddress->NetbiosName, + 16))) { + continue; + } + + // + // This connection is valid, so we use it. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen %lx\n", Connection)); + + RemoveEntryList (REQUEST_LINKAGE(Request)); + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + // + // Save this information now for whenever we complete the listen. + // + + RemoteInformation = ListenParameters->ReturnConnectionInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + if (ListenParameters->RequestFlags & TDI_QUERY_ACCEPT) { + + // + // We have to wait for an accept before sending the + // session init ack, so we complete the listen and wait. + // + + ListenRequest = Request; + Connection->ListenRequest = NULL; + + NB_DEBUG2 (CONNECTION, ("Queued listen on %lx awaiting accept\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ACCEPT; + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_W_ACCEPT); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } else { + + // + // We are ready to go, so we send out the find route request + // for the remote. We keep the listen alive and the CREF_LISTEN + // reference on until this completes. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen on %lx\n", Connection)); + + ListenRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } + + // + // Complete the listen if needed. + // + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION (ListenRequest) = 0; + REQUEST_STATUS (ListenRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK ( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest (Device, ListenRequest); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + + return; + + } + + // + // We could not find a listen, so we indicate to every + // client. Make sure there is no session initialize for this + // remote being indicated. If there is not, we insert + // ourselves in the queue to block others. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ConnectIndicationInProgress.Flink; + p != &Device->ConnectIndicationInProgress; + p = p->Flink) { + + ConnInd = CONTAINING_RECORD (p, CONNECT_INDICATION, Linkage); + + if ((RtlEqualMemory(ConnInd->NetbiosName, SessInit->DestinationName, 16)) && + (RtlEqualMemory(&ConnInd->RemoteAddress, Conn->IpxHeader.SourceNetwork, 12)) && + (ConnInd->ConnectionId == Sess->SourceConnectionId)) { + + // + // We are processing a request from this remote for + // the same ID, to avoid confusion we just exit. + // + +#if DBG + DbgPrint ("NBI: Already processing connect to <%.16s>\n", SessInit->DestinationName); +#endif + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + } + + RtlCopyMemory (&TempConnInd.RemoteAddress, SessInit->DestinationName, 16); + RtlCopyMemory (&TempConnInd.RemoteAddress, Conn->IpxHeader.SourceNetwork, 12); + TempConnInd.ConnectionId = Sess->SourceConnectionId; + + InsertTailList (&Device->ConnectIndicationInProgress, &TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + + // + // Now scan through the address to find someone who has + // an indication routine registed and wants this connection. + // + + + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No posted listen requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_CONNECT]) { + + if ((*AddressFile->ConnectionHandler)( + AddressFile->HandlerContexts[TDI_EVENT_CONNECT], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, // user data + NULL, + 0, // options + NULL, + &ConnectionContext, + &AcceptIrp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + continue; + + } + + AcceptRequest = NbiAllocateRequest (Device, AcceptIrp); + + IF_NOT_ALLOCATED(AcceptRequest) { + + AcceptStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // The client accepted the connect, so activate + // the connection and complete the accept. + // listen. This lookup references the connection + // so we know it will remain valid. + // + + Connection = NbiLookupConnectionByContext ( + AddressFile, + ConnectionContext); + + if (Connection != NULL) { + + ASSERT (Connection->AddressFile == AddressFile); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle2); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + NB_DEBUG2 (CONNECTION, ("Indication on %lx returned connection %lx\n", AddressFile, Connection)); + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + Connection->Retries = Device->KeepAliveCount; + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + NbiReferenceConnectionLock (Connection, CREF_ACCEPT); + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + Connection->AcceptRequest = AcceptRequest; + AcceptStatus = STATUS_PENDING; + + // + // Take us out of this list now, we will jump to + // FoundConnection which is past the removal below. + // + + RemoveEntryList (&TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned invalid connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + + } + + NbiDereferenceConnection (Connection, CREF_BY_CONTEXT); + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned unknown connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + + } + } + + // + // Complete the accept request in the failure case. + // + + if (AcceptStatus != STATUS_PENDING) { + + REQUEST_STATUS (AcceptRequest) = AcceptStatus; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest (Device, AcceptRequest); + + } else { + + // + // We found a connection, so we break; this is + // a jump since the while exit assumes the + // address lock is held. + // + + goto FoundConnection; + + } + + } + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Take us out of the list that blocks other indications + // from this remote to this address. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + RemoveEntryList (&TempConnInd.Linkage); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + +FoundConnection: + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + + NbiDereferenceAddress (Address, AREF_FIND); + +} /* NbiProcessSessionInitialize */ + + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine handles session init ack frames. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The netbios header for the received frame. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + BOOLEAN TimerWasStopped = FALSE; + CTELockHandle CancelLH; + + if ((Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) && + (Sess->SendSequence == 0x0000) && + (Sess->ReceiveSequence == 0x0001)) { + + NB_DEBUG2 (CONNECTION, ("Completing connect on %lx\n", Connection)); + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + if (Connection->Retries == NbiDevice->ConnectionCount) { + ++NbiDevice->Statistics.ConnectionsAfterNoRetry; + } else { + ++NbiDevice->Statistics.ConnectionsAfterRetry; + } + ++NbiDevice->Statistics.OpenConnections; + + Connection->Retries = NbiDevice->KeepAliveCount; + NbiStartWatchdog (Connection); + + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 1; + Connection->UnAckedSend.SendSequence = 1; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 0; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = NbiDevice->KeepAliveCount; + if (NbiDevice->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveWindowSize - 1); + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 3; + } else { + Connection->NewNetbios = FALSE; + } + + if (Connection->MaximumPacketSize > SessInit->MaximumDataSize) { + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + } + + Request = Connection->ConnectRequest; + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify + // the automatic connection driver about + // this connection. + // + if (fAcdLoadedG) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + CTEGetLock(&AcdDriverG.SpinLock, &AcdHandle); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, AcdHandle); + if (fEnabled) + NbiNoteNewConnection(Connection); + } +#endif // RASAUTODIAL + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_SUCCESS; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiTransferReferenceConnection (Connection, CREF_CONNECT, CREF_ACTIVE); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiProcessSessionInitAck */ + + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + + // + // We reply to any session end, even if we don't know the + // connection, to speed up the disconnect on the remote. + // + + if (Connection == NULL) { + + NB_DEBUG (CONNECTION, ("Session end received on unknown id %lx\n", Sess->DestConnectionId)); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. We do this in case a full send has been + // received by the remote but he did not send an + // ack before the session went down -- this will + // prevent us from failing a send which actually + // succeeded. If we are not in W_ACK this may ack + // part of a send, but in that case we don't care + // since StopConnection will abort it anyway and + // the amount successfully received by the remote + // doesn't matter. + // + // This releases the lock. BUGBUG: Fix this. + // + + NB_DEBUG2 (CONNECTION, ("Session end at W_ACK, reframing %lx (%d)\n", Connection, Sess->ReceiveSequence)); + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle1)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on connection %lx\n", Connection)); + + } + + // + // This call sets the state to DISCONNECT and + // releases the connection lock. It will also + // complete a disconnect wait request if one + // is pending, and indicate to our client + // if needed. + // + + NbiStopConnection( + Connection, + STATUS_REMOTE_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on inactive connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEnd */ + + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END_ACK frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // Stop the timer, when the reference goes away it + // will shut down. We set the substate so if the + // timer is running it will not restart (BUGBUG: + // there is a small window here, but it is not + // harmful, we will just have to timeout one + // more time). + // + + NB_DEBUG2 (CONNECTION, ("Got session end ack on %lx\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_D_GOT_ACK; + if (CTEStopTimer (&Connection->Timer)) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEndAck */ + diff --git a/private/ntos/tdi/isn/nb/sources.inc b/private/ntos/tdi/isn/nb/sources.inc new file mode 100644 index 000000000..f54b4918b --- /dev/null +++ b/private/ntos/tdi/isn/nb/sources.inc @@ -0,0 +1,69 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnknb + +TARGETNAME=nwlnknb +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\inc;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 -DRASAUTODIAL +#-DRSRC_TIMEOUT_DBG + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\address.c \ + ..\autodial.c \ + ..\bind.c \ + ..\cache.c \ + ..\config.c \ + ..\connect.c \ + ..\datagram.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\frame.c \ + ..\nwlnknb.rc \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\send.c \ + ..\session.c \ + ..\timer.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj + \ No newline at end of file diff --git a/private/ntos/tdi/isn/nb/timer.c b/private/ntos/tdi/isn/nb/timer.c new file mode 100644 index 000000000..381b120e5 --- /dev/null +++ b/private/ntos/tdi/isn/nb/timer.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code which implements the timers for + netbios. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +ULONG NbiTickIncrement = 0; +ULONG NbiShortTimerDeltaTicks = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiInitializeTimers) +#endif + + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the retransmit timer for the given connection. + The connection is inserted on the short list if it isn't on already. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Insert us in the queue if we aren't in it. + // + + Connection->Retransmit = + Device->ShortAbsoluteTime + Connection->CurrentRetransmitTimeout; + + if (!Connection->OnShortList) { + + CTEAssert (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnShortList) { + Connection->OnShortList = TRUE; + InsertTailList (&Device->ShortList, &Connection->ShortList); + } + + if (!Device->ShortListActive) { + NbiStartShortTimer (Device); + Device->ShortListActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartRetransmit */ + + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the watchdog timer for a connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + Connection->Watchdog = Device->LongAbsoluteTime + Connection->WatchdogTimeout; + + if (!Connection->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnLongList) { + Connection->OnLongList = TRUE; + InsertTailList (&Device->LongList, &Connection->LongList); + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartWatchdog */ + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the retransmit timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Retransmit = 0; + +} /* NbiStopRetransmit */ + + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the watchdog timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Watchdog = 0; + +} /* NbiStopWatchdog */ +#endif + + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's retransmit timer + expires. It is called from NbiShortTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = NbiDevice; + BOOLEAN SendFindRoute; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + SendFindRoute = FALSE; + + ++Device->Statistics.ResponseTimerExpirations; + + if (!(Connection->NewNetbios) && + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Wait for ack timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // Set our current packetize location back to the + // spot of the last ack, and start up again. + // + // BUGBUG: Should we send a probe here? + // + + Connection->CurrentSend = Connection->UnAckedSend; + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NB_DEBUG2 (SEND, ("Connection %lx retransmit timeout\n", Connection)); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // This releases the lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) || + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Probe timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NbiStartRetransmit (Connection); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // Set this so we know to retransmit when the ack + // is received. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PROBE) { + Connection->ResponseTimeout = TRUE; + } + + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + if (SendFindRoute) { + + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_FORCE_RIP; + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireRetansmit */ + + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's watchdog timer + expires. It is called from NbiLongTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // If we are not idle, then something else is happening + // so the watchdog is unnecessary. + // + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE)) { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->SubState = CONNECTION_SUBSTATE_A_W_PROBE; + NbiStartRetransmit (Connection); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireWatchdog */ + + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the short connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE Device = (PDEVICE)Context; + PCONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + Device->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + Device->ShortTimerStart.QuadPart; + + TickDelta = TickDifference.LowPart / NbiShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + Device->ShortAbsoluteTime += TickDelta; + + if (Device->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + Device->ShortAbsoluteTime -= 0xe0000000; + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + Timeout = Connection->Retransmit; + if (Timeout) { + Connection->Retransmit = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + ASSERT (Connection->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Retransmit && + (Device->ShortAbsoluteTime > Connection->Retransmit)) { + + Connection->Retransmit = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireRetransmit (Connection); // no locks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + + break; + + } + + nextp = p->Flink; + + if (Connection->Retransmit == 0) { + + Connection->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Connection->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if (Connection->Retransmit != 0) { + InsertTailList(&Device->ShortList, &Connection->ShortList); + Connection->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&Device->ShortList)) { + Device->ShortListActive = FALSE; + } + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for enough times through here, + // If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (!Connection->DataAckPending) { + continue; + } + + ++Connection->DataAckTimeouts; + + if (Connection->DataAckTimeouts < Device->AckDelayTime) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_SHORT_D_ACK); + + Device->DataAckQueueChanged = FALSE; + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->DataAckPending) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // We set PiggybackAckTimeout to TRUE so that we won't try + // to piggyback a response until we get back traffic. + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = TRUE; + ++Device->Statistics.AckTimerExpirations; + ++Device->Statistics.PiggybackAckTimeouts; + + // + // This call releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_SHORT_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (Device->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&Device->DataAckConnections)) { + Device->DataAckActive = FALSE; + } + + + // + // Update the real counters from the temp ones. We have + // TimerLock here, which is good enough. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesSent, + Device->TempFrameBytesSent); + Device->Statistics.DataFramesSent += Device->TempFramesSent; + + Device->TempFrameBytesSent = 0; + Device->TempFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesReceived, + Device->TempFrameBytesReceived); + Device->Statistics.DataFramesReceived += Device->TempFramesReceived; + + Device->TempFrameBytesReceived = 0; + Device->TempFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + Device->ProcessingShortTimer = FALSE; + + if ((Device->ShortListActive || Device->DataAckActive) && + (Device->State != DEVICE_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } else { + + NbiDereferenceDevice (Device, DREF_SHORT_TIMER); + + } + +} /* NbiShortTimeout */ + + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the long connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, nextp; + LIST_ENTRY AdapterStatusList; + PREQUEST AdapterStatusRequest; + PCONNECTION Connection; + PNETBIOS_CACHE CacheName; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (++Device->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + Device->LongAbsoluteTime = 0x10000000; + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + Timeout = Connection->Watchdog; + if (Timeout) { + Connection->Watchdog = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + + if ((Device->LongAbsoluteTime % 4) == 0) { + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + ASSERT (Connection->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Watchdog && (Device->LongAbsoluteTime > Connection->Watchdog)) { + + Connection->Watchdog = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireWatchdog (Connection); // no spinlocks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBI: Stop processing LongList, %lx removed\n", Connection); +#endif + break; + + } + + nextp = p->Flink; + + if (Connection->Watchdog == 0) { + + Connection->OnLongList = FALSE; + RemoveEntryList(p); + + if (Connection->Watchdog != 0) { + InsertTailList(&Device->LongList, &Connection->LongList); + Connection->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + if (Connection->DataAckPending) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_LONG_D_ACK); + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Have to check again, because the connection might + // just have been stopped, and it also might just have + // had a data ack queued. + // + + if (Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if (Connection->DataAckPending) { + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + Device->DataAckQueueChanged = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiDereferenceConnection (Connection, CREF_LONG_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbiReceiveComplete((USHORT)0); + + + // + // Check if any adapter status queries are getting old. + // + + InitializeListHead (&AdapterStatusList); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + if (REQUEST_INFORMATION(AdapterStatusRequest) == 1) { + + // + // BUGBUG: We should resend a certain number of times. + // + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // We are going to abort this request, so dereference + // the cache entry it used. + // + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + } else { + + ++REQUEST_INFORMATION(AdapterStatusRequest); + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + NB_DEBUG2 (QUERY, ("AdapterStatus %lx got name but no response\n", AdapterStatusRequest)); + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + // + // See if a minute has passed and we need to check for empty + // cache entries to age out. We check for 64 seconds to make + // the mod operation faster. + // + +#if defined(_PNP_POWER) + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); +#endif _PNP_POWER + + ++Device->CacheTimeStamp; + + if ((Device->CacheTimeStamp % 64) == 0) { + + + // + // flush all the entries which have been around for ten minutes + // (LONG_TIMER_DELTA is in milliseconds). + // + + FlushOldFromNetbiosCacheTable( Device->NameCache, (600000 / LONG_TIMER_DELTA) ); + + } + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (Device->State != DEVICE_STATE_STOPPING) { + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } else { +#if defined(_PNP_POWER) + Device->LongTimerRunning = FALSE; +#endif _PNP_POWER + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + +#if defined(_PNP_POWER) + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER +} /* NbiLongTimeout */ + + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + Device - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + + if ((!Device->ProcessingShortTimer) && + (!(Device->ShortListActive)) && + (!(Device->DataAckActive))) { + + NbiReferenceDevice (Device, DREF_SHORT_TIMER); + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } + +} /* NbiStartShortTimer */ + + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + Device - Pointer to our device. + +Return Value: + + none. + +--*/ + +{ + + // + // NbiTickIncrement is the number of NT time increments + // which pass between each tick. NbiShortTimerDeltaTicks + // is the number of ticks which should happen in + // SHORT_TIMER_DELTA milliseconds (i.e. between each + // expiration of the short timer). + // + + NbiTickIncrement = KeQueryTimeIncrement(); + + if (NbiTickIncrement > (SHORT_TIMER_DELTA * MILLISECONDS)) { + NbiShortTimerDeltaTicks = 1; + } else { + NbiShortTimerDeltaTicks = (SHORT_TIMER_DELTA * MILLISECONDS) / NbiTickIncrement; + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + Device->ShortAbsoluteTime = 0x10000000; + Device->LongAbsoluteTime = 0x10000000; + + CTEInitTimer (&Device->ShortTimer); + CTEInitTimer (&Device->LongTimer); + +#if !defined(_PNP_POWER) + // + // One reference for the long timer. + // + + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + +#endif !_PNP_POWER + + Device->TimersInitialized = TRUE; + Device->ShortListActive = FALSE; + Device->ProcessingShortTimer = FALSE; + + InitializeListHead (&Device->ShortList); + InitializeListHead (&Device->LongList); + + CTEInitLock (&Device->TimerLock.Lock); + +} /* NbiInitializeTimers */ + diff --git a/private/ntos/tdi/isn/nb/up/makefile b/private/ntos/tdi/isn/nb/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/nb/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/nb/up/sources b/private/ntos/tdi/isn/nb/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isn/nb/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/rip/debug.h b/private/ntos/tdi/isn/rip/debug.h new file mode 100644 index 000000000..2086b91d5 --- /dev/null +++ b/private/ntos/tdi/isn/rip/debug.h @@ -0,0 +1,62 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: debug.h +// +// Description: Debug macros definitions +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _DEBUG_ +#define _DEBUG_ + +#if DBG +#define DBG_INIT ((ULONG)0x00000001) +#define DBG_IOCTL ((ULONG)0x00000002) +#define DBG_UNLOAD ((ULONG)0x00000004) +#define DBG_RCVPKT ((ULONG)0x00000008) +#define DBG_RECV ((ULONG)0x00000010) +#define DBG_ROUTE ((ULONG)0x00000020) +#define DBG_SEND ((ULONG)0x00000040) +#define DBG_RIP ((ULONG)0x00000080) +#define DBG_SNDREQ ((ULONG)0x00000100) +#define DBG_RIPTIMER ((ULONG)0x00000200) +#define DBG_NIC ((ULONG)0x00000400) +#define DBG_RIPAUX ((ULONG)0x00000800) +#define DBG_NOTIFY ((ULONG)0x00001000) +#define DBG_LINE ((ULONG)0x00002000) +#define DBG_NETBIOS ((ULONG)0x00004000) +#define DBG_INNACTIVITY ((ULONG)0x00008000) + + +#define DEF_DBG_LEVEL DBG_INIT | \ + DBG_IOCTL | \ + DBG_UNLOAD | \ + DBG_NIC | \ + DBG_LINE | \ + DBG_NOTIFY + +extern ULONG RouterDebugLevel; + +#define RtPrint(LEVEL,STRING) \ + do { \ + ULONG _level = (LEVEL); \ + if (RouterDebugLevel & _level) { \ + DbgPrint STRING; \ + } \ + } while (0) + +#define WAN_EMULATION + +#else +#define RtPrint(LEVEL,STRING) do {NOTHING;} while (0) +#endif + +#endif // _DEBUG_ diff --git a/private/ntos/tdi/isn/rip/dirs b/private/ntos/tdi/isn/rip/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/rip/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/rip/driver.c b/private/ntos/tdi/isn/rip/driver.c new file mode 100644 index 000000000..ece07e568 --- /dev/null +++ b/private/ntos/tdi/isn/rip/driver.c @@ -0,0 +1,1023 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: driver.c +// +// Description: router driver entry point +// +// Author: Stefan Solomon (stefans) October 13, 1993. +// +// Revision History: +// +//*** + +#include +#include "rtdefs.h" +#include "driver.h" + +#if DBG +ULONG RouterDebugLevel = DEF_DBG_LEVEL; +#else +ULONG RouterDebugLevel; +#endif + +NTSTATUS +GetRouterParameters(PUNICODE_STRING); + +NTSTATUS +RouterDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +RouterUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +RouterIoctl( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PVOID ioBuffer, + IN ULONG inputBufferLength, + IN ULONG outputBufferLength + ); + +USHORT dbgpktnr; + +NTSTATUS +IoctlSnapRoutes(VOID); + +NTSTATUS +IoctlGetNextRoute(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG sizep); + +NTSTATUS +IoctlCheckNetNumber(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlShowNicInfo(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlZeroNicStatistics(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlShowMemStatistics(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlGetWanInactivity(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlSetWanGlobalNet(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +NTSTATUS +IoctlDeleteWanGlobalAddress(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp); + +VOID +DeleteGlobalWanNet(VOID); + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path + to driver-specific key in the registry + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +{ + + PDEVICE_OBJECT deviceObject = NULL; + NTSTATUS ntStatus; + WCHAR deviceNameBuffer[] = L"\\Device\\Ipxroute"; + UNICODE_STRING deviceNameUnicodeString; + PIPX_INTERNAL_BIND_RIP_OUTPUT IpxBindBuffp = NULL; + + RtPrint(DBG_INIT, ("IPXROUTER: Entering DriverEntry\n")); + + // + // Create a non - EXCLUSIVE device object (more than 1 thread at a time + // can make requests to this device) + // + + RtlInitUnicodeString (&deviceNameUnicodeString, + deviceNameBuffer); + + ntStatus = IoCreateDevice (DriverObject, + 0, + &deviceNameUnicodeString, + FILE_DEVICE_IPXROUTER, + 0, + FALSE, + &deviceObject + ); + + if (NT_SUCCESS(ntStatus)) + { + // + // Create dispatch points for device control, create, close. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = + DriverObject->MajorFunction[IRP_MJ_CLOSE] = + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RouterDispatch; + DriverObject->DriverUnload = RouterUnload; + + } + else + { + RtPrint (DBG_INIT, ("IPXROUTER: IoCreateDevice failed\n")); + goto failure_exit; + } + + // get registry configuration + ntStatus = GetRouterParameters(RegistryPath); + + if(!NT_SUCCESS(ntStatus)) { + + RtPrint (DBG_INIT, ("IPXROUTER: Error reading registry parameters\n")); + goto failure_exit; + } + + // Bind to the ipx driver. + // If succesful, it will point the argument to a paged pool buffered with + // the Ipx driver output data. This buffer has to be freed after usage. + // The buffer is freed in the RouterInit routine. + ntStatus = BindToIpxDriver(&IpxBindBuffp); + + if(!NT_SUCCESS(ntStatus)) { + + RtPrint (DBG_INIT, ("IPXROUTER: Bind to Ipx driver failed\n")); + goto failure_exit; + } + + // initialize the router + ntStatus = RouterInit(IpxBindBuffp); + + if(!NT_SUCCESS(ntStatus)) { + + RtPrint (DBG_INIT, ("IPXROUTER: Error initializing the router\n")); + goto failure_exit; + } + + // Start the global timer + StartRtTimer(); + + // all initialization done + RouterInitialized = TRUE; + + // Start the routing functionality + ntStatus = RouterStart(); + + if(!NT_SUCCESS(ntStatus)) { + + RtPrint (DBG_INIT, ("IPXROUTER: Error starting the router\n")); + goto failure_exit; + } + + // started OK + return STATUS_SUCCESS; + +failure_exit: + + IoDeleteDevice (DriverObject->DeviceObject); + return ntStatus; +} + + + +NTSTATUS +RouterDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + Process the IRPs sent to this device. + +Arguments: + + DeviceObject - pointer to a device object + + Irp - pointer to an I/O Request Packet + +Return Value: + + +--*/ +{ + PIO_STACK_LOCATION irpStack; + PVOID ioBuffer; + ULONG inputBufferLength; + ULONG outputBufferLength; + ULONG ioControlCode; + NTSTATUS ntStatus; + + + // + // Init to default settings- we only expect 1 type of + // IOCTL to roll through here, all others an error. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current location in the Irp. This is where + // the function codes and parameters are located. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + + // + // Get the pointer to the input/output buffer and it's length + // + + ioBuffer = Irp->AssociatedIrp.SystemBuffer; + inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; + outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + + switch (irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + + RtPrint(DBG_IOCTL, ("IPXROUTER: IRP_MJ_CREATE\n")); + dbgpktnr = 0x5000; + + break; + + case IRP_MJ_CLOSE: + + RtPrint(DBG_IOCTL, ("IPXROUTER: IRP_MJ_CLOSE\n")); + + break; + + case IRP_MJ_DEVICE_CONTROL: + + ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + + switch (ioControlCode) + { + + case IOCTL_IPXROUTER_SNAPROUTES: + + Irp->IoStatus.Status = IoctlSnapRoutes(); + break; + + case IOCTL_IPXROUTER_GETNEXTROUTE: + + Irp->IoStatus.Status = IoctlGetNextRoute ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_CHECKNETNUMBER: + + Irp->IoStatus.Status = IoctlCheckNetNumber ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_SHOWNICINFO: + + Irp->IoStatus.Status = IoctlShowNicInfo ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_ZERONICSTATISTICS: + + Irp->IoStatus.Status = IoctlZeroNicStatistics ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_SHOWMEMSTATISTICS: + + Irp->IoStatus.Status = IoctlShowMemStatistics ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_GETWANINNACTIVITY: + + Irp->IoStatus.Status = IoctlGetWanInactivity ( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_SETWANGLOBALADDRESS: + + Irp->IoStatus.Status = IoctlSetWanGlobalNet( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + case IOCTL_IPXROUTER_DELETEWANGLOBALADDRESS: + + Irp->IoStatus.Status = IoctlDeleteWanGlobalAddress( + ioBuffer, + inputBufferLength, + outputBufferLength, + &Irp->IoStatus.Information + ); + break; + + default: + + RtPrint (DBG_INIT, ("IPXROUTER: unknown IRP_MJ_DEVICE_CONTROL\n")); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + break; + + } + + break; + } + + + // + // DON'T get cute and try to use the status field of + // the irp in the return status. That IRP IS GONE as + // soon as you call IoCompleteRequest. + // + + ntStatus = Irp->IoStatus.Status; + + IoCompleteRequest(Irp, + IO_NO_INCREMENT); + + + // + // We never have pending operation so always return the status code. + // + + return ntStatus; +} + + + +VOID +RouterUnload( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + +Arguments: + + DriverObject - pointer to a driver object + +Return Value: + + +--*/ +{ + PNICCB niccbp; + USHORT i; + + RouterUnloading = TRUE; + + // stop the global timer + StopRtTimer(); + + // stop the rip timer. If the rip timer work item has already been scheduled + // wait until it completes + StopRipTimer(); + + // stop the routing functionality + RouterStop(); + + // close all nics + for(i=0; iNicClosedEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL + ); + } + } + + // free resources allocated by all nics + for(i=0; iNicClosedEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL + ); + } + } + + // at this point, all rcv pkts are returned to the pool and no new packets + // can be allocated. + // all send packets have been freed and no new send requests are permitted. + + // unbind from the IPX driver + UnbindFromIpxDriver(); + + // free the allocated memory + DestroyNicCbs(); + DestroyRcvPktPool(); + + // + // Delete the device object + // + + RtPrint(DBG_UNLOAD, ("IPXROUTER: unloading\n")); + + IoDeleteDevice (DriverObject->DeviceObject); +} + +LIST_ENTRY displayroutes; + +NTSTATUS +IoctlGetNextRoute(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + PIPX_ROUTE_ENTRY rtep, drtep; + PLIST_ENTRY lep; + + ASSERT(outbufflen >= sizeof(IPX_ROUTE_ENTRY)); + + if(IsListEmpty(&displayroutes)) { + + return STATUS_NO_MORE_ENTRIES; + } + + lep = RemoveHeadList(&displayroutes); + + rtep = CONTAINING_RECORD(lep, IPX_ROUTE_ENTRY, PRIVATE.Linkage); + drtep = (PIPX_ROUTE_ENTRY)iobufferp; + + *drtep = *rtep; + *bytestransfp = sizeof(IPX_ROUTE_ENTRY); + + ExFreePool(rtep); + + return STATUS_SUCCESS; +} + +NTSTATUS +IoctlSnapRoutes(VOID) +{ + PIPX_ROUTE_ENTRY rtep, drtep; + UINT i; + KIRQL oldirql; + + InitializeListHead(&displayroutes); + + for(i=0; iPRIVATE.Linkage); + + while((rtep = IpxGetNextRoute(i)) != NULL) { + + drtep = ExAllocatePool(NonPagedPool, sizeof(IPX_ROUTE_ENTRY)); + *drtep = *rtep; + InsertTailList(&displayroutes, &drtep->PRIVATE.Linkage); + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[i], oldirql); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +IoctlCheckNetNumber(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + UINT seg; + UCHAR CheckNetwork[4]; + KIRQL oldirql; + + ASSERT(outbufflen >= sizeof(ULONG)); + + memcpy(CheckNetwork, iobufferp, 4); + + // set the output to no-conflict + *(PULONG)iobufferp = 1; + + seg = IpxGetSegment(CheckNetwork); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[seg], &oldirql); + + if(IpxGetRoute(seg, CheckNetwork)) { + + *(PULONG)iobufferp = 0; + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + *bytestransfp = sizeof(ULONG); + return STATUS_SUCCESS; +} + +NTSTATUS +IoctlShowNicInfo(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + PNICCB niccbp; + USHORT index; + USHORT i; + PSHOW_NIC_INFO nisp; + + ASSERT(outbufflen >= sizeof(SHOW_NIC_INFO)); + + index = *(PUSHORT)iobufferp; + + for(i=0; iDeviceType == IPX_ROUTER_INVALID_DEVICE_TYPE) { + + // skip the non configured nic + continue; + } + + // configured nic + if(index--) { + + // skip this + continue; + } + + // configured nic and index == 0 + nisp = (PSHOW_NIC_INFO)iobufferp; + + nisp->NicId = niccbp->NicId; + + if(niccbp->DeviceType == NdisMediumWan) { + + nisp->DeviceType = SHOW_NIC_WAN; + } + else + { + nisp->DeviceType = SHOW_NIC_LAN; + } + + nisp->NicState = niccbp->NicState; + memcpy(nisp->Network, niccbp->Network, 4); + memcpy(nisp->Node, niccbp->Node, 6); + nisp->TickCount = niccbp->TickCount; + nisp->StatBadReceived = niccbp->StatBadReceived; + nisp->StatRipReceived = niccbp->StatRipReceived; + nisp->StatRipSent = niccbp->StatRipSent; + nisp->StatRoutedReceived = niccbp->StatRoutedReceived; + nisp->StatRoutedSent = niccbp->StatRoutedSent; + nisp->StatType20Received = niccbp->StatType20Received; + nisp->StatType20Sent = niccbp->StatType20Sent; + + *bytestransfp = sizeof(SHOW_NIC_INFO); + return STATUS_SUCCESS; + } + + return STATUS_NO_MORE_ENTRIES; +} + +NTSTATUS +IoctlZeroNicStatistics(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + PNICCB niccbp; + USHORT i; + + for(i=0; iDeviceType == IPX_ROUTER_INVALID_DEVICE_TYPE) { + + // skip the non configured nic + continue; + } + + // configured nic + ZeroNicStatistics(niccbp); + } + + StatMemPeakCount = 0; + + return STATUS_SUCCESS; +} + +NTSTATUS +IoctlShowMemStatistics(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + PSHOW_MEM_STAT smsp; + + smsp = (PSHOW_MEM_STAT)iobufferp; + + smsp->PeakPktAllocCount = StatMemPeakCount; + smsp->CurrentPktAllocCount = StatMemAllocCount; + smsp->CurrentPktPoolCount = RcvPktCount; + smsp->PacketSize = UlongMaxFrameSize * sizeof(ULONG); + + *bytestransfp = sizeof(SHOW_MEM_STAT); + + return STATUS_SUCCESS; +} + +NTSTATUS +IoctlGetWanInactivity(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + PGET_WAN_INNACTIVITY pgwi; + PNICCB niccbp; + USHORT i; + + pgwi = (PGET_WAN_INNACTIVITY)iobufferp; + + // check that we have a valid NicId + if(pgwi->NicId == 0xFFFF) { + + // get the valid NicId for this remote node + for(i=0; iDeviceType == NdisMediumWan) && + (niccbp->NicState == NIC_ACTIVE)) { + + // check if this is the one we look for + if(!memcmp(pgwi->RemoteNode, niccbp->RemoteNode, 6)) { + + // this is the one + pgwi->NicId = niccbp->NicId; + break; + } + } + } + + // check that we have found the nic + if(pgwi->NicId == 0xFFFF) { + + // ERROR: no nic coresponding to this remote node + goto WanInactivityExit; + } + } + else + { + // check that we have a valid handle indeed + if(pgwi->NicId < MaximumNicCount) { + + // Nic id looks valid + niccbp = NicCbPtrTab[pgwi->NicId]; + + if(memcmp(pgwi->RemoteNode, niccbp->RemoteNode, 6)) { + + // ERROR: this nic id has a wrong remote node + // reset the nic id to indicate the error + pgwi->NicId = 0xFFFF; + + goto WanInactivityExit; + } + } + else + { + // ERROR: wrong nic id -> too big + // reset the nicid to indicate the error + pgwi->NicId = 0xFFFF; + + goto WanInactivityExit; + } + } + + // we got the correct nic id + pgwi->WanInnactivityCount = IpxGetWanInactivity(niccbp->NicId); + +WanInactivityExit: + + *bytestransfp = sizeof(GET_WAN_INNACTIVITY); + + return STATUS_SUCCESS; +} + +//*** +// +// Function: IoctlSetWanGlobalNet +// +// Descr: Called by ipxcp when the dll gets loaded, if configured with +// global wan net option. +// It generates a net number using the last four bytes of the address of +// the first lan net, checks that the net number is unique and then adds it +// to the routing table and marks the route as wan global. +// Returns the wan global net number to the caller. +// +//*** + +NTSTATUS +IoctlSetWanGlobalNet(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + UCHAR wnet[4]; // wan global net number + USHORT i; + PNICCB niccbp; + UINT seg; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + PSET_WAN_GLOBAL_ADDRESS wgap; + BOOLEAN statconfig; // TRUE -> static net config + // FALSE -> dynamic net config + ULONG wnetnumber; + LARGE_INTEGER tickcount; + + // check if we have already been called to configure the router for a WanGlobalNet. + // If this has hapened, clean up before seting the new wan global network number + DeleteGlobalWanNet(); + + // get the wnet desired value from the ioctl request + wgap = (PSET_WAN_GLOBAL_ADDRESS)iobufferp; + memcpy(wnet, wgap->WanGlobalNetwork, 4); + + // assume success and set the final error code + wgap->ErrorCode = 0; + *bytestransfp = sizeof(SET_WAN_GLOBAL_ADDRESS); + + // check if this is a static value or dynamic config is wanted + if(!memcmp(wnet, nulladdress, 4)) { + + // wnet is null -> we are requested to make the address + // get the node address of the first LAN card and make the net address with its last + // four bytes + + // Put the tick count value in case we don't find a LAN card ! + KeQueryTickCount(&tickcount); + PUTULONG2LONG(wnet, tickcount.LowPart); + + statconfig = FALSE; + + for(i=0; iDeviceType != IPX_ROUTER_INVALID_DEVICE_TYPE) && // configured nic + (niccbp->DeviceType != NdisMediumWan)) { // LAN nic + + memcpy(wnet, &niccbp->Node[2], 4); + break; + } + } + + } + else + { + // wnet is the number the user requests + statconfig = TRUE; + } + + // Check that the static/dynamic wan global net nr is a unique net number + seg = IpxGetSegment(wnet); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[seg], &oldirql); + + while(IpxGetRoute(seg, wnet)) { + + // the network number exists -> we are allowed to resolve the conflict only in + // the case we are configuring dynamically + if(!statconfig) { + + // increment the number and try again + GETLONG2ULONG(&wnetnumber, wnet); + wnetnumber++; + PUTULONG2LONG(wnet, wnetnumber); + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + seg = IpxGetSegment(wnet); + // RELOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[seg], &oldirql); + } + else + { + // return and report the error + wgap->ErrorCode = ERROR_IPXCP_NETWORK_NUMBER_IN_USE; + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + return STATUS_SUCCESS; + } + } + + // there is no such net, add it + if((rtep = ExAllocatePool(NonPagedPool, sizeof(IPX_ROUTE_ENTRY))) == NULL) { + + // can't allocate the route entry -> return + wgap->ErrorCode = ERROR_IPXCP_MEMORY_ALLOCATION_FAILURE; + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + return STATUS_SUCCESS; + } + + // set up the new route entry + memcpy(rtep->Network, wnet, IPX_NET_LEN); + + rtep->NicId = 0xFFFE; // that's what we use for the global wan net + + memcpy(rtep->NextRouter, nulladdress, IPX_NODE_LEN); + rtep->Flags = IPX_ROUTER_LOCAL_NET | IPX_ROUTER_PERMANENT_ENTRY | IPX_ROUTER_GLOBAL_WAN_NET; + rtep->Timer = 0; // TTL of this route entry is 3 min + rtep->Segment = seg; + rtep->TickCount = DEFAULT_WAN_GLOBAL_NET_TICKCOUNT; + rtep->HopCount = 1; + + InitializeListHead(&rtep->AlternateRoute); + + RtPrint(DBG_INIT, ("IpxRouter: IoctlSetWanGlobalNet: Adding route entry for global WAN net %x-%x-%x-%x \n", + wnet[0], + wnet[1], + wnet[2], + wnet[3])); + + IpxAddRoute(seg, rtep); + + // set our global variable to indicate we have a global wan net + WanGlobalNetworkEnabled = TRUE; + memcpy(WanGlobalNetwork, rtep->Network, 4); + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + // Broadcast the new route entry on all the LAN segments + BroadcastWanNetUpdate(rtep, NULL, NULL); + + // copy the net and return + memcpy(wgap->WanGlobalNetwork, wnet, IPX_NET_LEN); + + return STATUS_SUCCESS; +} + +VOID +DeleteGlobalWanNet(VOID) +{ + UINT seg; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + + // if configured with a wan global network, delete and free the route entry now + if(WanGlobalNetworkEnabled) { + + WanGlobalNetworkEnabled = FALSE; + + seg = IpxGetSegment(WanGlobalNetwork); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[seg], &oldirql); + + if(rtep = IpxGetRoute(seg, WanGlobalNetwork)) { + + IpxDeleteRoute(seg, rtep); + + RtPrint(DBG_INIT, ("IpxRouter: DeleteGlobalWanNet: Deleted wan global net route entry\n")); + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + if(rtep) { + + ExFreePool(rtep); + } + } +} + +NTSTATUS +IoctlDeleteWanGlobalAddress(PVOID iobufferp, + ULONG inbufflen, + ULONG outbufflen, + PULONG bytestransfp) +{ + // If we are called with this IOCtl, it means IPXCP has been reconfigured to + // to use static/dynamic wan nets allocation and NOT the global wan net. + // If the router had been configured previously for the global wan net, delete the + // net and reconfigure now. + DeleteGlobalWanNet(); + + return STATUS_SUCCESS; +} diff --git a/private/ntos/tdi/isn/rip/driver.h b/private/ntos/tdi/isn/rip/driver.h new file mode 100644 index 000000000..a75b65ba4 --- /dev/null +++ b/private/ntos/tdi/isn/rip/driver.h @@ -0,0 +1,150 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + driver.h + +Abstract: + + +Environment: + + kernel & User mode + +Notes: + + +Revision History: + +--*/ + + +// +// Define the various device type values. Note that values used by Microsoft +// Corporation are in the range 0-32767, and 32768-65535 are reserved for use +// by customers. +// + +#define FILE_DEVICE_IPXROUTER 0x00008000 + + + +// +// Macro definition for defining IOCTL and FSCTL function control codes. Note +// that function codes 0-2047 are reserved for Microsoft Corporation, and +// 2048-4095 are reserved for customers. +// + +#define IPXROUTER_IOCTL_INDEX (ULONG)0x00000800 + + +// +// Define our own private IOCTLs +// + +#define IOCTL_IPXROUTER_SNAPROUTES CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+1,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_GETNEXTROUTE CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+2,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_CHECKNETNUMBER CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+3,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_SHOWNICINFO CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+4,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_ZERONICSTATISTICS CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+5,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_SHOWMEMSTATISTICS CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+6,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_GETWANINNACTIVITY CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+7,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_SETWANGLOBALADDRESS CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+8,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_IPXROUTER_DELETEWANGLOBALADDRESS CTL_CODE(FILE_DEVICE_IPXROUTER, \ + IPXROUTER_IOCTL_INDEX+9,\ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + + +//*** Nic Info Ioctl Data *** + +#define SHOW_NIC_LAN 0 +#define SHOW_NIC_WAN 1 + +#define SHOW_NIC_CLOSED 0 +#define SHOW_NIC_CLOSING 1 +#define SHOW_NIC_ACTIVE 2 +#define SHOW_NIC_PENDING_OPEN 3 + +typedef struct _SHOW_NIC_INFO { + + USHORT NicId; + USHORT DeviceType; + USHORT NicState; + UCHAR Network[4]; + UCHAR Node[6]; + USHORT TickCount; + ULONG StatBadReceived; + ULONG StatRipReceived; + ULONG StatRipSent; + ULONG StatRoutedReceived; + ULONG StatRoutedSent; + ULONG StatType20Received; + ULONG StatType20Sent; + } SHOW_NIC_INFO, *PSHOW_NIC_INFO; + +//*** Memory Statistics Data *** + +typedef struct _SHOW_MEM_STAT { + + ULONG PeakPktAllocCount; + ULONG CurrentPktAllocCount; + ULONG CurrentPktPoolCount; + ULONG PacketSize; + } SHOW_MEM_STAT, *PSHOW_MEM_STAT; + +//*** Wan Innactivity Data *** +// For the first call the NicId is set to 0xffff. The router will associate +// the remote node with a valid nic id, which will be used in subsequent calls. + +typedef struct _GET_WAN_INNACTIVITY { + + USHORT NicId; + UCHAR RemoteNode[6]; + ULONG WanInnactivityCount; + } GET_WAN_INNACTIVITY, *PGET_WAN_INNACTIVITY; + +//*** Wan Global Address Data *** + +#define ERROR_IPXCP_NETWORK_NUMBER_IN_USE 1 +#define ERROR_IPXCP_MEMORY_ALLOCATION_FAILURE 2 + +typedef struct _SET_WAN_GLOBAL_ADDRESS { + + UCHAR WanGlobalNetwork[4]; + ULONG ErrorCode; + } SET_WAN_GLOBAL_ADDRESS, *PSET_WAN_GLOBAL_ADDRESS; diff --git a/private/ntos/tdi/isn/rip/globals.c b/private/ntos/tdi/isn/rip/globals.c new file mode 100644 index 000000000..8480f1073 --- /dev/null +++ b/private/ntos/tdi/isn/rip/globals.c @@ -0,0 +1,86 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: globals.c +// +// Description: global configuration parameters and data structures +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +//*** Router Driver State *** + +// 1. Initialization Flag - no receives are accepted as long as the driver is +// not initialized +// FALSE - not initialized, TRUE - initialized + +BOOLEAN RouterInitialized = FALSE; + +// 2. Unloading Flag - indicates that driver unloading is taking place +// FALSE - not unloading, TRUE - unloading + +BOOLEAN RouterUnloading = FALSE; + +//*** Router Type - LAN-WAN-LAN or Client/Server *** + +BOOLEAN LanWanLan = FALSE; + +//*** Enable LAN to LAN routing on the same machine *** +// by default, this is disabled in the first RAS only version *** +ULONG EnableLanRouting = 0; + +//*** some auxiliary data + +UCHAR nulladdress[] = {0, 0, 0, 0, 0, 0}; +UCHAR bcastaddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +// +//*** Routing Table auxiliary structures *** +// + +UINT SegmentCount; // nr of segments (hash buckets) of the RT +PKSPIN_LOCK SegmentLocksTable; // points to the array of segment locks for RT + +// frame size +ULONG MaxFrameSize = DEF_MAX_FRAME_SIZE; + +// MAC header needed +ULONG MacHeaderNeeded = 40; + +// RIP requests/responses queue + +NDIS_SPIN_LOCK RipPktsListLock; +LIST_ENTRY RipPktsList; + +// Propagated & net up bcast control structures + +NDIS_SPIN_LOCK PropagatedPktsListLock; +LIST_ENTRY PropagatedPktsList; + +// this dpc initialized with the SendNext function +KDPC PropagatedPktsDpc; +BOOLEAN PropagatedPktsDpcQueued = FALSE; + +//*** Entry Points into the IPX stack *** + +IPX_INTERNAL_SEND IpxSendPacket; +IPX_INTERNAL_GET_SEGMENT IpxGetSegment; +IPX_INTERNAL_GET_ROUTE IpxGetRoute; +IPX_INTERNAL_ADD_ROUTE IpxAddRoute; +IPX_INTERNAL_DELETE_ROUTE IpxDeleteRoute; +IPX_INTERNAL_GET_FIRST_ROUTE IpxGetFirstRoute; +IPX_INTERNAL_GET_NEXT_ROUTE IpxGetNextRoute; +// +// [BUGBUGZZ] remove since NdisWan does it. +// +IPX_INTERNAL_INCREMENT_WAN_INACTIVITY IpxIncrementWanInactivity; +IPX_INTERNAL_QUERY_WAN_INACTIVITY IpxGetWanInactivity; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData; diff --git a/private/ntos/tdi/isn/rip/globals.h b/private/ntos/tdi/isn/rip/globals.h new file mode 100644 index 000000000..8fc037708 --- /dev/null +++ b/private/ntos/tdi/isn/rip/globals.h @@ -0,0 +1,369 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: globals.h +// +// Description: global routines and data structures +// +// Author: Stefan Solomon (stefans) October 18, 1993. +// +// Revision History: +// +//*** + +//*** Router Driver State *** + +// 1. Initialization Flag +// FALSE - not initialized, TRUE - initialized + +extern BOOLEAN RouterInitialized; + +// 2. Unloading Flag - indicates that driver unloading is taking place +// FALSE - not unloading, TRUE - unloading + +extern BOOLEAN RouterUnloading; + +// Router Role - Total or partial connectivity + +extern BOOLEAN LanWanLan; + +// LAN routing on the same machine - disabled for RAS + +extern ULONG EnableLanRouting; + +// Netbios Routing enable/disable + +extern ULONG NetbiosRouting; + +// max nic count +extern USHORT MaximumNicCount; + +//*** some auxiliary data + +extern UCHAR nulladdress[]; +extern UCHAR bcastaddress[]; + +// +//*** Routing Table auxiliary structures *** +// + +extern UINT SegmentCount; // nr of segments (hash buckets) of the RT +extern PKSPIN_LOCK SegmentLocksTable; // points to the array of segment locks for RT + +// frame size +extern ULONG MaxFrameSize; + +// MAC header needed +extern ULONG MacHeaderNeeded; + + +// RIP requests/responses queue + +NDIS_SPIN_LOCK RipPktsListLock; +LIST_ENTRY RipPktsList; + +// Propagated & net up bcast control structures + +extern NDIS_SPIN_LOCK PropagatedPktsListLock; +extern LIST_ENTRY PropagatedPktsList; + +// this dpc initialized with the SendNext function +extern KDPC PropagatedPktsDpc; +extern BOOLEAN PropagatedPktsDpcQueued; + +// rcv pkt pool size as one of: small(1), medium(2), large(3). (config parameter) +extern UINT RcvPktPoolSize; + +// the number of receive packets per pool segment (config parameter) +extern UINT RcvPktsPerSegment; + +// memory statistics: peak allocation counter +extern ULONG StatMemPeakCount; +extern ULONG StatMemAllocCount; + +extern UINT RcvPktCount; // total pkts allocated for the + +// Max frame size as a multiple of ULONGs +extern UINT UlongMaxFrameSize; + +//*** max send pkts queued limit: over this limit the send pkts get discarded +extern ULONG MaxSendPktsQueued; + +//*** Entry Points into the IPX stack *** + +IPX_INTERNAL_SEND IpxSendPacket; +IPX_INTERNAL_GET_SEGMENT IpxGetSegment; +IPX_INTERNAL_GET_ROUTE IpxGetRoute; +IPX_INTERNAL_ADD_ROUTE IpxAddRoute; +IPX_INTERNAL_DELETE_ROUTE IpxDeleteRoute; +IPX_INTERNAL_GET_FIRST_ROUTE IpxGetFirstRoute; +IPX_INTERNAL_GET_NEXT_ROUTE IpxGetNextRoute; +// +// [BUGBUGZZ] remove since NdisWan does it. +// +IPX_INTERNAL_INCREMENT_WAN_INACTIVITY IpxIncrementWanInactivity; +IPX_INTERNAL_QUERY_WAN_INACTIVITY IpxGetWanInactivity; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData; + +extern PNICCB *NicCbPtrTab; + +extern USHORT VirtualNicId; +extern UCHAR VirtualNetwork[4]; + +//*** Global Functions *** + +NTSTATUS +BindToIpxDriver(PIPX_INTERNAL_BIND_RIP_OUTPUT *IpxBindBuffpp); + +VOID +UnbindFromIpxDriver(VOID); + +NTSTATUS +RouterInit(PIPX_INTERNAL_BIND_RIP_OUTPUT IpxBindBuffp); + +VOID +InitRtTimer(VOID); + +VOID +StartRtTimer(VOID); + +VOID +StopRtTimer(VOID); + +UINT +CreateRcvPktPool(VOID); + +VOID +DestroyRcvPktPool(VOID); + +VOID +RcvPktPoolScavenger(VOID); + + +UINT +CreateNicCbs(PIPX_INTERNAL_BIND_RIP_OUTPUT IpxBindBuffp); + +VOID +DestroyNicCbs(VOID); + +PPACKET_TAG +AllocateRcvPkt(PNICCB niccbp); + +VOID +FreeRcvPkt(PPACKET_TAG pktp); + +NTSTATUS +RouterStart(VOID); + +BOOLEAN +RtReceive ( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterCtx, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl +); + +VOID +RtReceiveComplete ( + IN USHORT NicId +); + +VOID +RtStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength +); + +VOID +RtSendComplete ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +); + +VOID +RtTransferDataComplete ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred +); + +VOID +RtFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute +); + +VOID +RtLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData +); + +VOID +RtLineDown ( + IN USHORT NicId, + IN ULONG FwdAdapterCtx +); + +VOID +RtScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry +); + +VOID +ProcessNbPacket(PPACKET_TAG pktp); + +VOID +ProcessRipPacket(PPACKET_TAG pktp); + +VOID +RoutePacket(PPACKET_TAG pktp); + +VOID +SendPacket(PPACKET_TAG pktp); + +VOID +InitRipSndDispatcher(VOID); + +VOID +InitRipSndAtNic(PNICCB niccbp); + +VOID +RipDispatchSndReq(PRIP_SNDREQ sndreqp); + +BOOLEAN +RipQueueSndReqAtNic(PNICCB niccbp, + PRIP_SNDREQ sndreqp); + +VOID +SendRipPktCompleted(PPACKET_TAG pktp); + +PIPX_ROUTE_ENTRY +GetRoute(UINT segment, + BOOLEAN FirstRoute); + +VOID +SetNetworkEntry(PUCHAR nep, + PIPX_ROUTE_ENTRY rtep); + +VOID +InitRipTimer(VOID); + +VOID +RipTimer(VOID); + +VOID +EnableRcvPktAllocation(PNICCB niccbp, + BOOLEAN enab_mode); + +BOOLEAN +IsRipSndResourceFree(PNICCB niccbp); + +BOOLEAN +IsRcvPktResourceFree(PNICCB niccbp); + +VOID +RipSendAtNicCompleted(PRIP_SNDREQ sndreqp); + +NIC_OPEN_STATUS +NicOpen(PNICCB niccbp); + +NIC_CLOSE_STATUS +NicClose(PNICCB niccbp, + USHORT CloseCompletionOption); + +UINT +AddRouteToBcastSndReq(PLIST_ENTRY nodelistp, + PIPX_ROUTE_ENTRY rtep); + +PRIP_SNDREQ +GetBcastSndReq(PLIST_ENTRY nodelistp, + PUSHORT NicIdp); + +VOID +RouterStop(VOID); + +VOID +StopRipTimer(); + +NIC_RESOURCES_STATUS +NicFreeResources(PNICCB niccbp); + +VOID +BroadcastRipUpdate(PRIP_SNDREQ sndreqp, // send request + PNICCB niccbp, // do not send on this nic + PKEVENT eventp); // wait if this event is not NULL +VOID +BroadcastRipGeneralResponse(PRIP_SNDREQ sndreqp); + +VOID +NicCloseComplete(PNICCB niccbp); + +USHORT +tickcount(UINT linkspeed); + +VOID +WanGenRequestTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + + +BOOLEAN +IsNetInNbPacket(PPACKET_TAG pktp, + PNICCB niccbp); +VOID +SendPropagatedPacket(PPACKET_TAG pktp); + +VOID +SendNextPropagatedPkt(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +VOID +ZeroNicStatistics(PNICCB niccbp); + +VOID +InitWanNodeHT(VOID); + +PNICCB +GetWanNodeNiccbp(PUCHAR nodep); + +VOID +AddWanNodeToHT(PNICCB niccbp); + +VOID +RemoveWanNodeFromHT(PNICCB niccbp); + +extern BOOLEAN WanGlobalNetworkEnabled; +extern UCHAR WanGlobalNetwork[]; + +VOID +InitNetbiosRoutingFilter(VOID); + +BOOLEAN +IsNetbiosRoutingAllowed(PNICCB srcniccbp, + PNICCB dstniccbp); + + +VOID +BroadcastWanNetUpdate(PIPX_ROUTE_ENTRY rtep, // route entry to bcast + PNICCB niccbp, // do not send on this nic + PKEVENT eventp); // synch event + +VOID +SendGenRequestOnWanClient(VOID); diff --git a/private/ntos/tdi/isn/rip/init.c b/private/ntos/tdi/isn/rip/init.c new file mode 100644 index 000000000..a5a4cb5b8 --- /dev/null +++ b/private/ntos/tdi/isn/rip/init.c @@ -0,0 +1,174 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: init.c +// +// Description: initialize global data structures +// +// Author: Stefan Solomon (stefans) October 5, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + + +//*** +// +// Function: RouterInit +// +// Descr: Initializes global data structures +// +// Parameters: IpxBindBuffp - pointer to the ipx module bind output buffer +// +// Returns: STATUS_SUCCESS +// STATUS_INSUFFICIENT_RESOURCES +// +//*** + +NTSTATUS +RouterInit(PIPX_INTERNAL_BIND_RIP_OUTPUT IpxBindBuffp) +{ + int i; + PIPX_NIC_DATA NicDatap; + UINT MaxLanFrameSize; + BOOLEAN NicCbManInitialized = FALSE; + BOOLEAN RcvPktManInitialized = FALSE; + BOOLEAN RipResponseManInitialized = FALSE; + + + //*** Initialize NicCBs *** + + if(CreateNicCbs(IpxBindBuffp)) { + + goto cleanup; + } + + NicCbManInitialized = TRUE; + + // Initialize the table of handlers into the IPX driver + + IpxSendPacket = IpxBindBuffp->SendHandler; + IpxGetSegment = IpxBindBuffp->GetSegmentHandler; + IpxGetRoute = IpxBindBuffp->GetRouteHandler; + IpxAddRoute = IpxBindBuffp->AddRouteHandler; + IpxDeleteRoute = IpxBindBuffp->DeleteRouteHandler; + IpxGetFirstRoute = IpxBindBuffp->GetFirstRouteHandler; + IpxGetNextRoute = IpxBindBuffp->GetNextRouteHandler; + + // + // [BUGBUGZZ] remove since NdisWan does it. + // + IpxIncrementWanInactivity = IpxBindBuffp->IncrementWanInactivityHandler; + IpxGetWanInactivity = IpxBindBuffp->QueryWanInactivityHandler; + IpxTransferData = IpxBindBuffp->TransferDataHandler; + + // Initialize the Routing Table Auxiliary Structures + + SegmentCount = IpxBindBuffp->SegmentCount; + SegmentLocksTable = IpxBindBuffp->SegmentLocks; + + // get the MAC header needed by the IPX module + MacHeaderNeeded = IpxBindBuffp->MacHeaderNeeded; + + // try to determine the MaxLanFrameSize + MaxLanFrameSize = 0; + + for(i=0, NicDatap = IpxBindBuffp->NicInfoBuffer.NicData; + iNicInfoBuffer.NicCount; + i++, NicDatap++) { + + if((NicDatap->DeviceType != NdisMediumWan) && + (NicDatap->LineInfo.MaximumPacketSize > MaxLanFrameSize)) { + + MaxLanFrameSize = NicDatap->LineInfo.MaximumPacketSize; + } + } + + if(MaxLanFrameSize) { + + MaxFrameSize = MaxLanFrameSize; + } + else + { + RtPrint(DBG_INIT, ("IpxRouter: RouterInit: There are no LAN devices configured !\n")); + } + + //*** Initialize the Rcv Pkt Manager *** + + if(CreateRcvPktPool()) { + + goto cleanup; + } + + RcvPktManInitialized = TRUE; + + //*** Initialize the Netbios Bcast Control Structures *** + + INITIALIZE_SPIN_LOCK(&PropagatedPktsListLock); + InitializeListHead(&PropagatedPktsList); + KeInitializeDpc(&PropagatedPktsDpc, SendNextPropagatedPkt, NULL); + InitNetbiosRoutingFilter(); + + //*** Initialize the RIP requests/responses queue *** + + INITIALIZE_SPIN_LOCK(&RipPktsListLock); + InitializeListHead(&RipPktsList); + + //*** Initialize the RIP Response Manager *** + + InitRipSndDispatcher(); + + //*** Initialize the Wan Nodes Hash Table *** + + InitWanNodeHT(); + + // all done, free the bind output buffer + ExFreePool((PVOID)IpxBindBuffp); + + // initialize the global timer + InitRtTimer(); + + // open all the configured nics + for(i=0; iNicState == NIC_PENDING_OPEN) { + + NicOpen(NicCbPtrTab[i]); + } + } + + return STATUS_SUCCESS; + +cleanup: + + // free all the nonpaged pool memory allocated up to this point + + if(NicCbManInitialized) { + + DestroyNicCbs(); + } + + if(RcvPktManInitialized) { + + DestroyRcvPktPool(); + } + + if(RipResponseManInitialized) { + +/*** !!! taken out temporarily + + DestroyRipResponseMan(); + +***/ + } + + // all cleaned up, free the bind output buffer + ExFreePool(IpxBindBuffp); + + return STATUS_INSUFFICIENT_RESOURCES; +} diff --git a/private/ntos/tdi/isn/rip/ipxbind.c b/private/ntos/tdi/isn/rip/ipxbind.c new file mode 100644 index 000000000..e4da3867e --- /dev/null +++ b/private/ntos/tdi/isn/rip/ipxbind.c @@ -0,0 +1,191 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: ipxbind.c +// +// Description: binding to the IPX driver +// +// Author: Stefan Solomon (stefans) October 18, 1993. +// +// Revision History: +// +//*** + +#include +#include "rtdefs.h" + +NTSTATUS +ReadIpxDeviceName(VOID); + +ULONG +IsWanGlobalNetRequested(VOID); + +// global handle of the IPX driver +HANDLE FileHandle; + +UNICODE_STRING UnicodeFileName; +PWSTR FileNamep; + +NTSTATUS +BindToIpxDriver(PIPX_INTERNAL_BIND_RIP_OUTPUT *IpxBindBuffpp) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PUCHAR bufferp; + UINT outbuflen, inbuflen; + PIPX_INTERNAL_BIND_INPUT bip; + + // Read Ipx exported device name from the registry + ReadIpxDeviceName(); + + InitializeObjectAttributes( + &ObjectAttributes, + &UnicodeFileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + Status = NtCreateFile( + &FileHandle, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L + ); + + if (!NT_SUCCESS(Status)) { + + RtPrint(DBG_INIT, ("IpxRouter: Open of the IPX driver failed with %lx\n", Status)); + return Status; + } + + RtPrint(DBG_INIT, ("IpxRouter: Open of the IPX driver was successful!\n")); + + // First, send a IOCTL to find out how much data we need to allocate + inbuflen = sizeof(IPX_INTERNAL_BIND_INPUT); + if((bip = ExAllocatePool(PagedPool, inbuflen)) == NULL) { + + NtClose(FileHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // fill in our bind data + bip->Version = 1; + bip->Identifier = IDENTIFIER_RIP; + bip->BroadcastEnable = TRUE; + bip->LookaheadRequired = IPXH_HDRSIZE; + bip->ProtocolOptions = 0; + bip->ReceiveHandler = RtReceive; + bip->ReceiveCompleteHandler = RtReceiveComplete; + bip->StatusHandler = RtStatus; + bip->SendCompleteHandler = RtSendComplete; + bip->TransferDataCompleteHandler = RtTransferDataComplete; + bip->FindRouteCompleteHandler = RtFindRouteComplete; + bip->LineUpHandler = RtLineUp; + bip->LineDownHandler = RtLineDown; + bip->ScheduleRouteHandler = RtScheduleRoute; + + if(IsWanGlobalNetRequested()) { + + bip->RipParameters = IPX_RIP_PARAM_GLOBAL_NETWORK; + } + else + { + bip->RipParameters = 0; + } + + Status = NtDeviceIoControlFile( + FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + inbuflen, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + + if (Status == STATUS_PENDING) { + Status=NtWaitForSingleObject( + FileHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (Status != STATUS_BUFFER_TOO_SMALL) { + + RtPrint(DBG_INIT, ("IpxRouter: Ioctl to the IPX driver failed with %lx\n", Status)); + + ExFreePool(bip); + NtClose(FileHandle); + return STATUS_INVALID_PARAMETER; + } + outbuflen = IoStatusBlock.Information; + + if((bufferp = ExAllocatePool(PagedPool, outbuflen)) == NULL) { + + ExFreePool(bip); + NtClose(FileHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + Status = NtDeviceIoControlFile( + FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + bip, // Input Buffer + inbuflen, // Input Buffer Length + bufferp, // Output Buffer + outbuflen); // Output Buffer Length + + + if (Status == STATUS_PENDING) { + Status=NtWaitForSingleObject( + FileHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (Status != STATUS_SUCCESS) { + + RtPrint(DBG_INIT, ("IpxRouter: Ioctl to the IPX driver failed with %lx\n", IoStatusBlock.Status)); + + ExFreePool(bip); + ExFreePool(bufferp); + NtClose(FileHandle); + return Status; + } + + RtPrint(DBG_INIT, ("IpxRouter: Succesfuly bound to the IPX driver\n")); + + ExFreePool(bip); + + *IpxBindBuffpp = (PIPX_INTERNAL_BIND_RIP_OUTPUT)bufferp; + + return Status; +} + + +VOID +UnbindFromIpxDriver(VOID) +{ + NtClose(FileHandle); + ExFreePool(FileNamep); +} diff --git a/private/ntos/tdi/isn/rip/lineind.c b/private/ntos/tdi/isn/rip/lineind.c new file mode 100644 index 000000000..079a98463 --- /dev/null +++ b/private/ntos/tdi/isn/rip/lineind.c @@ -0,0 +1,430 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: lineind.c +// +// Description: WAN connection/disconection routines +// +// Author: Stefan Solomon (stefans) November 20, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +BOOLEAN WanGlobalNetworkEnabled = FALSE;// this is set to TRUE if the router has been + // configured to assign the same global address + // to all wan clients +UCHAR WanGlobalNetwork[4]; + + +VOID +SendWanGenRequest(PNICCB niccbp); + +VOID +StartWanGenRequestTimer(PNICCB niccbp); + +//*** +// +// Function: RtLineUp +// +// Descr: Called when a new WAN connection has been established +// +//*** + +VOID +RtLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData) +{ + PNICCB niccbp; + PIPX_ROUTE_ENTRY rtep, oldrtep; + PIPXCP_CONFIGURATION configp; + UINT segment; + KIRQL oldirql; + + RtPrint(DBG_LINE, ("IpxRouter: RtLineUp: Entered on NicId %d\n", NicId)); + + configp = (PIPXCP_CONFIGURATION)ConfigurationData; + niccbp = NicCbPtrTab[NicId]; + + //*** If the NIC is active, this is just an update. Ignore it for the + // moment + + if(niccbp->NicState != NIC_CLOSED) { + + return; + } + + // + //*** set up the NIC Control Block with the configuration information *** + // + + memcpy(niccbp->Network, configp->Network, 4); + memcpy(niccbp->Node, configp->LocalNode, 6); + memcpy(niccbp->RemoteNode, configp->RemoteNode, 6); + + // set the role of this node in this wan connection + if(configp->ConnectionClient) { + + niccbp->WanConnectionClient = TRUE; + } + else + { + niccbp->WanConnectionClient = FALSE; + } + + niccbp->LinkSpeed = LineInfo->LinkSpeed; + niccbp->TickCount = tickcount(niccbp->LinkSpeed); + niccbp->MaximumPacketSize = LineInfo->MaximumPacketSize; + niccbp->MacOptions = LineInfo->MacOptions; + niccbp->DeviceType = DeviceType; + + // reset statistics counters + ZeroNicStatistics(niccbp); + + // try 3 times to get the other end's routes + niccbp->WanGenRequestCount = 3; + + // now, try to open the nic + if(NicOpen(niccbp) != NIC_OPEN_SUCCESS) { + + // !!! + return; + } + + // + //*** check if we have a global wan route *** + // + if((WanGlobalNetworkEnabled) && + (!niccbp->WanConnectionClient)) { + + // + //*** There is a global WAN route AND this is a "server" node *** + //*** Add the remote "client" node to the WAN Nodes Hash Table *** + // + + AddWanNodeToHT(niccbp); + } + else + { + // + //*** There is no global WAN route OR this is a "client" node *** + //*** Add a new entry in the routing table for the local WAN net *** + // + + if((rtep = ExAllocatePool(NonPagedPool, sizeof(IPX_ROUTE_ENTRY))) == NULL) { + + // can't allocate the route entry -> close the nic and return + NicClose(niccbp, 0); + return; + } + + segment = IpxGetSegment(niccbp->Network); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql); + + // Clean-up any old propagated route with the same net address + // At this point, we do not worry about alternate routes, cause we do + // not add alternate routes in the routing table yet. + if((oldrtep = IpxGetRoute(segment, niccbp->Network)) != NULL) { + + RtPrint(DBG_LINE, ("IpxRouter: RtLineUp: Deleting old route for net %x-%x-%x-%x on NicId %d\n", + niccbp->Network[0], + niccbp->Network[1], + niccbp->Network[2], + niccbp->Network[3], + oldrtep->NicId)); + + IpxDeleteRoute(segment, oldrtep); + ExFreePool(oldrtep); + } + + // set up the new route entry + memcpy(rtep->Network, niccbp->Network, IPX_NET_LEN); + rtep->NicId = niccbp->NicId; + memcpy(rtep->NextRouter, niccbp->Node, IPX_NODE_LEN); + rtep->Flags = IPX_ROUTER_LOCAL_NET; + rtep->Timer = 0; // TTL of this route entry is 3 min + rtep->Segment = segment; + rtep->TickCount = niccbp->TickCount; + rtep->HopCount = 1; + + InitializeListHead(&rtep->AlternateRoute); + + RtPrint(DBG_LINE, ("IpxRouter: RtLineUp: Adding new route for net %x-%x-%x-%x on NicId %d\n", + niccbp->Network[0], + niccbp->Network[1], + niccbp->Network[2], + niccbp->Network[3], + niccbp->NicId)); + + IpxAddRoute(segment, rtep); + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + // Broadcast the new route entry on all the LAN segments + BroadcastWanNetUpdate(rtep, niccbp, NULL); + } + + // + //*** Get the WAN peer's routing table info. *** + // + + // Send a general request on this net and then send more general requests + // with a timer. + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + if(niccbp->NicState != NIC_ACTIVE) { + + // nic has been closed while we were opening; reset the counter + // to show to the closing timer that the wan request timer will not + // get scheduled. + niccbp->WanGenRequestCount = 0; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return; + } + + --niccbp->WanGenRequestCount; + + if(niccbp->WanGenRequestCount) { + + // we have at least one more request to send beside the one we are + // sending now -> start the timer + StartWanGenRequestTimer(niccbp); + } + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + SendWanGenRequest(niccbp); +} + +//*** +// +// Function: RtLineDown +// +// Descr: Called when a WAN line has been disconnected +// +//*** + +VOID +RtLineDown (IN USHORT NicId, IN ULONG FwdAdapterCtx) +{ + LIST_ENTRY DownRoutesList; + UINT seg; + BOOLEAN FirstRoute; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + PRIP_SNDREQ sndreqp; + PNICCB niccbp; + USHORT DownNicId; + + RtPrint(DBG_LINE, ("IpxRouter: RtLineDown: Entered for NicId %d\n", NicId)); + + + // get the nic ptr for this snd req + niccbp = NicCbPtrTab[NicId]; + + if (niccbp->NicState!=NIC_ACTIVE) { + RtPrint (DBG_LINE, ("IpxRouter: NicId %d is already inactive\n", NicId)); + return; + } + // scan the routing table and delete all the routing table entries + // visible through this nic. + InitializeListHead(&DownRoutesList); + + for(seg=0; segFlags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // skip it! + continue; + } + + // check if this route entry is based on the nic going down + if(rtep->NicId == NicId) { + + IpxDeleteRoute(seg, rtep); + + // mark it as down + rtep->HopCount = 16; + + // add the route to the packets we prepare for bcast + AddRouteToBcastSndReq(&DownRoutesList, rtep); + + // finally, free the route entry + ExFreePool(rtep); + } + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + } // for all segments + + // broadcast all the deleted routes + if((sndreqp = GetBcastSndReq(&DownRoutesList, &DownNicId)) != NULL) { + + // set up the request to send a bcast response with the changes + // to all the nics except this one + BroadcastRipUpdate(sndreqp, niccbp, NULL); + } + + // + //*** If this is a "server" node and global WAN net if it has a global Wan net + //*** for remote clients, remove the client node form the wan nodes hash table + // + if((WanGlobalNetworkEnabled) && + (!niccbp->WanConnectionClient)) { + + // + //*** There is a global WAN route AND this is a "server" node *** + //*** Add the remote "client" node to the WAN Nodes Hash Table *** + // + + RemoveWanNodeFromHT(niccbp); + } + + // finally, close the nic + NicClose(niccbp, 0); + + return; +} + + +VOID +SendWanGenRequest(PNICCB niccbp) +{ + PRIP_SNDREQ sndrqp; + + if((sndrqp = ExAllocatePool(NonPagedPool, sizeof(RIP_SNDREQ))) == NULL) { + + return; + } + + sndrqp->SndReqId = RIP_GEN_REQUEST; + sndrqp->SendOnAllNics = FALSE; // send to this nic only + memcpy(sndrqp->DestNode, bcastaddress, IPX_NODE_LEN); + sndrqp->DestSock = IPX_RIP_SOCKET; + sndrqp->DoNotSendNicCbp = NULL; // do no except any nic + sndrqp->SenderNicCbp = niccbp; + sndrqp->SndCompleteEventp = NULL; + + RipQueueSndReqAtNic(niccbp, sndrqp); +} + + +VOID +WanGenRequestTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PNICCB niccbp; + + niccbp = (PNICCB)DefferedContext; + + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + if(niccbp->NicState != NIC_ACTIVE) { + + // reset the request counter to show that we won't reschedule + // the timer + niccbp->WanGenRequestCount = 0; + + RtPrint(DBG_LINE, ("IpxRouter: WanGenRequestTimeout: closing done on NicId %d\n", niccbp->NicId)); + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return; + } + + --niccbp->WanGenRequestCount; + + RtPrint(DBG_LINE, ("IpxRouter: WanGenRequestTimeout: sending gen req on NicId %d remaining %d\n", niccbp->NicId, niccbp->WanGenRequestCount)); + + if(niccbp->WanGenRequestCount) { + + StartWanGenRequestTimer(niccbp); + } + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + SendWanGenRequest(niccbp); +} + +//*** +// +// Function: StartWanGenRequestTimer +// +// Descr: Starts the timer for 2 sec at this Nic Cb +// +// Params: pointer to Nic Cb +// +// Returns: none +// +//*** + +VOID +StartWanGenRequestTimer(PNICCB niccbp) +{ + LARGE_INTEGER timeout; + + timeout.LowPart = (ULONG)(-4000 * 10000L); // 4 sec + timeout.HighPart = -1; + + KeSetTimer(&niccbp->WanGenRequestTimer, timeout, &niccbp->WanGenRequestDpc); +} + +//*** +// +// Function: SendGenRequestOnWanClient +// +// Descr: Sends periodical gen requests on the WAN client to get the +// remote router's updates +// +//*** + +VOID +SendGenRequestOnWanClient(VOID) +{ + PNICCB niccbp; + USHORT i; + + // scan all nics to find the wan client nic active (if any) + for(i=0; iNicState == NIC_ACTIVE) && + (niccbp->DeviceType == NdisMediumWan) && + (niccbp->WanConnectionClient)) { + + // send a RIP General Request on this NIC + // There is only one client NIC (or none) per router + SendWanGenRequest(niccbp); + + return; + } + } +} diff --git a/private/ntos/tdi/isn/rip/mp/makefile b/private/ntos/tdi/isn/rip/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/rip/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/rip/mp/sources b/private/ntos/tdi/isn/rip/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/rip/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/rip/nbproc.c b/private/ntos/tdi/isn/rip/nbproc.c new file mode 100644 index 000000000..50b6e1d58 --- /dev/null +++ b/private/ntos/tdi/isn/rip/nbproc.c @@ -0,0 +1,38 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: nbproc.c +// +// Description: process netbios propagated packets +// +// Author: Stefan Solomon (stefans) October 11, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +//*** +// +// Function: ProcessNbPacket +// +// Descr: +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +ProcessNbPacket(PPACKET_TAG rcvpktp) +{ + // STUB !!! + FreeRcvPkt(rcvpktp); + + return; +} diff --git a/private/ntos/tdi/isn/rip/netbios.c b/private/ntos/tdi/isn/rip/netbios.c new file mode 100644 index 000000000..0664164e8 --- /dev/null +++ b/private/ntos/tdi/isn/rip/netbios.c @@ -0,0 +1,155 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: netbios.c +// +// Description: process netbios broadcast packets +// +// Author: Stefan Solomon (stefans) December 16, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +ULONG NetbiosRouting = 0; //enable netbios routing + +VOID +ProcessNbPacket(PPACKET_TAG pktp) +{ + UCHAR rtcount; + PUCHAR hdrp; + PNICCB niccbp; + + if(!NetbiosRouting) { + + // discard + FreeRcvPkt(pktp); + return; + } + + // get a pointer to the IPX header + hdrp = pktp->DataBufferp; + + // get a pointer to the packet owner NicCb + niccbp = pktp->PacketOwnerNicCbp; + + RtPrint(DBG_NETBIOS, ("IpxRouter: ProcessNbPacket: recvd pkt 0x%x on Nic %d\n", pktp, niccbp->NicId)); + + // get the number of routers this packet has crossed + rtcount = *(hdrp + IPXH_XPORTCTL); + + // check that this is a netbios bcast packet and didnt exceed the limit of + // routers to traverse + if(memcmp(hdrp + IPXH_DESTNODE, bcastaddress, 6) || + (rtcount >= 8)) { + + // discard + FreeRcvPkt(pktp); + return; + } + + // check if the packet has been sent more then once on this net + if(IsNetInNbPacket(pktp, niccbp)) { + + // discard + FreeRcvPkt(pktp); + return; + } + + // the packet will be broadcasted on all the nets that are LAN and are NOT + // included in the Network Number fields. + + memcpy(hdrp + IPXH_HDRSIZE + 4 * rtcount, + niccbp->Network, + IPX_NET_LEN); + + (*(hdrp + IPXH_XPORTCTL))++; + + // set the destination network in the packet to 0 + memcpy(hdrp + IPXH_DESTNET, nulladdress, 4); + + SendPropagatedPacket(pktp); +} + + +BOOLEAN +IsNetInNbPacket(PPACKET_TAG pktp, + PNICCB niccbp) +{ + UCHAR rtcount, i; + PUCHAR hdrp; + + // get a pointer to the IPX header + hdrp = pktp->DataBufferp; + + // get the number of routers this packet has crossed + rtcount = *(hdrp + IPXH_XPORTCTL); + + for(i=0; iNetwork, + IPX_NET_LEN))) { + + return TRUE; + } + } + + return FALSE; +} + + +// Filter array for netbios routing. The first index is the source, the +// second is the destination. The convention is 0 is LAN and 1 is WAN. +BOOLEAN NetbiosRoutingFilter[2][2] = { FALSE, FALSE, FALSE, FALSE }; + +VOID +InitNetbiosRoutingFilter(VOID) +{ + if(NetbiosRouting & NETBIOS_ROUTING_LAN_TO_LAN) { + + NetbiosRoutingFilter[0][0] = TRUE; + } + + if(NetbiosRouting & NETBIOS_ROUTING_WAN_TO_LAN) { + + NetbiosRoutingFilter[1][0] = TRUE; + } + + if(NetbiosRouting & NETBIOS_ROUTING_LAN_TO_WAN) { + + NetbiosRoutingFilter[0][1] = TRUE; + } +} + +//*** +// +// Function: IsNetbiosRoutingAllowed +// +// Descr: Returns the value in the NetbiosRoutingFilter array corresponding +// to the src and dest device types +//*** + +BOOLEAN +IsNetbiosRoutingAllowed(PNICCB srcniccbp, + PNICCB dstniccbp) +{ + USHORT dst = 0, src = 0; + + if(srcniccbp->DeviceType == NdisMediumWan) { + + src = 1; + } + + if(dstniccbp->DeviceType == NdisMediumWan) { + + dst = 1; + } + + return NetbiosRoutingFilter[src][dst]; +} diff --git a/private/ntos/tdi/isn/rip/nicman.c b/private/ntos/tdi/isn/rip/nicman.c new file mode 100644 index 000000000..424351f2a --- /dev/null +++ b/private/ntos/tdi/isn/rip/nicman.c @@ -0,0 +1,640 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: nicman.c +// +// Description: NicCb management routines +// +// Author: Stefan Solomon (stefans) October 7, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +// counters + +USHORT MaximumNicCount; // total number of Nics possble + +// array of pointers to NicCb indexed by the NicId +PNICCB *NicCbPtrTab; + +PNICCB NicCbArray; + +VOID +ConfigureNicCb(PNICCB niccbp, + PIPX_NIC_DATA nicdatp); + +USHORT VirtualNicId; +UCHAR VirtualNetwork[4]; + +VOID +StartNicCloseTimer(PNICCB niccbp); + +VOID +NicCloseTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + + + +//*** +// +// Function: CreateNicCbs +// +// Descr: creates the NicCbs structures and queues them in their respective +// lists +// +// Params: none +// +// Returns: 0 - success, 1 - failure +// +//*** + +UINT +CreateNicCbs(PIPX_INTERNAL_BIND_RIP_OUTPUT IpxBindBuffp) +{ + ULONG tablen, arraylen; + PNICCB niccbp; + PIPX_NIC_DATA nicdatp; + UINT LanNicsCount = 0; + UINT WanNicsCount = 0; + USHORT ConfiguredNicCount; + USHORT i; + + RtPrint(DBG_INIT, ("IpxRouter: CreateNicCbs: Entered\n")); + + MaximumNicCount = IpxBindBuffp->MaximumNicCount; + ConfiguredNicCount = IpxBindBuffp->NicInfoBuffer.NicCount; + + RtPrint(DBG_INIT, ("IpxRouter: Max Nics Count = %d, Configured Nics Count = %d\n", + MaximumNicCount, ConfiguredNicCount)); + + ASSERT(ConfiguredNicCount <= MaximumNicCount); + + // allocate the NicCbs Ptrs and NicCbs structures tables + tablen = sizeof(PNICCB) * MaximumNicCount; + + if((NicCbPtrTab = (PNICCB *)CTEAllocMem(tablen)) == NULL) { + + // memory allocation failure, abort all + return 1; + } + + // zero the tables + RtlZeroMemory(NicCbPtrTab, tablen); + + arraylen = sizeof(NICCB) * MaximumNicCount; + + if((NicCbArray = (PNICCB)CTEAllocMem(arraylen)) == NULL) { + + // memory allocation failure, abort all + CTEFreeMem(NicCbPtrTab); + return 1; + } + + // zero the tables + RtlZeroMemory(NicCbArray, arraylen); + + // initialize the NicCb pointers table and + // initialize ALL NicCbs to the NIC_CLOSED status + niccbp = NicCbArray; // NicCb table start + + for(i=0; iNicId = i; + InitializeListHead(&niccbp->SendQueue); + InitializeListHead(&niccbp->ReceiveQueue); + + KeInitializeDpc(&niccbp->NicCloseDpc, NicCloseTimeout, niccbp); + KeInitializeTimer(&niccbp->NicCloseTimer); + KeInitializeEvent(&niccbp->NicClosedEvent, NotificationEvent, FALSE); + + // initialize the WanGenRequests sender + KeInitializeDpc(&niccbp->WanGenRequestDpc, WanGenRequestTimeout, niccbp); + KeInitializeTimer(&niccbp->WanGenRequestTimer); + niccbp->WanGenRequestCount = 0; + + INITIALIZE_SPIN_LOCK(&niccbp->NicLock); + + InitRipSndAtNic(niccbp); + + // default -> always enabled + niccbp->WanRoutingDisabled = FALSE; + + niccbp->NicState = NIC_CLOSED; + + // set device type to an invalid type + niccbp->DeviceType = IPX_ROUTER_INVALID_DEVICE_TYPE; + + } + + // configure the NicCbs that we got in the config data buffer + nicdatp = IpxBindBuffp->NicInfoBuffer.NicData; // config data start + + for(i=0; iNicId < MaximumNicCount); + + niccbp = NicCbPtrTab[nicdatp->NicId]; + ConfigureNicCb(niccbp, nicdatp); + + // If this is not a Wan device, open it + if(niccbp->DeviceType != NdisMediumWan) { + + niccbp->NicState = NIC_PENDING_OPEN; + } + +#if DBG + if(niccbp->DeviceType == NdisMediumWan) { + WanNicsCount++; + } + else + { + LanNicsCount++; + } +#endif + + } + + RtPrint(DBG_INIT, ("IpxRouter: CreateNicCbs created %d Lan Nics\n", LanNicsCount)); + RtPrint(DBG_INIT, ("IpxRouter: CreateNicCbs created %d Wan Nics\n", WanNicsCount)); + + // get the virtual numbers, if any + VirtualNicId = IpxBindBuffp->NicInfoBuffer.VirtualNicId; + memcpy(VirtualNetwork, IpxBindBuffp->NicInfoBuffer.VirtualNetwork, IPX_NET_LEN); + + // All Done + return 0; +} + +//*** +// +// Function: DestroyNicCbs +// +// Descr: frees the memory allocated for NicCbs +// +// Params: none +// +// Returns: none +// +//*** + +VOID +DestroyNicCbs(VOID) +{ + UINT i; + + for(i=0; iNicLock); + } + + CTEFreeMem(NicCbPtrTab); + CTEFreeMem(NicCbArray); +} + +//*** +// +// Function: ConfigureNicCb +// +// Descr: Initializes the NicCb data struct +// +// Params: NicCb ptr, Nic data buffer ptr +// +// Returns: none +// +//*** + +VOID +ConfigureNicCb(PNICCB niccbp, + PIPX_NIC_DATA nicdatp) +{ +#if DBG + char *devtypestr; +#endif + + niccbp->NicId = nicdatp->NicId; + memcpy(niccbp->Network, nicdatp->Network, 4); + memcpy(niccbp->Node, nicdatp->Node, 6); + niccbp->LinkSpeed = nicdatp->LineInfo.LinkSpeed; + niccbp->TickCount = tickcount(niccbp->LinkSpeed); + niccbp->MaximumPacketSize = nicdatp->LineInfo.MaximumPacketSize; + niccbp->MacOptions = nicdatp->LineInfo.MacOptions; + niccbp->DeviceType = nicdatp->DeviceType; + + niccbp->SendPktsQueuedCount = 0; + + ZeroNicStatistics(niccbp); + +#if DBG + if(niccbp->DeviceType == NdisMediumWan) { + + devtypestr = "WAN"; + } + else + { + devtypestr = "LAN"; + } +#endif + + // update the WanRoutingDisabled flag from the IPX_NIC_DATA + if(nicdatp->EnableWanRouter) { + + RtPrint(DBG_INIT, ("IpxRouter: Wan Router Enabled for NicId %d\n", niccbp->NicId)); + + niccbp->WanRoutingDisabled = FALSE; + } + else + { + RtPrint(DBG_INIT, ("IpxRouter: Wan Router Disabled for NicId %d\n", niccbp->NicId)); + + niccbp->WanRoutingDisabled = TRUE; + } + + RtPrint(DBG_INIT, ("IpxRouter: Configured Nic %d with DeviceType %s\n", niccbp->NicId, devtypestr)); +} + +USHORT +tickcount(UINT linkspeed) +{ + USHORT tc; + + ASSERT(linkspeed != 0); + + if(linkspeed >= 10000) { + + // link speed >= 1M bps + return 1; + } + else + { + // compute the necessary time to send a 576 bytes packet over this + // line and express it as nr of ticks. + // One tick = 55ms + + tc = 57600 / linkspeed; + tc = tc / 55 + 1; + return tc; + } +} + +//*** +// +// Function: NicClose +// +// Descr: Starts the nic closing operation, as follows: +// 1. Sets the nic state to NIC_CLOSING, which disables further +// receives/sends on this nic. Doing this will complete immediately +// any receives, receive complete and sends. +// 2. Dequeues and completes all snd requests waiting in this nic +// rip snd req queue. +// 3. Checks if all closing conditions are met. +// 4. If the resources are freed, nic is closed. Else the nic closing +// timer is started which will do a periodic check of closing +// conditions. +// +//*** + +NIC_CLOSE_STATUS +NicClose(PNICCB niccbp, + USHORT CloseCompletionOption) +{ + PLIST_ENTRY lep; + PRIP_SNDREQ sndreqp; + LIST_ENTRY RemovedRipSndReqList; + NIC_CLOSE_STATUS rc; + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + switch(niccbp->NicState) { + + case NIC_CLOSED: + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return NIC_CLOSE_SUCCESS; + + case NIC_CLOSING: + + niccbp->CloseCompletionOptions |= CloseCompletionOption; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return NIC_CLOSE_PENDING; + + case NIC_ACTIVE: + + niccbp->CloseCompletionOptions |= CloseCompletionOption; + + break; + + default: + + ASSERT(FALSE); + break; + } + + // cancel the Wan Gen Requests timer + if(niccbp->DeviceType == NdisMediumWan) { + + // check if the timer has been scheduled to fire + // (this is equivalent with checking if there are wan gen req to send) + if(niccbp->WanGenRequestCount) { + + // the wan requests timer is in the system's timer queue because + // there still are requests to send. Try to cancel it now + if(KeCancelTimer(&niccbp->WanGenRequestTimer)) { + + RtPrint(DBG_NIC, ("IpxRouter: NicClose: Cancel WanReqTimer for NicId %d was successful\n", niccbp->NicId)); + // cancel was successful, reset the request count + niccbp->WanGenRequestCount = 0; + } + else + { + RtPrint(DBG_NIC, ("IpxRouter: NicClose: Could not cancel WanReqTimer for NicId %d\n", niccbp->NicId)); + } + } + } + + InitializeListHead(&RemovedRipSndReqList); + + // remove all pending rip snd requests + while(!IsListEmpty(&niccbp->RipSendQueue)) { + + lep = RemoveHeadList(&niccbp->RipSendQueue); + sndreqp = CONTAINING_RECORD(lep, RIP_SNDREQ, NicLinkage); + + InsertTailList(&RemovedRipSndReqList, &sndreqp->NicLinkage); + } + + // check if all resources for this nic have been de-allocated + if(IsListEmpty(&niccbp->SendQueue) && + IsListEmpty(&niccbp->ReceiveQueue) && + (niccbp->RipSndReqp == NULL) && + (niccbp->WanGenRequestCount == 0)) { + + niccbp->NicState = NIC_CLOSED; + niccbp->CloseCompletionOptions = 0; + + rc = NIC_CLOSE_SUCCESS; + } + else + { + niccbp->NicState = NIC_CLOSING; + KeResetEvent(&niccbp->NicClosedEvent); + StartNicCloseTimer(niccbp); + rc = NIC_CLOSE_PENDING; + } + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // invoke completion routine for all the removed snd requests + while(!IsListEmpty(&RemovedRipSndReqList)) { + + lep = RemoveHeadList(&RemovedRipSndReqList); + sndreqp = CONTAINING_RECORD(lep, RIP_SNDREQ, NicLinkage); + + RipSendAtNicCompleted(sndreqp); + } + +#if DBG + + if(rc == NIC_CLOSE_SUCCESS) { + + RtPrint(DBG_NIC, ("IpxRouter: NicClose: Closed OK for NicId: %d\n", niccbp->NicId)); + } + else + { + RtPrint(DBG_NIC, ("IpxRouter: NicClose: Close pending for NicId: %d\n", niccbp->NicId)); + } + +#endif + + return rc; +} + +VOID +NicCloseTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PNICCB niccbp; + BOOLEAN SignalCompletionEvent = FALSE; + BOOLEAN CallCompletionRoutine = FALSE; + + niccbp = (PNICCB)DefferedContext; + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + switch(niccbp->NicState) { + + case NIC_CLOSING: + + // check closing conditions + if((IsListEmpty(&niccbp->SendQueue)) && + (IsListEmpty(&niccbp->ReceiveQueue)) && + (niccbp->RipSndReqp == NULL) && + (niccbp->WanGenRequestCount == 0)) { + + niccbp->NicState = NIC_CLOSED; + + if(niccbp->CloseCompletionOptions & SIGNAL_CLOSE_COMPLETION_EVENT) { + + SignalCompletionEvent = TRUE; + } + + if(niccbp->CloseCompletionOptions & CALL_CLOSE_COMPLETION_ROUTINE) { + + CallCompletionRoutine = TRUE; + } + + niccbp->CloseCompletionOptions = 0; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + if(CallCompletionRoutine) { + + NicCloseComplete(niccbp); + } + + if(SignalCompletionEvent) { + + // signal the router driver that this nic is closed + KeSetEvent(&niccbp->NicClosedEvent, 0L, FALSE); + } + + RtPrint(DBG_NIC, ("IpxRouter: NicCloseTimeout: Closed OK for NicId: %d\n", niccbp->NicId)); + } + else + { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // restart the timer and check next time + StartNicCloseTimer(niccbp); + + RtPrint(DBG_NIC, ("IpxRouter: NicCloseTimeout: Close pending for NicId: %d\n", niccbp->NicId)); + } + + break; + + case NIC_CLOSED: + + // we can be called with nic closed only to check that nic + // rcv pkts have all been released + if(IsRcvPktResourceFree) { + + // signal the router driver that this nic is closed + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + KeSetEvent(&niccbp->NicClosedEvent, 0L, FALSE); + + RtPrint(DBG_NIC, ("IpxRouter: NicCloseTimeout: Free Resources OK for NicId: %d\n", niccbp->NicId)); + } + else + { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // restart the timer and check next time + StartNicCloseTimer(niccbp); + + RtPrint(DBG_NIC, ("IpxRouter: NicCloseTimeout: Free Resources pending for NicId: %d\n", niccbp->NicId)); + } + + break; + + default: + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + ASSERT(FALSE); + break; + } +} + +NIC_RESOURCES_STATUS +NicFreeResources(PNICCB niccbp) +{ + NIC_RESOURCES_STATUS rc; + + ASSERT(niccbp->NicState == NIC_CLOSED); + + if(IsRcvPktResourceFree(niccbp)) { + + rc = NIC_RESOURCES_FREED; + } + else + { + // we reuse the closing timer for freeing the resources + KeResetEvent(&niccbp->NicClosedEvent); + StartNicCloseTimer(niccbp); + rc = NIC_RESOURCES_PENDING; + } + +#if DBG + + if(rc == NIC_RESOURCES_FREED) { + + RtPrint(DBG_NIC, ("IpxRouter: NicFreeResources: Resources freed OK for NicId: %d\n", niccbp->NicId)); + } + else + { + RtPrint(DBG_NIC, ("IpxRouter: NicFreeResources: Free resources pending for NicId: %d\n", niccbp->NicId)); + } + +#endif + + return rc; +} + +//*** +// +// Function: NicOpen +// +// Descr: Sets the nic state to active so that further receives/snd can +// execute. +// +//*** + +NIC_OPEN_STATUS +NicOpen(PNICCB niccbp) +{ + + RtPrint(DBG_NIC, ("IpxRouter: NicOpen: Entered for NicId: %d\n", niccbp->NicId)); + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + if(RouterUnloading) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return NIC_OPEN_FAILURE; + } + + // check that we are CLOSED + switch(niccbp->NicState) { + + case NIC_CLOSED: + case NIC_PENDING_OPEN: + + // reset the close completion options + niccbp->CloseCompletionOptions = 0; + + break; + + default: + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return NIC_OPEN_FAILURE; + } + + niccbp->NicState = NIC_ACTIVE; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return NIC_OPEN_SUCCESS; +} + + +//*** +// +// Function: StartNicCloseTimer +// +// Descr: Starts the timer for 200 ms at this Nic Cb +// +// Params: pointer to Nic Cb +// +// Returns: none +// +//*** + +VOID +StartNicCloseTimer(PNICCB niccbp) +{ + LARGE_INTEGER timeout; + + timeout.LowPart = (ULONG)(-200 * 10000L); // 200 ms + timeout.HighPart = -1; + + KeSetTimer(&niccbp->NicCloseTimer, timeout, &niccbp->NicCloseDpc); +} + +VOID +ZeroNicStatistics(PNICCB niccbp) +{ + niccbp->StatBadReceived = 0; + niccbp->StatRipReceived = 0; + niccbp->StatRipSent = 0; + niccbp->StatRoutedReceived = 0; + niccbp->StatRoutedSent = 0; + niccbp->StatType20Received = 0; + niccbp->StatType20Sent = 0; +} diff --git a/private/ntos/tdi/isn/rip/nwlnkrip.rc b/private/ntos/tdi/isn/rip/nwlnkrip.rc new file mode 100644 index 000000000..06f8b26da --- /dev/null +++ b/private/ntos/tdi/isn/rip/nwlnkrip.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 RIP Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkrip.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkrip.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/rip/packet.h b/private/ntos/tdi/isn/rip/packet.h new file mode 100644 index 000000000..6d5d3b91b --- /dev/null +++ b/private/ntos/tdi/isn/rip/packet.h @@ -0,0 +1,81 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: packet.h +// +// Description: Contains general definitions for the ipx and rip packets +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _PACKET_ +#define _PACKET_ + +//*** Socket Numbers + +#define IPX_RIP_SOCKET (USHORT)0x453 +#define IPX_NETBIOS_SOCKET (USHORT)0x455 + +//*** Packet Types + +#define IPX_RIP_TYPE 1 // RIP request/reply packet +#define IPX_NETBIOS_TYPE 20 // Netbios propagated packet + +//*** RIP Operations + +#define RIP_REQUEST (USHORT)1 +#define RIP_RESPONSE (USHORT)2 + +//*** Offsets into the IPX header + +#define IPXH_HDRSIZE 30 // Size of the IPX header +#define IPXH_CHECKSUM 0 // Checksum +#define IPXH_LENGTH 2 // Length +#define IPXH_XPORTCTL 4 // Transport Control +#define IPXH_PKTTYPE 5 // Packet Type +#define IPXH_DESTADDR 6 // Dest. Address (Total) +#define IPXH_DESTNET 6 // Dest. Network Address +#define IPXH_DESTNODE 10 // Dest. Node Address +#define IPXH_DESTSOCK 16 // Dest. Socket Number +#define IPXH_SRCADDR 18 // Source Address (Total) +#define IPXH_SRCNET 18 // Source Network Address +#define IPXH_SRCNODE 22 // Source Node Address +#define IPXH_SRCSOCK 28 // Source Socket Number + +#define IPX_NET_LEN 4 +#define IPX_NODE_LEN 6 + +//*** RIP OPERATION FIELD + +#define RIP_OPCODE 30 // rip operation code offset + +//*** Network entry structure in the RIP request/response + +#define RIP_INFO 32 // first network entry in the rip packet + +#define NE_ENTRYSIZE 8 // 4 network + 2 hops + 2 ticks +#define NE_NETNUMBER 0 // network number offset +#define NE_NROFHOPS 4 // number of hops offset +#define NE_NROFTICKS 6 // nymber of ticks offset + +//*** maximum nr of hops for a normal packet *** + +#define IPX_MAX_HOPS 16 + +//*** define rip response packet size + +#define RIP_RESPONSE_PACKET_LEN 432 + +//*** offsets into the netbios session data packet *** + +#define NB_CONNECTION_CONTROL_FLAG 30 +#define NB_DATA_STREAM_TYPE 31 +#define NB_TOTAL_DATA_LENGTH 38 + +#endif diff --git a/private/ntos/tdi/isn/rip/rcvind.c b/private/ntos/tdi/isn/rip/rcvind.c new file mode 100644 index 000000000..392f31e74 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rcvind.c @@ -0,0 +1,448 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: rcvind.c +// +// Description: receive indication handler +// +// Author: Stefan Solomon (stefans) October 8 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +VOID +ReceivePacketComplete(PPACKET_TAG rcvpktp, + UINT BytesTransferred); + +VOID +DbgFilterReceivedPacket(PUCHAR hdrp); + +//*** +// +// Function: RtReceive +// +// Descr: This routine receives control from the IPX driver as an +// indication that a frame has been received on one of our NICs. +// This routine is time critical. +// +// Params: +// +// Returns: +// +//*** + +BOOLEAN +RtReceive(NDIS_HANDLE MacBindingHandle, + NDIS_HANDLE MacReceiveContext, + ULONG FwdAdapterCtx, + PIPX_LOCAL_TARGET RemoteAddress, + ULONG MacOptions, + PUCHAR LookaheadBuffer, + UINT LookaheadBufferSize, + UINT LookaheadBufferOffset, + UINT PacketSize, + PMDL pMdl) +{ + PNICCB niccbp; + PPACKET_TAG rcvpktp; + NDIS_STATUS NdisStatus; + UINT BytesTransferred; + PNDIS_PACKET pktdescrp; + + // + //*** Some Basic Validations *** + // + + RtPrint(DBG_RECV, ("IpxRouter: RtReceive: Entered\n")); + +#if DBG + DbgFilterReceivedPacket(LookaheadBuffer); +#endif + + // check that our configuration process has terminated OK + if(!RouterInitialized) { + + return FALSE; + } + // check that the packet fits our buffers + if(PacketSize > MaxFrameSize) { + + return FALSE; + } + // check if we got the whole IPX header in the lookahead buffer + if(LookaheadBufferSize < IPXH_HDRSIZE) { + + return FALSE; + } + // check if we are active on this NIC + niccbp = NicCbPtrTab[RemoteAddress->NicId]; + + if(niccbp->DeviceType != NdisMediumWan) { + + // ckeck if this is not our own loopedback broadcast packet + if(!memcmp(RemoteAddress->MacAddress, niccbp->Node, 6)) { + + return FALSE; + } + + // This is a LAN NIC, the source node is unique + if(!memcmp(LookaheadBuffer + IPXH_SRCNODE, niccbp->Node, 6)) { + + return FALSE; + } + } + else + { + // This is a WAN NIC, the source node is 1 and may conflict. + // Make an extra check with the network number + if(!memcmp(LookaheadBuffer + IPXH_SRCNET, niccbp->Network, 4) && + !memcmp(LookaheadBuffer + IPXH_SRCNODE, niccbp->Node, 6)) { + + // same net && same node -> loopback -> discard + return FALSE; + } + } + + // check if the packet didn't exceed the allowed number of hops + if(*(LookaheadBuffer + IPXH_XPORTCTL) >= 16) { + + return FALSE; + } + // + //*** Accept the packet *** + // + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + // check that we are enabled to receive on this nic + if(niccbp->NicState != NIC_ACTIVE) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return FALSE; + } + + // try to get a packet from the rcv pkt pool + if((rcvpktp = AllocateRcvPkt(niccbp)) == NULL) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + RtPrint(DBG_RECV, ("IpxRouter: RtReceive: Can't allocate a rcv pkt\n")); + return FALSE; + } + + // set up the new packet + pktdescrp = CONTAINING_RECORD(rcvpktp, NDIS_PACKET, ProtocolReserved); + + // enqueue the packet in the NIC's recv list and wait for the + // transfer to complete + rcvpktp->QueueOwnerNicCbp = niccbp; + + InsertTailList(&niccbp->ReceiveQueue, &rcvpktp->PacketLinkage); + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // try to get the packet data + IpxTransferData(&NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset, // start of IPX header + PacketSize, // packet size starting at IPX header + pktdescrp, + &BytesTransferred); + + if(NdisStatus != NDIS_STATUS_PENDING) { + + // complete the frame processing + RtTransferDataComplete(pktdescrp, NdisStatus, BytesTransferred); + } + return FALSE; +} + + +//*** +// +// Function: RtTransferDataComplete +// +// Descr: +// +//*** + +VOID +RtTransferDataComplete(PNDIS_PACKET packetp, + NDIS_STATUS NdisStatus, + UINT BytesTransferred) +{ + PPACKET_TAG rcvpktp; + PNICCB niccbp; + + rcvpktp = (PPACKET_TAG)(packetp->ProtocolReserved); + niccbp = rcvpktp->QueueOwnerNicCbp; + + // remove the packet from the receive queue + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + RemoveEntryList(&rcvpktp->PacketLinkage); + + // check the success of the transfer and our Nic state + if((NdisStatus != NDIS_STATUS_SUCCESS) || + (niccbp->NicState != NIC_ACTIVE)) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + RtPrint(DBG_NOTIFY, ("IpxRouter: RtTransferDataComplete: failed %x\n", NdisStatus)); + FreeRcvPkt(rcvpktp); + return; + } + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + ReceivePacketComplete(rcvpktp, BytesTransferred); + return; +} + +//*** +// +// Function: ReceivePacketComplete +// +// Descr: actual packet processing +// +//*** + +VOID +ReceivePacketComplete(PPACKET_TAG rcvpktp, + UINT BytesTransferred) +{ + USHORT pktlen; + PUCHAR hdrp; + PNICCB niccbp; + USHORT destsock; + + // get a pointer to the IPX header + hdrp = rcvpktp->DataBufferp; + + // get a pointer to the packet owner NicCb + niccbp = rcvpktp->PacketOwnerNicCbp; + + // check that we have the whole packet + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + if(BytesTransferred < pktlen) { + + // we miss a part of the IPX frame + niccbp->StatBadReceived++; + + // free the packet and get out + RtPrint(DBG_RECV, ("IpxRouter: ReceivePacketComplete: incomplete transfer\n")); + FreeRcvPkt(rcvpktp); + return; + } + + //*** if dest net is 0, replace it with our net + if(!memcmp(hdrp + IPXH_DESTNET, nulladdress, IPX_NET_LEN)) { + + memcpy(hdrp + IPXH_DESTNET, niccbp->Network, IPX_NET_LEN); + } + + //*** if src net is 0, replace it with our net + if(!memcmp(hdrp + IPXH_SRCNET, nulladdress, IPX_NET_LEN)) { + + memcpy(hdrp + IPXH_SRCNET, niccbp->Network, IPX_NET_LEN); + } + + // check if the packet is destined for our own internal processes + if(!memcmp(hdrp + IPXH_DESTNET, niccbp->Network, IPX_NET_LEN)) { + + // + //*** Packet directed to us (Netbios bcast or RIP) *** + // + + // check if this is a Netbios Broadcast packet + if(*(hdrp + IPXH_PKTTYPE) == IPX_NETBIOS_TYPE) { + + niccbp->StatType20Received++; + + // this is a propagated Netbios packet + ProcessNbPacket(rcvpktp); + + return; + } + + // check if this is a RIP packet + GETSHORT2USHORT(&destsock, hdrp + IPXH_DESTSOCK); + if(destsock == IPX_RIP_SOCKET) { + + niccbp->StatRipReceived++; + + // this is a RIP packet. + // Queue it for postprocessing by the receive complete + ACQUIRE_SPIN_LOCK(&RipPktsListLock); + + InsertTailList(&RipPktsList, &rcvpktp->PacketLinkage); + + RELEASE_SPIN_LOCK(&RipPktsListLock); + + return; + } + + // This packet is not for us !!! + niccbp->StatBadReceived++; + + RtPrint(DBG_RECV, ("IpxRouter: ReceivePacketComplete: packet is not for the router!!!\n")); + FreeRcvPkt(rcvpktp); + return; + } + + else + { + // check if this packet is destined to the RIP socket + // this may happen if a badly configured router thinks it is on a + // different net segment + GETSHORT2USHORT(&destsock, hdrp + IPXH_DESTSOCK); + if(destsock == IPX_RIP_SOCKET) { + + niccbp->StatBadReceived++; + + // discard the packet + FreeRcvPkt(rcvpktp); + return; + } + + // + //*** Packet to be routed + // + + niccbp->StatRoutedReceived++; + + RoutePacket(rcvpktp); + } +} + +//*** +// +// Function: RtReceiveComplete +// +// Descr: This routine receives control from the IPX driver after one or +// more receive operations have completed and no receive is in progress. +// It is called under less severe time constraints than RtReceive. +// We use it to perform post processing of RIP requests/replies +// queued in the RIP queue. +// +// Params: +// +// Returns: +// +//*** + +VOID +RtReceiveComplete(USHORT NicId) +{ + LIST_ENTRY TempRipProcessList; + PLIST_ENTRY lep; + PPACKET_TAG pktp; + + RtPrint(DBG_RECV, ("IpxRouter: RtReceiveComplete: Entered\n")); + + // check that our configuration process has terminated OK + if(!RouterInitialized) { + + return; + } + + InitializeListHead(&TempRipProcessList); + + ACQUIRE_SPIN_LOCK(&RipPktsListLock); + + while(!IsListEmpty(&RipPktsList)) { + + lep = RemoveHeadList(&RipPktsList); + InsertTailList(&TempRipProcessList, lep); + } + + RELEASE_SPIN_LOCK(&RipPktsListLock); + + while(!IsListEmpty(&TempRipProcessList)) { + + lep = RemoveHeadList(&TempRipProcessList); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + + ProcessRipPacket(pktp); + } +} + +#if DBG + +ULONG DbgFilterTrap = 0; // 1 - on dst and src (net + node), + // 2 - on dst (net + node), + // 3 - on src (net + node), + // 4 - on dst (net + node + socket) + +UCHAR DbgFilterDstNet[4]; +UCHAR DbgFilterDstNode[6]; +UCHAR DbgFilterDstSocket[2]; +UCHAR DbgFilterSrcNet[4]; +UCHAR DbgFilterSrcNode[6]; +UCHAR DbgFilterSrcSocket[2]; +PUCHAR DbgFilterFrame; + +VOID +DbgFilterReceivedPacket(PUCHAR hdrp) +{ + switch(DbgFilterTrap) { + + case 1: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6) && + !memcmp(hdrp + IPXH_SRCNET, DbgFilterSrcNet, 4) && + !memcmp(hdrp + IPXH_SRCNODE, DbgFilterSrcNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 2: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 3: + + if(!memcmp(hdrp + IPXH_SRCNET, DbgFilterSrcNet, 4) && + !memcmp(hdrp + IPXH_SRCNODE, DbgFilterSrcNode, 6)) { + + DbgBreakPoint(); + } + + break; + + case 4: + + if(!memcmp(hdrp + IPXH_DESTNET, DbgFilterDstNet, 4) && + !memcmp(hdrp + IPXH_DESTNODE, DbgFilterDstNode, 6) && + !memcmp(hdrp + IPXH_DESTSOCK, DbgFilterDstSocket, 2)) { + + DbgBreakPoint(); + } + + break; + + default: + + break; + } + + DbgFilterFrame = hdrp; +} + +#endif diff --git a/private/ntos/tdi/isn/rip/rcvpkt.c b/private/ntos/tdi/isn/rip/rcvpkt.c new file mode 100644 index 000000000..250e97dbc --- /dev/null +++ b/private/ntos/tdi/isn/rip/rcvpkt.c @@ -0,0 +1,833 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: rcvpkt.c +// +// Description: rcv pkt pool manager +// +// Author: Stefan Solomon (stefans) October 5, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +PRCVPKT_SEGMENT +CreateRcvPktSegment(VOID); + +VOID +DestroyRcvPktSegment(PRCVPKT_SEGMENT segp); + +PPACKET_TAG +CreateRcvPkt(PULONG buffp, + PRCVPKT_SEGMENT segp); + +VOID +DestroyRcvPkt(PPACKET_TAG pktp); + +// the pool size parameter +UINT RcvPktPoolSize = RCVPKT_MEDIUM_POOL_SIZE; + +// the number of receive packets per pool segment (config parameter) +UINT RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT; +UINT LowRcvPktsCount = 0; + + +// Max frame size as a multiple of ULONGs +UINT UlongMaxFrameSize; + +// +//*** Control Structures For the Rcv Pkt Segment List *** +// + +NDIS_SPIN_LOCK RcvPktSegListLock; + +UINT RcvPktSegCount = 0; // total segments allocated + // for this pool +UINT MaxRcvPktCount = 0; // max nr of pkts the pool can have +UINT RcvPktCount = 0; // total pkts allocated for the + // pool: free + owned by nics +PUINT RcvPktPerNicCount; // table of pkts allocated for + // each nic, indexed by NicId +LIST_ENTRY RcvPktSegList; // list of pool segments + +//*** Statistics: Peak rcv pkts allocation *** + +ULONG StatMemAllocCount = 0; +ULONG StatMemPeakCount = 0; + +//*** Control Structure for the Allocate Ahead Function *** + +typedef enum _ALLOC_AHEAD_STATE { + + ALLOC_AHEAD_IDLE, + ALLOC_AHEAD_ACTIVE + } ALLOC_AHEAD_STATE; + +ALLOC_AHEAD_STATE AllocAheadState; + +WORK_QUEUE_ITEM AllocAheadWorkItem; + +VOID +AllocAhead(PVOID parameter); + +VOID +CheckAllocationAhead(VOID); + +//*** +// +// Function: CreateRcvPktPool +// +// Descr: Allocates the rcv pkt descr and buff descr pools. +// Creates the rcv pkt segment list and allocates one segment +// +// Params: none +// +// Returns: 0 - success, 1 - failure +// +//*** + +UINT +CreateRcvPktPool(VOID) +{ + UINT MaxRcvPktsPerNic; + PRCVPKT_SEGMENT segp; + + RtPrint(DBG_INIT, ("IpxRouter: CreateRcvPktPool: Entered\n")); + + // calculate the maxrcvpktspernic function of the pool size + switch(RcvPktPoolSize) { + + case RCVPKT_SMALL_POOL_SIZE: + + MaxRcvPktsPerNic = 100; + break; + + case RCVPKT_MEDIUM_POOL_SIZE: + + MaxRcvPktsPerNic = 250; + break; + + case RCVPKT_LARGE_POOL_SIZE: + default: + + MaxRcvPktsPerNic = 0; // unlimited + break; + } + + INITIALIZE_SPIN_LOCK(&RcvPktSegListLock); + + InitializeListHead(&RcvPktSegList); + + // initialize the pool max limit. + if(MaxRcvPktsPerNic) { + + MaxRcvPktCount = MaximumNicCount * MaxRcvPktsPerNic; + } + else + { + MaxRcvPktCount = 0xFFFFFFFF; + } + + UlongMaxFrameSize = MaxFrameSize / sizeof(ULONG) + 1; + + // allocate the array of rcv pkts allocated /nic + if((RcvPktPerNicCount = (PUINT)CTEAllocMem( + MaximumNicCount * sizeof(UINT))) == NULL) { + + goto cleanup; + } + RtlZeroMemory(RcvPktPerNicCount, MaximumNicCount * sizeof(UINT)); + + // + //*** Create the first segment and insert it in the pool + // + + if((segp = CreateRcvPktSegment()) == NULL) { + + goto cleanup; + } + + // chain the segment in the rcv pkt seg list + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + RcvPktSegCount++; + RcvPktCount += segp->MaxPktCount; + + InsertTailList(&RcvPktSegList, &segp->SegmentLinkage); + + // initialize the allocate ahead structures + AllocAheadState = ALLOC_AHEAD_IDLE; + LowRcvPktsCount = RcvPktsPerSegment / 2; + ExInitializeWorkItem(&AllocAheadWorkItem, AllocAhead, NULL); + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + // All Done + + return 0; + +cleanup: + + DestroyRcvPktPool(); + + return 1; +} + +//*** +// +// Function: DestroyRcvPktPool +// +// Descr: Destroys all the pool segments. +// Frees the rcv pkt segment memory array. +// +// Params: none +// +// Returns: none +// +//*** + +VOID +DestroyRcvPktPool(VOID) +{ + PLIST_ENTRY slp; + PRCVPKT_SEGMENT sp; + + RtPrint(DBG_INIT, ("IpxRouter: DestroyRcvPktPool: Entered\n")); + + // ckeck if segment list has been created and destroy it if it exists + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + while(!IsListEmpty(&RcvPktSegList)) { + + slp = RemoveTailList(&RcvPktSegList); + sp = CONTAINING_RECORD(slp, RCVPKT_SEGMENT, SegmentLinkage); + + DestroyRcvPktSegment(sp); + } + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + if(RcvPktPerNicCount) { + + CTEFreeMem(RcvPktPerNicCount); + } + + DEINITIALIZE_SPIN_LOCK(&RcvPktSegListLock); +} + +//*** +// +// Function: CreateRcvPktSegment +// +// Descr: Allocates a memory buffer of size: +// rcv pkt segment + n * maxframesize. +// Allocates n rcv pkt descr and 2n buff descr and creates the +// rcv packets. +// Chains all rcv pkts in the rcv pkt segment. +// Note: +// Only one buff descr is chained in each packet descr. +// A ptr is kept in the packet tag to the second buff descr which +// is associated with the MAC header in the packet tag. +// +// Params: none +// +// Returns: Segment ptr or NULL if failure. +// +//*** + +PRCVPKT_SEGMENT +CreateRcvPktSegment(VOID) +{ + PRCVPKT_SEGMENT segp; + ULONG seglen; + UINT i; + PPACKET_TAG pktp; + PULONG buffp; + NDIS_STATUS NdisStatus; + UINT PktReservedLen; + + RtPrint(DBG_RCVPKT, ("IpxRouter: CreateRcvPktSegment: Entered\n")); + + seglen = sizeof(RCVPKT_SEGMENT) + + RcvPktsPerSegment * UlongMaxFrameSize * sizeof(ULONG); + + if((segp = CTEAllocMem(seglen)) == NULL) { + + return NULL; + } + + RtlZeroMemory(segp, sizeof(RCVPKT_SEGMENT)); + InitializeListHead(&segp->PacketList); + + // Allocate receive packet descriptors and buffer descriptors pools + // for this segment + + PktReservedLen = sizeof(PACKET_TAG); + segp->RcvPktDescrPoolSize = RcvPktsPerSegment; + + NdisAllocatePacketPool( + &NdisStatus, + &segp->RcvPktDescrPoolHandle, + segp->RcvPktDescrPoolSize, + PktReservedLen); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + CTEFreeMem(segp); + return NULL; + } + + // each packet has 2 buffer descriptors + segp->RcvPktBuffDescrPoolSize = 2 * RcvPktsPerSegment; + + NdisAllocateBufferPool ( + &NdisStatus, + &segp->RcvPktBuffDescrPoolHandle, + segp->RcvPktBuffDescrPoolSize); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacketPool(segp->RcvPktDescrPoolHandle); + CTEFreeMem(segp); + return NULL; + } + + // Make the list of packets + for(i=0, buffp=segp->DataBuffer; iPacketList, &pktp->PacketLinkage); + segp->AvailablePktCount++; + + buffp += UlongMaxFrameSize; + } + else + { + DbgBreakPoint(); + DestroyRcvPktSegment(segp); + return NULL; + } + } + + // set up the total packet allocation count for this segment + segp->MaxPktCount = segp->AvailablePktCount; + + RtPrint(DBG_RCVPKT, ("IpxRouter: CreateRcvPktSegment: success\n")); + + return segp; +} + + +//*** +// +// Function: DestroyRcvPktSegment +// +// Descr: Dequeues the pkt descr and +// buff descriptors to their respective pools. +// Frees the memory buffer. +// +// Params: Segment ptr +// +// Returns: none +// +//*** + +VOID +DestroyRcvPktSegment(PRCVPKT_SEGMENT segp) +{ + PLIST_ENTRY lep; + PPACKET_TAG pktp; + + RtPrint(DBG_RCVPKT, ("IpxRouter: DestroyRcvPktSegment: Entered\n")); + + while(!IsListEmpty(&segp->PacketList)) { + + lep = RemoveHeadList(&segp->PacketList); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + DestroyRcvPkt(pktp); + } + + // deallocate the buff descr pool and packet descr pool + NdisFreeBufferPool(segp->RcvPktBuffDescrPoolHandle); + NdisFreePacketPool(segp->RcvPktDescrPoolHandle); + + CTEFreeMem(segp); +} + +//*** +// +// Function: CreateRcvPkt +// +// Descr: allocates a pkt descr and 2 buff descr +// makes the necessary chains +// +// Params: data buffer ptr +// +// Returns: prt to packet or null is failure +// +//*** + +PPACKET_TAG +CreateRcvPkt(PULONG buffp, + PRCVPKT_SEGMENT segp) +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisDataBuffer; + PNDIS_BUFFER NdisMacBuffer; + UINT bufflen; + PPACKET_TAG pktp; + + RtPrint(DBG_RCVPKT, ("IpxRouter: CreateRcvPkt: Entered\n")); + + bufflen = UlongMaxFrameSize * sizeof(ULONG); + + NdisAllocatePacket(&NdisStatus, + &NdisPacket, + segp->RcvPktDescrPoolHandle); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + return NULL; + } + + pktp = (PPACKET_TAG)&NdisPacket->ProtocolReserved; + RtlZeroMemory(pktp, sizeof(PACKET_TAG)); + + NdisAllocateBuffer(&NdisStatus, + &NdisDataBuffer, + segp->RcvPktBuffDescrPoolHandle, + buffp, + bufflen); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacket(NdisPacket); + + return NULL; + } + + NdisAllocateBuffer(&NdisStatus, + &NdisMacBuffer, + segp->RcvPktBuffDescrPoolHandle, + pktp->MacHeader, + MacHeaderNeeded); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacket(NdisPacket); + NdisFreeBuffer(NdisDataBuffer); + + return NULL; + } + + NdisChainBufferAtFront(NdisPacket, NdisDataBuffer); + pktp->Identifier = IDENTIFIER_RIP; + pktp->ReservedPvoid[0] = NULL; + pktp->ReservedPvoid[1] = NULL; + pktp->PacketType = RCV_PACKET; + pktp->RcvPktSegmentp = segp; + pktp->DataBufferp = (PUCHAR)buffp; + pktp->DataBufferLength = bufflen; + pktp->HeaderBuffDescrp = NdisMacBuffer; + + return pktp; +} + +//*** +// +// Function: DestroyRcvPkt +// +// Descr: deallocates a pkt descr and 2 buff descr +// unmakes the necessary chains +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +DestroyRcvPkt(PPACKET_TAG pktp) +{ + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + + RtPrint(DBG_RCVPKT, ("IpxRouter: DestroyRcvPkt: Entered\n")); + + NdisPacket = CONTAINING_RECORD(pktp, NDIS_PACKET, ProtocolReserved); + + // free the data buffer descr + NdisUnchainBufferAtBack (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + else + { + // !!! break + DbgBreakPoint(); + } + + // free the mac hdr buff descr + NdisFreeBuffer(pktp->HeaderBuffDescrp); + + NdisFreePacket(NdisPacket); +} + +//*** +// +// Function: AllocateRcvPkt +// +// Descr: Tries to do the allocation starting with the FIRST available +// segment. +// If no available segments, tries to allocate one. +// Decrements available pkts counter and resets scavenger's tick +// count. Sets the packet tag rcv pool array ptr to this segment. +// HOLDS THE LOCK until done. +// +// Params: NicCbp to charge +// +// Returns: Packet or NULL if the rcv pkt pool array is empty. +// +//*** + +PPACKET_TAG +AllocateRcvPkt(PNICCB niccbp) +{ + PLIST_ENTRY nextp; + PRCVPKT_SEGMENT segp; + PPACKET_TAG pktp; + USHORT NicId; + PLIST_ENTRY lep; + UINT FreeRcvPktCount = 0; // how many pkts are available in the + // pool + + NicId = niccbp->NicId; + + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + // walk the segments list until we find a segment with available pkts + nextp = RcvPktSegList.Flink; + + while(nextp != &RcvPktSegList) { + + segp = CONTAINING_RECORD(nextp, RCVPKT_SEGMENT, SegmentLinkage); + if(segp->AvailablePktCount) { + + goto allocation_ok; + } + nextp = segp->SegmentLinkage.Flink; + } + + // pool is empty, check if we can create a new segment + if(RcvPktCount >= MaxRcvPktCount) { + + // we can't allocate anything + goto allocation_failure; + } + + // we can allocate a new segment + if((segp = CreateRcvPktSegment()) == NULL) { + + // we are beyond salvation !!! break + goto allocation_failure; + } + + // increment the segment count + RcvPktSegCount++; + + // add the new segment to the pool + InsertTailList(&RcvPktSegList, &segp->SegmentLinkage); + + // and increment the global allocation counter + RcvPktCount += segp->AvailablePktCount; + +allocation_ok: + + RtPrint(DBG_RCVPKT, ("IpxRouter: AllocateRcvPkt: OK\n")); + + lep = RemoveHeadList(&segp->PacketList); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + + ASSERT(pktp != NULL); + + segp->AvailablePktCount--; + + // reset the segment aging timer + segp->AgingTimer = 0; + + // charge the nic for this allocation + RcvPktPerNicCount[NicId]++; + + // set the nic owner in the packet + pktp->PacketOwnerNicCbp = niccbp; + + // set the packet type + pktp->PacketType = RCV_PACKET; + + // before we return the packet, we check if we have hit the low packet + // count and, if true, queue a work item to allocate a new segment + CheckAllocationAhead(); + + // return the packet + + // update statistics + StatMemAllocCount++; + + if(StatMemPeakCount < StatMemAllocCount) { + + StatMemPeakCount = StatMemAllocCount; + } + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return pktp; + +allocation_failure: + + RtPrint(DBG_RCVPKT, ("IpxRouter: AllocateRcvPkt: Failure\n")); + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return NULL; +} + +//*** +// +// Function: FreeRcvPkt +// +// Descr: Inserts the rcv pkt in the respective list and increments the +// available pkts counter for this pool segment. +// HOLDS the lock until done. +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +FreeRcvPkt(PPACKET_TAG pktp) +{ + PRCVPKT_SEGMENT segp; + +#if DBG + PRCVPKT_SEGMENT walksegp; + PLIST_ENTRY nextp; +#endif // DBG + + USHORT NicId; + + RtPrint(DBG_RCVPKT, ("IpxRouter: FreeRcvPkt: Entered\n")); + + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + // update statistics + StatMemAllocCount--; + + // discharge the Nic + NicId = pktp->PacketOwnerNicCbp->NicId; + RcvPktPerNicCount[NicId]--; + + // get the packet segment + segp = pktp->RcvPktSegmentp; + +#if DBG + + { + // check that this segment is indeed in our list by walking the list + BOOLEAN ValidSegment = FALSE; + + nextp = RcvPktSegList.Flink; + + while(nextp != &RcvPktSegList) { + + walksegp = CONTAINING_RECORD(nextp, RCVPKT_SEGMENT, SegmentLinkage); + if(nextp == &segp->SegmentLinkage) { + + ValidSegment = TRUE; + break; + } + nextp = walksegp->SegmentLinkage.Flink; + } + + ASSERT(ValidSegment == TRUE); + } +#endif + + // put packet back into the segment list + InsertTailList(&segp->PacketList, &pktp->PacketLinkage); + + // increment the available pkts count for this segment + segp->AvailablePktCount++; + + ASSERT(segp->AvailablePktCount <= segp->MaxPktCount); + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return; +} + +//*** +// +// Function: RcvPktPoolScavenger +// +// Descr: Entered every 2 sec as a timer DPC. +// Increments the scavenger tick count for the segment at the +// tail. If the tick count reaches 3 (6 secs not used) destroys +// the segment. +// The scavenger does not act on the FIRST segment. +// HOLDS THE LOCK until done +// +// +// Params: none +// +// Returns: none +// +//*** + +VOID +RcvPktPoolScavenger(VOID) +{ + PRCVPKT_SEGMENT segp; + PLIST_ENTRY lastep; + +// RtPrint(DBG_RCVPKT, ("IpxRouter: Pool scavenger: pool has %d segmentst\n", RcvPktSegCount)); + + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + ASSERT(RcvPktSegCount); + + if(RcvPktSegCount == 1) { + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return; + } + + // get to the last segment in the list + lastep = RcvPktSegList.Blink; + segp = CONTAINING_RECORD(lastep, RCVPKT_SEGMENT, SegmentLinkage); + + // check if all packets are returned to the segment + if(segp->AvailablePktCount == segp->MaxPktCount) { + + // we can age this segment + if(segp->AgingTimer++ >= 3) { + + // this segment too old and may be deleted + RtPrint(DBG_RCVPKT, ("IpxRouter: Pool scavenger: pool has %d segments, will destroy the last\n", + RcvPktSegCount)); + + RemoveEntryList(&segp->SegmentLinkage); + RcvPktSegCount--; + RcvPktCount -= segp->MaxPktCount; + + DestroyRcvPktSegment(segp); + } + } + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return; +} + +//*** +// +// Function: AllocAhead +// +// Descr: +// +//*** + +VOID +AllocAhead(PVOID Parameter) +{ + PRCVPKT_SEGMENT segp; + + RtPrint(DBG_RCVPKT, ("IpxRouter: AllocAhead: Entered\n")); + + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + // check if we can create a new segment + if(RcvPktCount >= MaxRcvPktCount) { + + // we can't allocate anything + goto aa_exit; + } + + // we can allocate a new segment + if((segp = CreateRcvPktSegment()) == NULL) { + + // we are beyond salvation !!! break + goto aa_exit; + } + + // increment the segment count + RcvPktSegCount++; + + // add the new segment to the pool + InsertTailList(&RcvPktSegList, &segp->SegmentLinkage); + + // and increment the global allocation counter + RcvPktCount += segp->AvailablePktCount; + +aa_exit: + + AllocAheadState = ALLOC_AHEAD_IDLE; + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); +} + +VOID +CheckAllocationAhead(VOID) +{ + UINT FreeRcvPktsCount = 0; + PLIST_ENTRY nextp; + PRCVPKT_SEGMENT segp; + + // get the total number of free packets in the pool and check if we + // are at our low count + nextp = RcvPktSegList.Flink; + + while(nextp != &RcvPktSegList) { + + segp = CONTAINING_RECORD(nextp, RCVPKT_SEGMENT, SegmentLinkage); + FreeRcvPktsCount += segp->AvailablePktCount; + nextp = segp->SegmentLinkage.Flink; + } + + if((FreeRcvPktsCount <= LowRcvPktsCount) && + (AllocAheadState == ALLOC_AHEAD_IDLE)) { + + ExQueueWorkItem(&AllocAheadWorkItem, CriticalWorkQueue); + AllocAheadState = ALLOC_AHEAD_ACTIVE; + } +} + +BOOLEAN +IsRcvPktResourceFree(PNICCB niccbp) +{ + USHORT NicId; + BOOLEAN res_free = FALSE; + + NicId = niccbp->NicId; + + ACQUIRE_SPIN_LOCK(&RcvPktSegListLock); + + if(!RcvPktPerNicCount[NicId]) { + + res_free = TRUE; + } + + RELEASE_SPIN_LOCK(&RcvPktSegListLock); + + return res_free; +} diff --git a/private/ntos/tdi/isn/rip/registry.c b/private/ntos/tdi/isn/rip/registry.c new file mode 100644 index 000000000..b7b85b5d0 --- /dev/null +++ b/private/ntos/tdi/isn/rip/registry.c @@ -0,0 +1,258 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: registry.c +// +// Description: routines for reading the registry configuration +// +// Author: Stefan Solomon (stefans) November 9, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +NTSTATUS +SetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +extern UNICODE_STRING UnicodeFileName; +extern PWSTR FileNamep; + +//*** +// +// Function: ReadIpxDeviceName +// +// Descr: Reads the device name exported by ipx so we can bind to it +// +//*** + +NTSTATUS +ReadIpxDeviceName(VOID) +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = L"NwLnkIpx\\Linkage"; + + // + // Set up QueryTable to do the following: + // + + // + // 1) Call SetIpxDeviceName for the string in "Export" + // + + QueryTable[0].QueryRoutine = SetIpxDeviceName; + QueryTable[0].Flags = 0; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + +NTSTATUS +SetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - NULL. + + EntryContext - NULL. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + FileNamep = (PWSTR)ExAllocatePool(NonPagedPool, ValueLength); + if (FileNamep != NULL) { + + RtlCopyMemory(FileNamep, ValueData, ValueLength); + RtlInitUnicodeString (&UnicodeFileName, FileNamep); + } + + return STATUS_SUCCESS; +} + +//*** +// +// Function: GetRouterParameters +// +// Descr: Reads the parameters from the registry and sets them +// +//*** + +NTSTATUS +GetRouterParameters(IN PUNICODE_STRING RegistryPath) +{ + + NTSTATUS Status; + PWSTR RegistryPathBuffer; + PWSTR Parameters = L"Parameters"; + RTL_QUERY_REGISTRY_TABLE paramTable[7]; // table size = nr of params + 1 + + RegistryPathBuffer = (PWSTR)ExAllocatePool(NonPagedPool, RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + RtlZeroMemory(¶mTable[0], sizeof(paramTable)); + + paramTable[0].QueryRoutine = NULL; + paramTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + paramTable[0].Name = Parameters; + + paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[1].Name = L"RcvPktPoolSize"; + paramTable[1].EntryContext = &RcvPktPoolSize; + paramTable[1].DefaultType = REG_DWORD; + paramTable[1].DefaultData = &RcvPktPoolSize; + paramTable[1].DefaultLength = sizeof(ULONG); + + paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[2].Name = L"RcvPktsPerSegment"; + paramTable[2].EntryContext = &RcvPktsPerSegment; + paramTable[2].DefaultType = REG_DWORD; + paramTable[2].DefaultData = &RcvPktsPerSegment; + paramTable[2].DefaultLength = sizeof(ULONG); + + paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[3].Name = L"NetbiosRouting"; + paramTable[3].EntryContext = &NetbiosRouting; + paramTable[3].DefaultType = REG_DWORD; + paramTable[3].DefaultData = &NetbiosRouting; + paramTable[3].DefaultLength = sizeof(ULONG); + + paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[4].Name = L"MaxSendPktsQueued"; + paramTable[4].EntryContext = &MaxSendPktsQueued; + paramTable[4].DefaultType = REG_DWORD; + paramTable[4].DefaultData = &MaxSendPktsQueued; + paramTable[4].DefaultLength = sizeof(ULONG); + + paramTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[5].Name = L"EnableLanRouting"; + paramTable[5].EntryContext = &EnableLanRouting; + paramTable[5].DefaultType = REG_DWORD; + paramTable[5].DefaultData = &EnableLanRouting; + paramTable[5].DefaultLength = sizeof(ULONG); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPathBuffer, + paramTable, + NULL, + NULL); + + if(!NT_SUCCESS(Status)) { + + RtPrint (DBG_INIT, ("IpxRouter: Missing Parameters key in the registry\n")); + } + + ExFreePool(RegistryPathBuffer); + + // check if the parameters received are within limits: + if((RcvPktPoolSize > RCVPKT_LARGE_POOL_SIZE) || + (RcvPktPoolSize < RCVPKT_SMALL_POOL_SIZE)) { + + RcvPktPoolSize = RCVPKT_MEDIUM_POOL_SIZE; + } + + if((RcvPktsPerSegment > MAX_RCV_PKTS_PER_SEGMENT) || + (RcvPktsPerSegment < MIN_RCV_PKTS_PER_SEGMENT)) { + + RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT; + } + + // even if the RtlQueryRegistryValues has failed, we return success and will + // use the defaults. + return STATUS_SUCCESS; +} + +ULONG +IsWanGlobalNetRequested(VOID) +{ + + NTSTATUS Status; + PWSTR IpxCpParametersPath = L"RemoteAccess\\Parameters\\Ipx"; + RTL_QUERY_REGISTRY_TABLE paramTable[2]; // table size = nr of params + 1 + + ULONG WanGlobalNetRequested = 0; + + RtlZeroMemory(¶mTable[0], sizeof(paramTable)); + + paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[0].Name = L"GlobalWanNet"; + paramTable[0].EntryContext = &WanGlobalNetRequested; + paramTable[0].DefaultType = REG_DWORD; + paramTable[0].DefaultData = &WanGlobalNetRequested; + paramTable[0].DefaultLength = sizeof(ULONG); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxCpParametersPath, + paramTable, + NULL, + NULL); + + RtPrint(DBG_INIT, ("IpxRouter: GlobalWanNet request = %d\n", WanGlobalNetRequested)); + + return WanGlobalNetRequested; +} diff --git a/private/ntos/tdi/isn/rip/ripaux.c b/private/ntos/tdi/isn/rip/ripaux.c new file mode 100644 index 000000000..f61bbb573 --- /dev/null +++ b/private/ntos/tdi/isn/rip/ripaux.c @@ -0,0 +1,455 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: ripaux.c +// +// Description: Misc aux routines for doing RIP +// +// Author: Stefan Solomon (stefans) November 5, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +typedef struct _NIC_NODE { + + LIST_ENTRY NodeLinkage; + LIST_ENTRY BcastPktsList; + USHORT NicId; + } NIC_NODE, *PNIC_NODE; + +//*** +// +// Function: AddRouteToBcastSndReq +// +// Descr: Builds a list of nodes where each node represents a +// Nic. Each node has an attached list of broadcast packets +// which contain the list of routes to be advertised. +// At each invokation, the coresponding nic node is located +// (or created) and the route information is set in the +// broadcast packet attached to the node. +// +//*** + +UINT +AddRouteToBcastSndReq(PLIST_ENTRY nodelistp, + PIPX_ROUTE_ENTRY rtep) +{ + PLIST_ENTRY nextp; + PNIC_NODE nodep; + BOOLEAN found; + PRIP_UPDATE_SNDREQ respcbp = NULL; // ptr to changes response to bcast + PUCHAR hdrp; // Ipx pkt header + USHORT pktlen; + PLIST_ENTRY lep; + + // traverse the nodes list looking for our nic id. + nextp = nodelistp->Flink; + found = FALSE; + + while(nextp != nodelistp) { + + nodep = CONTAINING_RECORD(nextp, NIC_NODE, NodeLinkage); + if(nodep->NicId == rtep->NicId) { + + found = TRUE; + break; + } + + nextp = nextp->Flink; + } + + if(!found) { + + // create the node we need + if((nodep = ExAllocatePool(NonPagedPool, sizeof(NIC_NODE))) == NULL) { + + // can't create the node + return 1; + } + + InitializeListHead(&nodep->BcastPktsList); + nodep->NicId = rtep->NicId; + + // create a send bcast request structure and add it to the node + if((respcbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_UPDATE_SNDREQ) + RIP_SNDPKT_MAXLEN)) == NULL) { + + // free the node + ExFreePool(nodep); + return 1; + } + + InsertTailList(&nodep->BcastPktsList, &respcbp->RipSndReq.NicLinkage); + + // get the Ipx packet length and Ipx packet header + hdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket; + pktlen = RIP_INFO; + + // now add the node to the nodes list + InsertTailList(nodelistp, &nodep->NodeLinkage); + + } + else + { + // we found the node + // now go to the last packet in the node and check if there is room for + // a network entry + ASSERT(!IsListEmpty(&nodep->BcastPktsList)); + lep = nodep->BcastPktsList.Blink; + respcbp = CONTAINING_RECORD(lep, RIP_UPDATE_SNDREQ, RipSndReq.NicLinkage); + + // get IPX packet length + hdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket; + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + if(pktlen >= RIP_RESPONSE_PACKET_LEN) { + + // this packet is full + // create a new send bcast request structure and add it to the node + if((respcbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_UPDATE_SNDREQ) + RIP_SNDPKT_MAXLEN)) == NULL) { + + return 1; + } + + InsertTailList(&nodep->BcastPktsList, &respcbp->RipSndReq.NicLinkage); + + // get the Ipx packet length and Ipx packet header + hdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket; + pktlen = RIP_INFO; + } + } + + // add the new route entry to the bcast pkt + SetNetworkEntry(hdrp + pktlen, rtep); + + // increment the packet length and put it in the packet + pktlen += NE_ENTRYSIZE; + PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, pktlen); + + RtPrint(DBG_RIPAUX, ("IpxRouter: AddRouteToBcastSndReq: net entry added for NicId %d\n", nodep->NicId)); + + return 0; +} + +//*** +// +// Function: GetBcastSndReq +// +// Descr: For each call it tries to remove one broadcast packet +// from the list. If the nic node list of packets is empty +// after the removeal, the nic node is freed. +// +//*** + +PRIP_SNDREQ +GetBcastSndReq(PLIST_ENTRY nodelistp, + PUSHORT NicIdp) +{ + PNIC_NODE nodep; + PRIP_SNDREQ sndreqp; + PLIST_ENTRY lep; + + if(IsListEmpty(nodelistp)) { + + return NULL; + } + + lep = nodelistp->Flink; + nodep = CONTAINING_RECORD(lep, NIC_NODE, NodeLinkage); + + ASSERT(!IsListEmpty(&nodep->BcastPktsList)); + + lep = RemoveHeadList(&nodep->BcastPktsList); + sndreqp = CONTAINING_RECORD(lep, RIP_SNDREQ, NicLinkage); + *NicIdp = nodep->NicId; + + if(IsListEmpty(&nodep->BcastPktsList)) { + + RemoveEntryList(&nodep->NodeLinkage); + ExFreePool(nodep); + } + + RtPrint(DBG_RIPAUX, ("IpxRouter: GetBcastSndReq: got snd req pkt for NicId %d\n", *NicIdp)); + + return sndreqp; +} + +//*** +// +// Function: BroadcastRipUpdate +// +// Descr: Set up the snd req for this bcast and dispatch it. +// If wait on event is requested, wait until send completes. +// +//*** + +VOID +BroadcastRipUpdate(PRIP_SNDREQ sndreqp, // send request + PNICCB niccbp, // do not send on this nic + PKEVENT eventp) // wait if this event is not NULL +{ + sndreqp->SndReqId = RIP_UPDATE; + sndreqp->SendOnAllNics = TRUE; + memcpy(sndreqp->DestNode, bcastaddress, IPX_NODE_LEN); + sndreqp->DestSock = IPX_RIP_SOCKET; + sndreqp->DoNotSendNicCbp = niccbp; // do not send update on this nic + sndreqp->SenderNicCbp = NULL; + + sndreqp->SndCompleteEventp = eventp; + + if(eventp != NULL) { + + // WAIT on event after the bcast req is dispatched. + KeResetEvent(eventp); + + // dispatch the bcast request + RipDispatchSndReq(sndreqp); + + // wait for this request to complete. + KeWaitForSingleObject( + eventp, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL + ); + + KeResetEvent(eventp); + + } + else + { + // NO WAIT -> dispatch the bcast request and return + RipDispatchSndReq(sndreqp); + } +} + +//*** +// +// Function: BroadcastRipGeneralResponse +// +// Descr: Set up the snd req for this bcast and dispatch it. +// +//*** + +VOID +BroadcastRipGeneralResponse(PRIP_SNDREQ sndreqp) +{ + sndreqp->SndReqId = RIP_GEN_RESPONSE; + sndreqp->SendOnAllNics = TRUE; // send on all + memcpy(sndreqp->DestNode, bcastaddress, IPX_NODE_LEN); + sndreqp->DestSock = IPX_RIP_SOCKET; + sndreqp->DoNotSendNicCbp = NULL; // send without exception + sndreqp->SenderNicCbp = NULL; + sndreqp->SndCompleteEventp = NULL; + + RipDispatchSndReq(sndreqp); +} + + +//*** +// +// Function: BroadcastWanNetUpdate +// +// Descr: Broadcasts RIP update for one WAN network entry +// +//*** + +VOID +BroadcastWanNetUpdate(PIPX_ROUTE_ENTRY rtep, // route entry to bcast + PNICCB niccbp, // do not send on this nic + PKEVENT eventp) // synch event +{ + PRIP_UPDATE_SNDREQ respcbp = NULL; // ptr to changes response to bcast + PUCHAR sndhdrp; + USHORT sndpktlen; + PRIP_SNDREQ sndreqp; + + // allocate a send request struct to bcast changes in the routing table + if((respcbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_UPDATE_SNDREQ) + RIP_SNDPKT_MINLEN)) == NULL) { + //!!! + return; + } + + // get the ipx hdr ptr for the send packet + sndhdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket; + + // set the initial length + sndpktlen = RIP_INFO; + + // fill in the network entry structure in the packet with the + // info from the route entry + SetNetworkEntry(sndhdrp + sndpktlen, rtep); + + // increment the send packet length to the next network entry + sndpktlen += NE_ENTRYSIZE; + + // set the new packet length + PUTUSHORT2SHORT(sndhdrp + IPXH_LENGTH, sndpktlen); + + // set up the request to send a bcast response with the changes + sndreqp = &respcbp->RipSndReq; + + BroadcastRipUpdate(sndreqp, niccbp, eventp); +} + + + + + + + + + + + + + + +//*** Routines for handling the hash table of node numbers -> nic ptrs mapping. + +// The WAN nodes hash table + +NDIS_SPIN_LOCK WanNodeHTLock; + +LIST_ENTRY WanNodeHT[NODE_HTSIZE]; + +//*** +// +// Function: InitWanNodeHT +// +// Descr: +// +//*** + +VOID +InitWanNodeHT(VOID) +{ + int i; + PLIST_ENTRY WanNodeHTBucketp; + + INITIALIZE_SPIN_LOCK(&WanNodeHTLock); + + WanNodeHTBucketp = WanNodeHT; + + for(i=0; iRemoteNode, nodep, 6)) { + + RELEASE_SPIN_LOCK(&WanNodeHTLock); + return niccbp; + } + + nextp = niccbp->WanHtLinkage.Flink; + } + + RELEASE_SPIN_LOCK(&WanNodeHTLock); + + return NULL; +} + + +//*** +// +// Function: AddWanNodeToHT +// +// Descr: Inserts a new node in the WAN nodes Hash Table +// +//*** + +VOID +AddWanNodeToHT(PNICCB niccbp) +{ + int hv; + + hv = ndhash(niccbp->RemoteNode); + + ACQUIRE_SPIN_LOCK(&WanNodeHTLock); + + InsertTailList(&WanNodeHT[hv], &niccbp->WanHtLinkage); + + RELEASE_SPIN_LOCK(&WanNodeHTLock); +} + +//*** +// +// Function: RemoveWanNodeFromHT +// +// Descr: Removes a WAN node from the WAN nodes Hash Table +// +//*** + +VOID +RemoveWanNodeFromHT(PNICCB niccbp) +{ + int hv; + + hv = ndhash(niccbp->RemoteNode); + + ACQUIRE_SPIN_LOCK(&WanNodeHTLock); + + RemoveEntryList(&niccbp->WanHtLinkage); + + RELEASE_SPIN_LOCK(&WanNodeHTLock); +} diff --git a/private/ntos/tdi/isn/rip/ripproc.c b/private/ntos/tdi/isn/rip/ripproc.c new file mode 100644 index 000000000..dc98e307d --- /dev/null +++ b/private/ntos/tdi/isn/rip/ripproc.c @@ -0,0 +1,745 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: ripproc.c +// +// Description: process rip packets +// +// Author: Stefan Solomon (stefans) October 11, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +VOID +RipRequest(PPACKET_TAG pktp); + +VOID +RipResponse(PPACKET_TAG pktp); + +VOID +SetNetworkEntry(PUCHAR nep, + PIPX_ROUTE_ENTRY rtep); + +//*** +// +// Function: ProcessRipPacket +// +// Descr: +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +ProcessRipPacket(PPACKET_TAG pktp) +{ + USHORT opcode; + PUCHAR hdrp; // ptr to the packet header + + // get a ptr to the packet header + hdrp = pktp->DataBufferp; + + // check the RIP operation type + GETSHORT2USHORT(&opcode, hdrp + RIP_OPCODE); + + switch(opcode) { + + case RIP_REQUEST: + + RipRequest(pktp); + break; + + case RIP_RESPONSE: + + RipResponse(pktp); + break; + + default: + + // this is an invalid frame + + RtPrint(DBG_NOTIFY, ("IpxRouter: ProcessRipPacket: Reject invalid frame\n")); + FreeRcvPkt(pktp); + break; + } +} + +//*** +// +// Function: RipRequest +// +// Descr: process the RIP request +// +//*** + +VOID +RipRequest(PPACKET_TAG pktp) +{ + USHORT reqlen; // offset to get next request + USHORT resplen; // offset to put next response + USHORT pktlen; // packet length + PUCHAR hdrp; // ptr to the packet header + PNICCB niccbp; // ptr to nic ctrl blk that received this packet + PNICCB rtniccbp; + PIPX_ROUTE_ENTRY rtep; + KIRQL oldirql; + PRIP_SNDREQ respcbp; + UINT segment; + BOOLEAN PingRouter = FALSE; + + RtPrint(DBG_RIP, ("IpxRouter: RipRequest: Entered\n")); + + // get a ptr to the packet owner Nic + niccbp = pktp->PacketOwnerNicCbp; + + // if there is no network number for this NIC we don't reply to this RIP + // request + if(!memcmp(niccbp->Network, nulladdress, IPX_NET_LEN)) { + + RtPrint(DBG_NOTIFY, ("IpxRouter: Cannot reply to RIP request on unnumbered net for NIC %d\n", + niccbp->NicId)); + + // free the packet buffer and return + FreeRcvPkt(pktp); + + return; + } + + // get a ptr to the packet header + hdrp = pktp->DataBufferp; + + // get IPX packet length + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + // We may have one or more network entry requests in the packet. + // If one network entry is 0xFFFFFFFF, then a general RIP response is + // requested. + + // for each network entry, try to get the answer from our routing table + for(reqlen = resplen = RIP_INFO; + reqlen < pktlen; + reqlen += NE_ENTRYSIZE) { + + // check if a general response is requested + if(!memcmp(hdrp + reqlen + NE_NETNUMBER, bcastaddress, IPX_NET_LEN)) { + + // queue a req for rip gen response for this net and free the packet. + // the req will be dequeued and processed by the rip response + // thread. + if((respcbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_SNDREQ))) != NULL) { + + // set up the send request + respcbp->SndReqId = RIP_GEN_RESPONSE; + respcbp->SendOnAllNics = FALSE; // send only to requesting node + // fill in the sending node address, to be used in the response + memcpy(respcbp->DestNode, hdrp + IPXH_SRCNODE, 6); + + // fill in the sending socket to be used in the response + GETSHORT2USHORT(&respcbp->DestSock, hdrp + IPXH_SRCSOCK); + + respcbp->DoNotSendNicCbp = NULL; + respcbp->SenderNicCbp = niccbp; + respcbp->SndCompleteEventp = NULL; + + if(!RipQueueSndReqAtNic(niccbp, respcbp)) { + + // can't queue this request + ExFreePool(respcbp); + respcbp = NULL; + } + } + + FreeRcvPkt(pktp); + return; + } + + //*** a specific response is requested. *** + // if the requested network number is 0, we replace it with + // the network segment the packet was received on: + if(!memcmp(hdrp + reqlen + NE_NETNUMBER, nulladdress, IPX_NET_LEN)) { + + RtPrint(DBG_RIP, ("IpxRouter: RipRequest: request info on directly attached net\n")); + memcpy(hdrp + reqlen + NE_NETNUMBER, niccbp->Network, IPX_NET_LEN); + PingRouter = TRUE; + } + + segment = IpxGetSegment(hdrp + reqlen + NE_NETNUMBER); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql); + + if(rtep = IpxGetRoute(segment, hdrp + reqlen + NE_NETNUMBER)) { + + // check if we can route the packet + // the route should be on a different nic id than the received + // packet. For the global WAN net, rtep->NicId = 0xFFFE ! + if(rtep->NicId != niccbp->NicId) { + + // if the response will be sent on a WAN link then there + // is some filtering to do + if(niccbp->DeviceType == NdisMediumWan) { + + // check if the target net is the global WAN net + if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) { + + // This is a request received on WAN and the target is not + // the global WAN net + rtniccbp = NicCbPtrTab[rtep->NicId]; + + // check if the target net is visible via a WAN-Disabled LAN + if((rtniccbp->DeviceType != NdisMediumWan) && + (rtniccbp->WanRoutingDisabled)) { + + // the target is a LAN disabled for WAN traffic + // skip it! + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + + // check if the nic to send on is a WAN client. + if(niccbp->WanConnectionClient) { + + // Check if LAN-WAN-LAN connectivity is enabled + if(!LanWanLan) { + + // this node can only inform about its virtual net + if(rtniccbp->NicId != VirtualNicId) { + + // skip it! + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + } + } + } + else + { + // this is a request received on WAN and the target is the + // global WAN net. + + // check if the nic to send on doesn't have the same WAN address + if(!memcmp(rtep->Network, niccbp->Network, 4)) { + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + + // check if the nic to send on is a WAN client. + if(niccbp->WanConnectionClient) { + + // Check if LAN-WAN-LAN connectivity is enabled + if(!LanWanLan) { + + // this node can only inform about its virtual net + // and the global net is not the virtual net + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + } + } + } + else + { + // The response will be sent on a LAN. + + // if LAN to LAN routing is disabled and if the target + // route is from a LAN Nic, we do not repond to it + if(!EnableLanRouting) { + + // LAN routing is disabled -> the only routes we can + // respond with are the virtual net or any WAN net + if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) { + + // the target net is not the global wan net + // check if it is not the virtual net + if(rtep->NicId != VirtualNicId) { + + // the target net is not the virtual net + // check the it is not a LAN net + if(NicCbPtrTab[rtep->NicId]->DeviceType != NdisMediumWan) { + + // The target net is NOT: + // 1. the global wan net + // 2. the vitual net + // 3. a WAN net + // I.e. -> target is a LAN net -> do not answer + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + } + } + } + + // if the destination nic is WAN and has a client role and + // LAN-WAN-LAN traffic is disabled, do not respond to it + if(!LanWanLan) { + + if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) { + + if( (NicCbPtrTab[rtep->NicId]->DeviceType == NdisMediumWan) && + (NicCbPtrTab[rtep->NicId]->WanConnectionClient)) { + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + } + } + + } + + // we can route it -> answer to it + // fill in the network entry structure in the packet with the + // info from the route entry + SetNetworkEntry(hdrp + resplen, rtep); + + // increment the response length to the next response entry + resplen += NE_ENTRYSIZE; + } + + if(PingRouter) { + + // answer to the ping request + SetNetworkEntry(hdrp + resplen, rtep); + + // increment the response length to the next response entry + resplen += NE_ENTRYSIZE; + } + } + + PingRouter = FALSE; + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + } + + // We are done answering this request. + // Check if any response has been generated + if(resplen == RIP_INFO) { + + // no response generated for this packet + RtPrint(DBG_RIP, ("IpxRouter: RipRequest: no response send for this request\n")); + FreeRcvPkt(pktp); + return; + } + + // Turn the packet around and send it. This is done by changing the + // packet's src and dest nodes and sockets. (src and dst net are the same) + memcpy(hdrp + IPXH_DESTNODE, hdrp + IPXH_SRCNODE, 6); + memcpy(hdrp + IPXH_SRCNODE, niccbp->Node, 6); + + memcpy(hdrp + IPXH_DESTSOCK, hdrp + IPXH_SRCSOCK, 2); + PUTUSHORT2SHORT(hdrp + IPXH_SRCSOCK, IPX_RIP_SOCKET); + + // change the packet type to RIP response + PUTUSHORT2SHORT(hdrp + RIP_OPCODE, RIP_RESPONSE); + + // set the new packet length + PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, resplen); + + // prepare remote address structure in the packet tag to send the packet + pktp->RemoteAddress.NicId = niccbp->NicId; + memcpy(pktp->RemoteAddress.MacAddress, hdrp + IPXH_DESTNODE, 6); + + // Send the packet. The packet will be freed when send completes. + RtPrint(DBG_RIP, ("IpxRouter: RipRequest: send response send for this request\n")); + + SendPacket(pktp); +} + +//*** +// +// Function: RipResponse +// +// Descr: Updates the routing table with the response info +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +RipResponse(PPACKET_TAG pktp) +{ + USHORT resplen; // offset of the next response network entry + USHORT pktlen; // IPX packet length + PUCHAR hdrp; // ptr to the packet header + PNICCB niccbp; // ptr to the nic ctrl blk the packet that + // received the packet + PIPX_ROUTE_ENTRY oldrtep, newrtep; // new and old routing tab entries + KIRQL oldirql; + USHORT nrofhops; + BOOLEAN RouteDown; + PRIP_UPDATE_SNDREQ respcbp = NULL; // ptr to changes response to bcast + PRIP_SNDREQ sndreqp; + PUCHAR sndhdrp; + USHORT sndpktlen; + UINT segment; + USHORT tickcount; + +#if DBG + UCHAR b[6]; +#endif + + RtPrint(DBG_RIP, ("IpxRouter: RipResponse: Entered\n")); + + // get a ptr to this Nic + niccbp = pktp->PacketOwnerNicCbp; + + // get a ptr to the received response packet header + hdrp = pktp->DataBufferp; + + // get received response packet length + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + // For each network entry, check if we have it in our routing table. + // If we do not have this entry or if + // it is a better entry than what we have, we add it to the routing table + // !!! for this primary version, we don't care about better entries !!! + + for(resplen = RIP_INFO; + resplen < pktlen; + resplen += NE_ENTRYSIZE) { + + newrtep = NULL; + // check if the network route is up or down + GETSHORT2USHORT(&nrofhops, hdrp + resplen + NE_NROFHOPS); + + if(nrofhops < 16) { + + RouteDown = FALSE; + } + else + { + RouteDown = TRUE; + } + + segment = IpxGetSegment(hdrp + resplen + NE_NETNUMBER); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql); + + // check if the entry exists. + if((oldrtep = IpxGetRoute(segment, hdrp + resplen + NE_NETNUMBER)) == NULL) { + + //*** This route does not exist *** + + // if this is a route down bcast and we didn't have this route + // we skip this information + if(RouteDown) { + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + + // if this is a route with 15 hops, we choose for now to ignore + // it. + if(nrofhops == 15) { + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + continue; + } + + // This is a new route. We add this route to the routing table + if(newrtep = ExAllocatePool(NonPagedPool, sizeof(IPX_ROUTE_ENTRY))) { + + // set it up + memcpy(newrtep->Network, hdrp + resplen + NE_NETNUMBER, IPX_NET_LEN); + newrtep->NicId = niccbp->NicId; + memcpy(newrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN); + newrtep->Flags = 0; + newrtep->Timer = 0; // TTL of this route entry is 3 min + newrtep->Segment = segment; + GETSHORT2USHORT(&newrtep->TickCount, hdrp + resplen + NE_NROFTICKS); + GETSHORT2USHORT(&newrtep->HopCount, hdrp + resplen + NE_NROFHOPS); + + // increment the hop count to reflect our new router in the path + // the tick count received from the other router includes the + // nr of ticks on the network segment it was sent, so no adjust + // is necessary + newrtep->HopCount++; + + InitializeListHead(&newrtep->AlternateRoute); + + // add it to the table + IpxAddRoute(segment, newrtep); + } + } + else + { + // + //*** This route exists in our routing table *** + // + + // first check if the response is coming from + // the same router as the one which we got the route from + if(memcmp(oldrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN)) { + + // + //** This is a response from another router for the same network + // + + // check if the response is : route unreachable + if(RouteDown) { + + // useless response -> discard it! + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); +#if DBG + memcpy(b, hdrp + resplen + NE_NETNUMBER, 4); +#endif + continue; + } + + // if the route is not down but this is a better route, add it + // instead. + + // Check if this is a better route + GETSHORT2USHORT(&tickcount, hdrp + resplen + NE_NROFTICKS); + if(tickcount >= oldrtep->TickCount) { + + // same or worse route, ignore it! + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); +#if DBG + memcpy(b, hdrp + resplen + NE_NETNUMBER, 4); +#endif + + continue; + } + + // Ignore this response if it refers to a permanent/local + // net + if((oldrtep->Flags & IPX_ROUTER_PERMANENT_ENTRY) || + (oldrtep->Flags & IPX_ROUTER_LOCAL_NET)) { + + // skip this route! + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + continue; + } + + // We have a better route + // Just copy the new route over the old route entry + // we do not modify the network and segment fields + + oldrtep->NicId = niccbp->NicId; + memcpy(oldrtep->NextRouter, hdrp + IPXH_SRCNODE, IPX_NODE_LEN); + oldrtep->Flags = 0; + oldrtep->Timer = 0; // TTL of this route entry is 3 min + GETSHORT2USHORT(&oldrtep->TickCount, hdrp + resplen + NE_NROFTICKS); + GETSHORT2USHORT(&oldrtep->HopCount, hdrp + resplen + NE_NROFHOPS); + + // increment the hop count to reflect our new router in the path + // the tick count received from the other router includes the + // nr of ticks on the network segment it was sent, so no adjust + // is necessary + oldrtep->HopCount++; + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); +#if DBG + memcpy(b, hdrp + resplen + NE_NETNUMBER, 4); +#endif + + continue; + } + + // + //*** This response comes from the same router as the one which gave + //*** us this route entry initially + // + + // First -> reset the aging timer + oldrtep->Timer = 0; + + // if the info tells us that the route is down, we should delete + // the route from the routing table and inform all other routers + // of the change + if(RouteDown) { + + // this may be a bogus packet from the wire + if((oldrtep->Flags & IPX_ROUTER_PERMANENT_ENTRY) || + (oldrtep->Flags & IPX_ROUTER_LOCAL_NET)) { + +#if DBG + memcpy(b, hdrp + IPXH_SRCNODE, 6); +#endif + RtPrint(DBG_NOTIFY, ("IpxRouter: RipResponse: Bogus resp permanent route down from %x-%x-%x-%x-%x-%x !!\n", + b[0],b[1],b[2],b[3],b[4],b[5])); + + RouteDown = FALSE; + } + else + { + IpxDeleteRoute(segment, oldrtep); + + // set the nr of hops to 16 in the route entry; used later to + // bcast the change + oldrtep->HopCount = 16; + } + } + else + { + // update hop and tick counts + GETSHORT2USHORT(&oldrtep->TickCount, hdrp + resplen + NE_NROFTICKS); + GETSHORT2USHORT(&oldrtep->HopCount, hdrp + resplen + NE_NROFHOPS); + + // increment the hop count to reflect our new router in the path + // the tick count received from the other router includes the + // nr of ticks on the network segment it was sent, so no adjust + // is necessary + oldrtep->HopCount++; + } + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + // if the rip response packet is bigger than the standard length we will + // fragment the bcast update and broadcast it with the rip timer + if(pktlen > RIP_RESPONSE_PACKET_LEN) { + + if(oldrtep && RouteDown) { + + ExFreePool(oldrtep); + } + + continue; + } + + // if the RIP response packet comes from a LAN and if the LAN to LAN + // routing is disabled, we won't broadcast any update. This is so because: + // 1. We don't broadcast updates on WAN + // 2. We broadcast only WAN updates on LAN if LAN routing disabled + if((!EnableLanRouting) && + (niccbp->DeviceType != NdisMediumWan)) { + + if(oldrtep && RouteDown) { + + ExFreePool(oldrtep); + } + + continue; + } + + // check if we will broadcast any change now and if a bcast packet + // request + buffer have been allocated + if (newrtep || (oldrtep && RouteDown)) { + + // check if a send bcast req struct has already been allocated + if(respcbp == NULL) { + + // allocate a send request struct to bcast changes in the routing table + respcbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_UPDATE_SNDREQ) + RIP_SNDPKT_MAXLEN); + + if(respcbp != NULL) { + + // get the ipx hdr ptr for the send packet + sndhdrp = (PUCHAR)respcbp->RipSndPktBuff.IpxPacket; + + // set the initial length + sndpktlen = RIP_INFO; + } + } + } + + // check if we have added a new route or deleted an old one + if(newrtep) { + + // write the network entry for this new route in the packet to be + // sent + if(respcbp) { + + // fill in the network entry structure in the packet with the + // info from the route entry + SetNetworkEntry(sndhdrp + sndpktlen, newrtep); + + // increment the send packet length to the next network entry + sndpktlen += NE_ENTRYSIZE; + } + } + else + { + // check if an old route has been deleted + if(oldrtep && RouteDown) { + + // write the network entry for this old route in the packet to be + // sent + if(respcbp) { + + // fill in the network entry structure in the packet with the + // info from the route entry + SetNetworkEntry(sndhdrp + sndpktlen, oldrtep); + + // increment the send packet length to the next network entry + sndpktlen += NE_ENTRYSIZE; + + ExFreePool(oldrtep); + } + } + } + } + + // if we have added or deleted some routes, we make a request to the rip bcast + // thread to have them bcasted over to the other nics + + if(respcbp) { + + // Send a bcast update with the changes to all the nics except this one + sndreqp = &respcbp->RipSndReq; + // set the new packet length + PUTUSHORT2SHORT(sndhdrp + IPXH_LENGTH, sndpktlen); + + BroadcastRipUpdate(sndreqp, niccbp, NULL); + } + + // free the packet + FreeRcvPkt(pktp); +} + + +//*** +// +// Function: SetNetworkEntry +// +// Descr: +// +//*** + +VOID +SetNetworkEntry(PUCHAR nep, // points to the network entry in the + // RIP packet + PIPX_ROUTE_ENTRY rtep) +{ + memcpy(nep + NE_NETNUMBER, rtep->Network, IPX_NET_LEN); + PUTUSHORT2SHORT(nep + NE_NROFHOPS, rtep->HopCount); + PUTUSHORT2SHORT(nep + NE_NROFTICKS, rtep->TickCount); +} diff --git a/private/ntos/tdi/isn/rip/ripsend.c b/private/ntos/tdi/isn/rip/ripsend.c new file mode 100644 index 000000000..229388e6c --- /dev/null +++ b/private/ntos/tdi/isn/rip/ripsend.c @@ -0,0 +1,1176 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: ripsend.c +// +// Description: processes rip send requests queued at the different +// queues: send all nets and send one net +// +// Author: Stefan Solomon (stefans) October 11, 1993. +// +// Revision History: +// +//*** + +#include +#include +#include "rtdefs.h" + +NDIS_SPIN_LOCK RipGlobalSndListLock; +LIST_ENTRY RipGlobalSndList; + +// this global specifies the type of RIP updates on WAN. +// For this first version it is set to 0 -> No Rip updates on WAN. +ULONG RipWanUpdate; + +VOID +StartRipSndAtNic(PVOID Parameter); + +VOID +InterPktGapTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +PNICCB +GetFirstAvailabelNic(USHORT BaseNicId, + PNICCB DoNotSendNicCbp); + +UINT +MakeRipSendPkts(PNICCB niccbp); + +UINT +MakeRipGenResponsePkts(PNICCB niccbp); + +PPACKET_TAG +AllocRipGenResponsePkt(PNICCB niccbp); + +UINT +MakeRipUpdatePkt(PNICCB niccbp); + +UINT +MakeRipGenRequestPkt(PNICCB niccbp); + +PPACKET_TAG +CreateRipNdisPkt(PRIP_SNDPKT_BUFF rbp, + PNICCB niccbp); + +PRIP_SNDPKT_BUFF +DestroyRipNdisPkt(PPACKET_TAG pktp); + +PNICCB +GetFirstAvailableNic(USHORT BaseNicId, + PNICCB DoNotSendNicCbp); + + +VOID +StartInterPktGapTimer(PNICCB niccbp); + +VOID +SetRipIpxHeader(PUCHAR hdrp, // pointer to the packet header + PNICCB niccbp, // pointer to the rip send request + USHORT RipOpcode); + +VOID +SetRipRemoteAddress(PPACKET_TAG pktp, + PNICCB niccbp); + +//*** +// +// Function: InitRipSndDispatcher +// +// Descr: initializes the global Rip send dispatcher +// +// Params: none +// +// Returns: none +// +//*** + +VOID +InitRipSndDispatcher(VOID) +{ + INITIALIZE_SPIN_LOCK(&RipGlobalSndListLock); + InitializeListHead(&RipGlobalSndList); +} + +//*** +// +// Function: InitRipSndAtNic +// +// Descr: Called at nic init time. +// Initializes the nic based Rip send machine +// +// Params: none +// +// Returns: none +// +//*** + + +VOID +InitRipSndAtNic(PNICCB niccbp) +{ + InitializeListHead(&niccbp->RipSendQueue); + + // set the rip send machine to IDLE state + niccbp->RipSndReqp = NULL; + + InitializeListHead(&niccbp->RipSndPktsList); + + ExInitializeWorkItem(&niccbp->RipSndReqWorkItem, StartRipSndAtNic, niccbp); + + KeInitializeDpc(&niccbp->InterPktGapDpc, InterPktGapTimeout, niccbp); + KeInitializeTimer(&niccbp->InterPktGapTimer); +} + +//*** +// +// Function: RipDispatchSndReq +// +// Descr: +// +//*** + +VOID +RipDispatchSndReq(PRIP_SNDREQ sndreqp) +{ + PNICCB niccbp; + + RtPrint(DBG_SNDREQ, ("IpxRouter: RipDispatchSndReq: Entered\n")); + + // get the first available nic to get the send request + // a non-null value in the DoNotSendNicCbp indicates we should not use + // this nic as a sender + niccbp = GetFirstAvailableNic(0, sndreqp->DoNotSendNicCbp); + if(niccbp == NULL) { + + // if this was an update AND we are unloading AND event has to be + // signaled, do it + if((sndreqp->SndReqId == RIP_UPDATE) && + (sndreqp->SndCompleteEventp != NULL) && + RouterUnloading) { + + KeSetEvent(sndreqp->SndCompleteEventp, 0L, FALSE); + } + + ExFreePool(sndreqp); + return; + } + ACQUIRE_SPIN_LOCK(&RipGlobalSndListLock); + + InsertTailList(&RipGlobalSndList, &sndreqp->GlobalLinkage); + + RELEASE_SPIN_LOCK(&RipGlobalSndListLock); + + sndreqp->SenderNicCbp = niccbp; + if(!RipQueueSndReqAtNic(niccbp, sndreqp)) { + + // can't dispatch to this nic, call completion + RipSendAtNicCompleted(sndreqp); + } +} + + +VOID +RipSendAtNicCompleted(PRIP_SNDREQ sndreqp) +{ + BOOLEAN Done = FALSE; // we are done with this snd req + BOOLEAN QueuedAtNic = FALSE; // request queued at a nic + PNICCB niccbp; + + RtPrint(DBG_SNDREQ, ("IpxRouter: RipSendAtNicCompleted: Entered for NicId %d\n", sndreqp->SenderNicCbp->NicId)); + + if(!sndreqp->SendOnAllNics) { + + // this request had to be sent on one nic only and is now terminated + Done = TRUE; + } + else + { + // this request has to be sent on all nics + // loop until the request is queued at a nic OR there are no more + // nics available + while(!Done && !QueuedAtNic) { + + // get the next available nic to get the send request + niccbp = GetFirstAvailableNic(sndreqp->SenderNicCbp->NicId, + sndreqp->DoNotSendNicCbp); + if(niccbp == NULL) { + + // No nic available + ACQUIRE_SPIN_LOCK(&RipGlobalSndListLock); + + RemoveEntryList(&sndreqp->GlobalLinkage); + + RELEASE_SPIN_LOCK(&RipGlobalSndListLock); + Done = TRUE; + } + else + { + sndreqp->SenderNicCbp = niccbp; + QueuedAtNic = RipQueueSndReqAtNic(niccbp, sndreqp); + } + } + } + + if(Done) { + + // if this was an update and we are unloading and event has to be + // signaled, do it + if((sndreqp->SndReqId == RIP_UPDATE) && + (sndreqp->SndCompleteEventp != NULL) && + RouterUnloading) { + + KeSetEvent(sndreqp->SndCompleteEventp, 0L, FALSE); + } + + ExFreePool(sndreqp); + } +} + +//*** +// +// Function: RipQueueSndReqAtNic +// +// Descr: Queues the Rip send req at this nic for delivery +// The Nic has been verified that is active prior to queueing +// +// Returns: TRUE - queued OK, FALSE - could not queue +// +//*** + +BOOLEAN +RipQueueSndReqAtNic(PNICCB niccbp, + PRIP_SNDREQ sndreqp) +{ + // check if we should start work right away or should queue it + // for deffered processing + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + // check if rip sending is enabled on this nic + if(niccbp->NicState != NIC_ACTIVE) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + RtPrint(DBG_SNDREQ, ("IpxRouter: RipQueueSndReqAtNic: NicId %d is closed or closing, cannot queue the snd req\n", niccbp->NicId)); + + return FALSE; + } + + // check if the rip send machine is ACTIVE + if(niccbp->RipSndReqp != NULL) { + + // the machine is ACTIVE processing another snd req; queue for later + InsertTailList(&niccbp->RipSendQueue, &sndreqp->NicLinkage); + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + RtPrint(DBG_SNDREQ, ("IpxRouter: RipQueueSndReqAtNic: Queued for NicId %d\n", niccbp->NicId)); + + return TRUE; + } + + // The RIP send machine for this Nic is IDLE. Activate it. + niccbp->RipSndReqp = sndreqp; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + RtPrint(DBG_SNDREQ, ("IpxRouter: RipQueueSndReqAtNic: Started for NicId %d\n", niccbp->NicId)); + + // if this is a directed response queue it in the critical, else noncritical + if((sndreqp->SndReqId == RIP_GEN_RESPONSE) && + (memcmp(sndreqp->DestNode, bcastaddress, IPX_NODE_LEN))) { + + ExQueueWorkItem(&niccbp->RipSndReqWorkItem, CriticalWorkQueue); + } + else + { + ExQueueWorkItem(&niccbp->RipSndReqWorkItem, DelayedWorkQueue); + } + + return TRUE; +} + +//*** +// +// Function: StartRipSndAtNic +// +// Descr: This routine is the work item queued by +// RipQueueSndReqAtNic when it starts the Rip Send Machine for +// this Nic. +// +// Params: Ptr to the Nic +// +//*** + +VOID +StartRipSndAtNic(PVOID Parameter) +{ + PNICCB niccbp; + PLIST_ENTRY lep; + PPACKET_TAG pktp; + + niccbp = (PNICCB)Parameter; + + // check that the rip send machine has been activated + ASSERT(niccbp->RipSndReqp != NULL); + + // allocate and prepare the packets for this send and queue them + // at the nic + while(MakeRipSendPkts(niccbp)) { + + // No packets have been prepared. Notify the dispatcher that we are + // done with this request. + RipSendAtNicCompleted(niccbp->RipSndReqp); + + // try to get the next send request queued at this nic + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + if(IsListEmpty(&niccbp->RipSendQueue)) { + + // no more work for this Nic -> set it to IDLE state + niccbp->RipSndReqp = NULL; + + // !!! announce the closing machine that the current Rip send processing has terminated !!! + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return; + } + + // there are snd requests queued + lep = RemoveHeadList(&niccbp->RipSendQueue); + + // set rip send machine to ACTIVE state + niccbp->RipSndReqp = CONTAINING_RECORD(lep, RIP_SNDREQ, NicLinkage); + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + } + + //*** send the first prepared packet *** + + lep = RemoveHeadList(&niccbp->RipSndPktsList); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + SendPacket(pktp); +} + +//*** +// +// Function: SendRipPktCompleted +// +// Descr: This function is called by the send completion routine when +// a Rip send packet has been completed. +// +//*** + +VOID +SendRipPktCompleted(PPACKET_TAG pktp) +{ + PNICCB niccbp; + PRIP_SNDPKT_BUFF rbp; + + niccbp = pktp->PacketOwnerNicCbp; + + // Destroy the ndis parts of the packet and get back the original snd pkt + // structure + rbp = DestroyRipNdisPkt(pktp); + + // If this was not an update broadcast, release the send pkt buffer struct + if(niccbp->RipSndReqp->SndReqId != RIP_UPDATE) { + + // free the snd pkt buffer + ExFreePool(rbp); + } + + // set the machine to wait for a interpacket gap + StartInterPktGapTimer(niccbp); +} + +//*** +// +// Function: InterPktGapTimeout +// +// Descr: Called by the timer Nic interpacket gap timer DPC when the +// interpacket gap timeout expired. Sends the next packet in the list +// +//*** + +VOID +InterPktGapTimeout(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PNICCB niccbp; + PLIST_ENTRY lep; + PPACKET_TAG pktp; + + niccbp = (PNICCB)DefferedContext; + + // check if we have more packets to send from this request + if(!IsListEmpty(&niccbp->RipSndPktsList)) { + + // dequeue the first packet from the list and send it + lep = RemoveHeadList(&niccbp->RipSndPktsList); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + + SendPacket(pktp); + return; + } + + //*** This Rip send has been completed *** + + // Notify the UPPER machine + RipSendAtNicCompleted(niccbp->RipSndReqp); + + // check if there are more send requests queued + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + if(IsListEmpty(&niccbp->RipSendQueue)) { + + // set the rip send machine to IDLE state + niccbp->RipSndReqp = NULL; + + // !!! announce the closing machine that the current Rip send processing has terminated !!! + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + return; + } + else + { + // there are snd requests queued + lep = RemoveHeadList(&niccbp->RipSendQueue); + + // set the rip send machine to ACTIVE state + niccbp->RipSndReqp = CONTAINING_RECORD(lep, RIP_SNDREQ, NicLinkage); + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + } + + // start a new work item to take care of this send + // if this is a directed response queue it in the critical, else noncritical + if((niccbp->RipSndReqp->SndReqId == RIP_GEN_RESPONSE) && + (memcmp(niccbp->RipSndReqp->DestNode, bcastaddress, IPX_NODE_LEN))) { + + ExQueueWorkItem(&niccbp->RipSndReqWorkItem, CriticalWorkQueue); + } + else + { + ExQueueWorkItem(&niccbp->RipSndReqWorkItem, DelayedWorkQueue); + } +} + +//*** +// +// Function: GetFirstAvailableNic +// +// Descr: returns the nic cbp ptr of the first nic starting with the +// specified base, which is not the src nic and which is active +// +//*** + +PNICCB +GetFirstAvailableNic(USHORT BaseNicId, + PNICCB DoNotSendNicCbp) +{ + USHORT i; + USHORT StartNicId; + PNICCB niccbp; + + if(BaseNicId == 0) { + + // we start from the beginning of the table + StartNicId = 0; + } + else + { + // check if this was the last nic + if(BaseNicId >= MaximumNicCount - 1) { + + return NULL; + } + + StartNicId = BaseNicId + 1; + } + + for(i=StartNicId; iNicState == NIC_ACTIVE) { + + // check if we should avoid this nic + if(DoNotSendNicCbp != NULL) { + + if(niccbp->NicId == DoNotSendNicCbp->NicId) { + + continue; // skip this nic + } + } + + // if this nic doesn't have a net number, we don't + // send anything on it. (In other words, we are a ROUTER, we can't + // send rip packets with source net == 0) + if(!memcmp(niccbp->Network, nulladdress, IPX_NET_LEN)) { + + continue; // skip this nic + } + + // for LAN-WAN-LAN we should look at the RipWanUpdate parameter + // on how and what to send on WAN. For this version we just + // don't send anything on WAN, unless we received a request for it + if(niccbp->DeviceType == NdisMediumWan) { + + continue; // skip this nic + } + + // we found it + return niccbp; + } + } + + return NULL; +} + +//*** +// +// Function: MakeRipSendPkts +// +// Descr: Invokes the make pkts routine according to the snd req type +// +//*** + +UINT +MakeRipSendPkts(PNICCB niccbp) +{ + PRIP_SNDREQ sndreqp; + UINT rc = 0; // assume success + + sndreqp = niccbp->RipSndReqp; + + switch(sndreqp->SndReqId) { + + case RIP_GEN_RESPONSE: + + rc = MakeRipGenResponsePkts(niccbp); + break; + + case RIP_UPDATE: + + rc = MakeRipUpdatePkt(niccbp); + break; + + case RIP_GEN_REQUEST: + + rc = MakeRipGenRequestPkt(niccbp); + break; + + default: + + ASSERT(FALSE); + break; + } + + return rc; +} + +//*** +// +// Function: MakeRipGenResponsePkts +// +// Descr: allocates and prepares all packets for the RIP gen response +// The routing table is walked and all network entries which +// were not received from this nic are copied into RIP response +// packets. As packets are built, they are queued into the rip +// send pkts queue at the nic. +// +// Params: nic +// +// Returns: 0 - success, 1 - did not prepare any packets +// +//*** + +UINT +MakeRipGenResponsePkts(PNICCB niccbp) +{ + PPACKET_TAG pktp; + UINT seg; + PUCHAR hdrp; + USHORT pktlen; + BOOLEAN FirstRoute; + PRIP_SNDPKT_BUFF rbp; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + PNICCB rtniccbp; + + // allocate first packet and set net entry ptr in the packet + if((pktp = AllocRipGenResponsePkt(niccbp)) == NULL) { + + return 1; + } + + // find out what type of Nic is this Nic + + + hdrp = pktp->DataBufferp; + pktlen = RIP_INFO; + + for(seg=0; segNicId = 0xFFFE ! + if(rtep->NicId != niccbp->NicId) { + + // check if the target net is a global wan net + if(!(rtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET)) { + + // the target is not the global wan net + rtniccbp = NicCbPtrTab[rtep->NicId]; + + // if the response will be sent on a WAN link then there + // is some filtering to do + if(niccbp->DeviceType == NdisMediumWan) { + + // check if the target net is visible via a WAN-Disabled LAN + if((rtniccbp->DeviceType != NdisMediumWan) && + (rtniccbp->WanRoutingDisabled)) { + + // skip it! + continue; + } + + // check if the nic to send on is a WAN client. + if(niccbp->WanConnectionClient) { + + // Check if LAN-WAN-LAN connectivity is enabled + if(!LanWanLan) { + + // this node can only inform about its virtual net + if(rtniccbp->NicId != VirtualNicId) { + + // skip it! + continue; + } + } + } + } + else + { + // The response will be sent on a LAN net + // check if LAN to LAN routing is enabled + if(!EnableLanRouting) { + + // LAN to LAN routing is disabled + // We will send a response only if the target net + // is WAN or virtual nic id + if(rtniccbp->NicId != VirtualNicId) { + + // The target is not the virtual net, check if + // it is a WAN net + if(rtniccbp->DeviceType != NdisMediumWan) { + + // The target net is a LAN -> don't answer + continue; + } + } + } + } + + // if the route originates from a wan client nic and + // LAN-WAN-LAN connectivity is not enabled, we do not propagate + // this route + if(!LanWanLan) { + + if((rtniccbp->DeviceType == NdisMediumWan) && + (rtniccbp->WanConnectionClient)) { + + // skip it! + continue; + } + } + } + else + { + // the target net is the global wan net + // check if the nic to send on is WAN + if(niccbp->DeviceType == NdisMediumWan) { + + // check if the WAN nic to send on doesn't have the same address + if(!memcmp(rtep->Network, niccbp->Network, 4)) { + + // skip it! + continue; + } + + // check if the WAN nic to send on is a connection client. + // We can send this info on a connection client only if LanWanLan + // is enabled + + if(niccbp->WanConnectionClient) { + + if(!LanWanLan) { + + // skip it! + continue; + } + } + } + } + + SetNetworkEntry(hdrp + pktlen, rtep); + pktlen += NE_ENTRYSIZE; + } + + if(pktlen >= RIP_RESPONSE_PACKET_LEN) { + + // we are done with this packet + PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, pktlen); + InsertTailList(&niccbp->RipSndPktsList, &pktp->PacketLinkage); + + if((pktp = AllocRipGenResponsePkt(niccbp)) == NULL) { + + // we can't go any further, we are partially done and we + // send what we have got so far + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + return 0; + } + + // we have got a new packet + hdrp = pktp->DataBufferp; + pktlen = RIP_INFO; + } + + } // while + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + } + + // we are done with this last packet. + // check if it has any entries + if(pktlen > RIP_INFO) { + + PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, pktlen); + InsertTailList(&niccbp->RipSndPktsList, &pktp->PacketLinkage); + } + else + { + // this packet does not have any rip info => free it + rbp = DestroyRipNdisPkt(pktp); + ExFreePool(rbp); + } + + // check if we have produced any packets + if(IsListEmpty(&niccbp->RipSndPktsList)) { + + return 1; + } + else + { + return 0; + } +} + +//*** +// +// Function: GetRoute +// +// Descr: invokes getfisrt route the first time, then get next route. +// +//*** + +PIPX_ROUTE_ENTRY +GetRoute(UINT segment, + BOOLEAN FirstRoute) +{ + if(FirstRoute) { + + return(IpxGetFirstRoute(segment)); + } + else + { + return(IpxGetNextRoute(segment)); + } +} + +//*** +// +// Function: AllocRipGenResponsePkt +// +// Descr: allocates the data buffer and the ndis descriptors and makes a +// gen response packet header in the data buffer. +// +//*** + +PPACKET_TAG +AllocRipGenResponsePkt(PNICCB niccbp) +{ + PPACKET_TAG pktp; + PRIP_SNDPKT_BUFF rbp; + + // allocate the send packet buffer for this packet. Do a max len allocation + if((rbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_SNDPKT_BUFF) + RIP_SNDPKT_MAXLEN)) == NULL) { + + return NULL; + } + + // create the ndis packet structures + // an ndis packet gets created and associated with the snd pkt buffer + if((pktp = CreateRipNdisPkt(rbp, niccbp)) == NULL) { + + ExFreePool(rbp); + + return NULL; + } + + // set the ipx header in the packet + SetRipIpxHeader(pktp->DataBufferp, niccbp, RIP_RESPONSE); + + // set the remote (destination) address + SetRipRemoteAddress(pktp, niccbp); + + return pktp; +} + +//*** +// +// Function: MakeRipUpdatePkt +// +// Descr: allocates only the ndis pkt and buff descriptors. The data buff +// has been already allocated in the snd req pkt. +// Then makes the ndis packet, formats the ipx header and queues +// the packet int the nic's rip send pkts queue +//*** + +UINT +MakeRipUpdatePkt(PNICCB niccbp) +{ + PPACKET_TAG pktp; + PRIP_SNDPKT_BUFF rbp; + PRIP_UPDATE_SNDREQ rup; + + // get the ptr to the send pkt buffer + rup = (PRIP_UPDATE_SNDREQ)(niccbp->RipSndReqp); + rbp = &rup->RipSndPktBuff; + + // create the ndis packet for this update send + if((pktp = CreateRipNdisPkt(rbp, niccbp)) == NULL) { + + return 1; + } + + // set up the ipx header in the packet + SetRipIpxHeader(pktp->DataBufferp, niccbp, RIP_RESPONSE); + + // set up the remote address + SetRipRemoteAddress(pktp, niccbp); + + // insert the packet in the list of packets at the nic + InsertTailList(&niccbp->RipSndPktsList, &pktp->PacketLinkage); + + return 0; +} + +//*** +// +// Function: MakeRipGenRequestPkt +// +// Descr: allocates the data buffer and the buffer descr and makes +// the ndis packet. Formats the request header and data and queues +// the packet in the RipSndPktsList +// +//*** + +UINT +MakeRipGenRequestPkt(PNICCB niccbp) +{ + PPACKET_TAG pktp; + PRIP_SNDPKT_BUFF rbp; + PUCHAR hdrp; + + // allocate the minimum send packet buffer for this packet + if((rbp = ExAllocatePool(NonPagedPool, + sizeof(RIP_SNDPKT_BUFF) + RIP_SNDPKT_MINLEN)) == NULL) { + + return 1; + } + + // create the ndis packet structures + // an ndis packet gets created and associated with the snd pkt buffer + if((pktp = CreateRipNdisPkt(rbp, niccbp)) == NULL) { + + ExFreePool(rbp); + + return 1; + } + // create the Ipx packet header in the data buffer part of the snd pkt buff + hdrp = pktp->DataBufferp; + + PUTUSHORT2SHORT(hdrp + IPXH_LENGTH, RIP_INFO + NE_ENTRYSIZE); + + // set the rest of the ipx header + SetRipIpxHeader(hdrp, niccbp, RIP_REQUEST); + + // set up the remote address in the packet to send + SetRipRemoteAddress(pktp, niccbp); + + // set up the gen request net entry in the packet + memcpy(hdrp + RIP_INFO + NE_NETNUMBER, bcastaddress, IPX_NET_LEN); + PUTUSHORT2SHORT(hdrp + RIP_INFO + NE_NROFHOPS, 0xFFFF); + PUTUSHORT2SHORT(hdrp + RIP_INFO + NE_NROFTICKS, 0xFFFF); + + // insert the packet in the list of packets at the nic + InsertTailList(&niccbp->RipSndPktsList, &pktp->PacketLinkage); + + return 0; +} + +//*** +// +// Function: CreateRipNdisPkt +// +// Descr: allocates the ndis pkt descr pool of 1 and buff descr pool of +// 2, allocates the pkt descr and buff descrs and chains them to +// form the necessary ndis pkt structure using the received snd +// buffer +//*** + +PPACKET_TAG +CreateRipNdisPkt(PRIP_SNDPKT_BUFF rbp, + PNICCB niccbp) +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisDataBuffer; + PNDIS_BUFFER NdisMacBuffer; + UINT PktReservedLen; + PPACKET_TAG pktp; + + // Allocate the packet descriptor and buffer descriptor pools + // for this packet + PktReservedLen = sizeof(PACKET_TAG); + rbp->PktDescrPoolSize = 1; + + NdisAllocatePacketPool( + &NdisStatus, + &rbp->PktDescrPoolHandle, + rbp->PktDescrPoolSize, + PktReservedLen); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + return NULL; + } + + // each packet has 2 buffer descriptors + rbp->BuffDescrPoolSize = 2; + + NdisAllocateBufferPool ( + &NdisStatus, + &rbp->BuffDescrPoolHandle, + rbp->BuffDescrPoolSize); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacketPool(rbp->PktDescrPoolHandle); + return NULL; + } + + // allocate the pkt descr, buff descriptors and chain them + NdisAllocatePacket(&NdisStatus, + &NdisPacket, + rbp->PktDescrPoolHandle); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacketPool(rbp->PktDescrPoolHandle); + NdisFreeBufferPool(rbp->BuffDescrPoolHandle); + return NULL; + } + + pktp = (PPACKET_TAG)&NdisPacket->ProtocolReserved; + RtlZeroMemory(pktp, sizeof(PACKET_TAG)); + + NdisAllocateBuffer(&NdisStatus, + &NdisDataBuffer, + rbp->BuffDescrPoolHandle, + rbp->IpxPacket, + 432); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacket(NdisPacket); + NdisFreePacketPool(rbp->PktDescrPoolHandle); + NdisFreeBufferPool(rbp->BuffDescrPoolHandle); + + return NULL; + } + + NdisAllocateBuffer(&NdisStatus, + &NdisMacBuffer, + rbp->BuffDescrPoolHandle, + pktp->MacHeader, + MacHeaderNeeded); + + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + NdisFreePacket(NdisPacket); + NdisFreeBuffer(NdisDataBuffer); + NdisFreePacketPool(rbp->PktDescrPoolHandle); + NdisFreeBufferPool(rbp->BuffDescrPoolHandle); + + return NULL; + } + + NdisChainBufferAtFront(NdisPacket, NdisDataBuffer); + pktp->Identifier = IDENTIFIER_RIP; + pktp->ReservedPvoid[0] = NULL; + pktp->ReservedPvoid[1] = NULL; + pktp->PacketType = RIP_SEND_PACKET; + pktp->RcvPktSegmentp = NULL; + pktp->DataBufferp = (PUCHAR)(rbp->IpxPacket); + pktp->DataBufferLength = 432; + pktp->PacketOwnerNicCbp = niccbp; + pktp->HeaderBuffDescrp = NdisMacBuffer; + + RtPrint(DBG_SNDREQ, ("IpxRouter: CreateRipNdisPkt pktp=0x%x\n", pktp)); + + return pktp; +} + +//*** +// +// Function: DestroyRipNdisPkt +// +// Descr: Frees the allocated ndis structures used in sending this packet +// +// Returns: ptr to the snd buffer used in this packets +// +//*** + +PRIP_SNDPKT_BUFF +DestroyRipNdisPkt(PPACKET_TAG pktp) +{ + PRIP_SNDPKT_BUFF rbp; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + PNICCB niccbp; + + RtPrint(DBG_SNDREQ, ("IpxRouter: DestroyRipNdisPkt pktp=0x%x\n", pktp)); + + niccbp = pktp->PacketOwnerNicCbp; + + // get a ptr to the send buff structure + rbp = CONTAINING_RECORD(pktp->DataBufferp, RIP_SNDPKT_BUFF, IpxPacket); + + NdisPacket = CONTAINING_RECORD(pktp, NDIS_PACKET, ProtocolReserved); + + // free the data buffer descr + NdisUnchainBufferAtBack (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + else + { + // !!! break + DbgBreakPoint(); + } + + // free the mac hdr buff descr + NdisFreeBuffer(pktp->HeaderBuffDescrp); + + NdisFreePacket(NdisPacket); + + NdisFreePacketPool(rbp->PktDescrPoolHandle); + NdisFreeBufferPool(rbp->BuffDescrPoolHandle); + + return rbp; +} + + +//*** +// +// Function: StartInterPktGapTimer +// +// Descr: Starts the timer for 55 ms at this Nic Cb +// +// Params: pointer to Nic Cb +// +// Returns: none +// +//*** + +VOID +StartInterPktGapTimer(PNICCB niccbp) +{ + LARGE_INTEGER timeout; + + timeout.LowPart = (ULONG)(-55 * 10000L); // 55 ms + timeout.HighPart = -1; + + KeSetTimer(&niccbp->InterPktGapTimer, timeout, &niccbp->InterPktGapDpc); +} + + + +VOID +SetRipIpxHeader(PUCHAR hdrp, // pointer to the packet header + PNICCB niccbp, // pointer to the rip send request + USHORT RipOpcode) +{ + PUTUSHORT2SHORT(hdrp + IPXH_CHECKSUM, 0xFFFF); + *(hdrp + IPXH_XPORTCTL) = 0; + *(hdrp + IPXH_PKTTYPE) = 1; // RIP packet + memcpy(hdrp + IPXH_DESTNET, niccbp->Network, IPX_NET_LEN); + memcpy(hdrp + IPXH_DESTNODE, niccbp->RipSndReqp->DestNode, IPX_NODE_LEN); + PUTUSHORT2SHORT(hdrp + IPXH_DESTSOCK, niccbp->RipSndReqp->DestSock); + memcpy(hdrp + IPXH_SRCNET, niccbp->Network, IPX_NET_LEN); + memcpy(hdrp + IPXH_SRCNODE, niccbp->Node, IPX_NODE_LEN); + PUTUSHORT2SHORT(hdrp + IPXH_SRCSOCK, IPX_RIP_SOCKET); + + // set the opcode + PUTUSHORT2SHORT(hdrp + RIP_OPCODE, RipOpcode); +} + +VOID +SetRipRemoteAddress(PPACKET_TAG pktp, + PNICCB niccbp) +{ + // set up the remote address in the packet to send + pktp->RemoteAddress.NicId = niccbp->NicId; + if(niccbp->DeviceType != NdisMediumWan) { + + // we send on a LAN + memcpy(pktp->RemoteAddress.MacAddress, niccbp->RipSndReqp->DestNode, IPX_NODE_LEN); + } + else + { + // we send on a WAN line. If the destination socket is broadcast, replace + // it with the address of the remote node + if(!memcmp(niccbp->RipSndReqp->DestNode, bcastaddress, IPX_NODE_LEN)) { + + memcpy(pktp->RemoteAddress.MacAddress, niccbp->RemoteNode, IPX_NODE_LEN); + } + else + { + memcpy(pktp->RemoteAddress.MacAddress, niccbp->RipSndReqp->DestNode, IPX_NODE_LEN); + } + } +} diff --git a/private/ntos/tdi/isn/rip/riptimer.c b/private/ntos/tdi/isn/rip/riptimer.c new file mode 100644 index 000000000..dd9e0d39c --- /dev/null +++ b/private/ntos/tdi/isn/rip/riptimer.c @@ -0,0 +1,221 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: riptimer.c +// +// Description: rip timer manager. Does periodic bcast and aging +// +// Author: Stefan Solomon (stefans) October 18, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +WORK_QUEUE_ITEM RipTimerWorkItem; + +UINT RipTimerCount; + +NDIS_SPIN_LOCK StopRipTimerLock; +BOOLEAN RipTimerStopRequested; +BOOLEAN RipTimerWorkItemPending; +KEVENT RipTimerWorkItemCompletedEvent; + +VOID +AgeAndBcastRoutes(PVOID Parameter); + +VOID +InitRipTimer(VOID) +{ + RipTimerCount = RIP_TIMEOUT; // 60 sec aging time + ExInitializeWorkItem(&RipTimerWorkItem, AgeAndBcastRoutes, NULL); + KeInitializeEvent(&RipTimerWorkItemCompletedEvent, NotificationEvent, FALSE); + INITIALIZE_SPIN_LOCK(&StopRipTimerLock); + RipTimerStopRequested = FALSE; + RipTimerWorkItemPending = FALSE; +} + +VOID +RipTimer(VOID) +{ + if(--RipTimerCount) { + + return; + } + + RipTimerCount = RIP_TIMEOUT; + + // timer has expired + // check if stop timer has been requested at this point + ACQUIRE_SPIN_LOCK(&StopRipTimerLock); + + if(RipTimerStopRequested) { + + // return with no further action + RELEASE_SPIN_LOCK(&StopRipTimerLock); + return; + } + + // Check if the work item is pending. We don't want to queue it twice + if(RipTimerWorkItemPending) { + + // return with no further action + RELEASE_SPIN_LOCK(&StopRipTimerLock); + return; + } + + // mark that we have queued the work item + RipTimerWorkItemPending = TRUE; + + RELEASE_SPIN_LOCK(&StopRipTimerLock); + + ExQueueWorkItem(&RipTimerWorkItem, DelayedWorkQueue); +} + +VOID +AgeAndBcastRoutes(PVOID Parameter) +{ + LIST_ENTRY DownRoutesList; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + PNICCB niccbp; + UINT seg; + BOOLEAN FirstRoute; + PRIP_SNDREQ sndreqp; + USHORT NicId; + + RtPrint(DBG_RIPTIMER, ("IpxRouter: AgeAndBcastRoutes: Entered\n")); + + InitializeListHead(&DownRoutesList); + + // Scan the routing table and decrement the timer for each route entry. + // If the timer == 0, remove the route entry. + // All removed route entries are recorded into an update rip packet. + // At the end, broadcast the update + + for(seg=0; segFlags & IPX_ROUTER_PERMANENT_ENTRY) || + (rtep->Flags & IPX_ROUTER_LOCAL_NET)) { + + // local route, skip it + continue; + } + + // check if this is a route accesible via a WAN nic. + // These are static routes and are never aged. + // They are deleted only when the line goes down. + niccbp = NicCbPtrTab[rtep->NicId]; + if(niccbp->DeviceType == NdisMediumWan) { + + // static WAN route, skip it + continue; + } + + // non local route and non static WAN route, age it + if(++rtep->Timer >= 3) { + + // this route is too old, delete it + IpxDeleteRoute(seg, rtep); + + // mark it as down + rtep->HopCount = 16; + + // add the route to the packets we prepare for bcast + AddRouteToBcastSndReq(&DownRoutesList, rtep); + + // finally, free the route entry + ExFreePool(rtep); + } + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + } // for all segments + + // broadcast all the deleted routes + while((sndreqp = GetBcastSndReq(&DownRoutesList, &NicId)) != NULL) { + + // get the nic ptr for this snd req + niccbp = NicCbPtrTab[NicId]; + + BroadcastRipUpdate(sndreqp, niccbp, NULL); + } + + // Finally, dispatch a periodic bcast request to all nics + if((sndreqp = ExAllocatePool(NonPagedPool, sizeof(RIP_SNDREQ))) != NULL) { + + BroadcastRipGeneralResponse(sndreqp); + } + + // If the router has a client WAN nic, request update on WAN from the + // remote server router + SendGenRequestOnWanClient(); + + // check if stop was requested and our completion is waited + ACQUIRE_SPIN_LOCK(&StopRipTimerLock); + + // mark that we have terminated + RipTimerWorkItemPending = FALSE; + + if(!RipTimerStopRequested) { + + RELEASE_SPIN_LOCK(&StopRipTimerLock); + return; + } + + RELEASE_SPIN_LOCK(&StopRipTimerLock); + + RtPrint(DBG_UNLOAD, ("IpxRouter: AgeAndBcastRoutes: signal completion to pending stop timer\n")); + + // signal that we are done + KeSetEvent(&RipTimerWorkItemCompletedEvent, 0L, FALSE); +} + + +VOID +StopRipTimer() +{ + KeResetEvent(&RipTimerWorkItemCompletedEvent); + + // check if the work item has been queued + ACQUIRE_SPIN_LOCK(&StopRipTimerLock); + + // mark that we request stopping + RipTimerStopRequested = TRUE; + + if(!RipTimerWorkItemPending) { + + RELEASE_SPIN_LOCK(&StopRipTimerLock); + + RtPrint(DBG_UNLOAD, ("IpxRouter: StopRipTimer: Completed immediately\n")); + return; + } + + RELEASE_SPIN_LOCK(&StopRipTimerLock); + + RtPrint(DBG_UNLOAD, ("IpxRouter: StopRipTimer: Waiting for the work item to complete\n")); + + KeWaitForSingleObject( + &RipTimerWorkItemCompletedEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL + ); +} diff --git a/private/ntos/tdi/isn/rip/route.c b/private/ntos/tdi/isn/rip/route.c new file mode 100644 index 000000000..82e44016e --- /dev/null +++ b/private/ntos/tdi/isn/rip/route.c @@ -0,0 +1,179 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: route.c +// +// Description: route packet routine +// +// Author: Stefan Solomon (stefans) October 11, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +//*** +// +// Function: RoutePacket +// +// Descr: Routes this packet +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +RoutePacket(PPACKET_TAG pktp) +{ + PUCHAR hdrp; // points to the IPX packet header + PUCHAR destnet; // points to destination network + UINT segment; // routing table segment + KIRQL oldirql; + PIPX_ROUTE_ENTRY tabrtep; + PNICCB dstniccbp, srcniccbp; // dest and src nic cb pointers + + // check if we know about the destination network + hdrp = pktp->DataBufferp; + destnet = hdrp + IPXH_DESTNET; + + segment = IpxGetSegment(destnet); + + ExAcquireSpinLock(&SegmentLocksTable[segment], &oldirql); + + if((tabrtep = IpxGetRoute(segment, destnet)) == NULL) { + + // no such route + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: route not found!\n")); + FreeRcvPkt(pktp); + return; + } + + // check if the destination route is the Global Wan Route. If it is, then get the + // nic id of the destination nic + if(tabrtep->Flags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // sanity check + ASSERT(tabrtep->NicId == 0xFFFE); + ASSERT(WanGlobalNetworkEnabled); + + // get the destination nic id from the wan nodes hash table + dstniccbp = GetWanNodeNiccbp(hdrp + IPXH_DESTNODE); + + // check if the nic hasn't been removed from the table (by RtLineDown) + if(dstniccbp == NULL) { + + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + FreeRcvPkt(pktp); + return; + } + else + { + // extra sanity checks + ASSERT(dstniccbp->DeviceType == NdisMediumWan); + ASSERT(!dstniccbp->WanConnectionClient); + } + } + else + { + dstniccbp = NicCbPtrTab[tabrtep->NicId]; + } + + // check if the destination route is reachable through another nic than + // the nic we received the packet from. + if(pktp->PacketOwnerNicCbp->NicId == dstniccbp->NicId) { + + // we can't send the packet back where it came from => discard it + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + FreeRcvPkt(pktp); + return; + } + + // if either the src or the dst of the packet is a WAN nic, there are + // a series of checks to perform + + // get the source nic for this packet + srcniccbp = NicCbPtrTab[pktp->PacketOwnerNicCbp->NicId]; + + // 1. check if one of the two nics is WAN and the other is a LAN nic disabled + // for WAN traffic + if( ((dstniccbp->DeviceType == NdisMediumWan) && + (srcniccbp->DeviceType != NdisMediumWan) && + (srcniccbp->WanRoutingDisabled)) || + + ((dstniccbp->DeviceType != NdisMediumWan) && + (srcniccbp->DeviceType == NdisMediumWan) && + (dstniccbp->WanRoutingDisabled)) ) { + + RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: discard pkt because WAN traffic disabled for this LAN\n")); + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + FreeRcvPkt(pktp); + return; + } + + // 2. check if one of the two nics is WAN and has a client role and + // LAN-WAN-LAN traffic is disabled + if(!LanWanLan) { + + if( ((dstniccbp->DeviceType == NdisMediumWan) && + (dstniccbp->WanConnectionClient)) || + + ((srcniccbp->DeviceType == NdisMediumWan) && + (srcniccbp->WanConnectionClient)) ) { + + RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: discard pkt because WAN has client role\n")); + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + FreeRcvPkt(pktp); + return; + } + } + + // 3. If both source and destination NICs are LAN, check if LAN routing is + // enabled. + if((dstniccbp->DeviceType != NdisMediumWan) && + (srcniccbp->DeviceType != NdisMediumWan) && + (!EnableLanRouting)) { + + RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: discard pkt because LAN routing disabled\n")); + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + FreeRcvPkt(pktp); + return; + } + + // general send preparation + pktp->RemoteAddress.NicId = dstniccbp->NicId; + + //*** check if the network is directly connected *** + + if(tabrtep->Flags & IPX_ROUTER_LOCAL_NET) { + + // prepare the packet for a direct send to the destination node + memcpy(&pktp->RemoteAddress.MacAddress, + hdrp + IPXH_DESTNODE, + IPX_NODE_LEN); + } + else + { + // prepare the packet to be sent to the next router in the path to + // the destination node + memcpy(&pktp->RemoteAddress.MacAddress, + tabrtep->NextRouter, + IPX_NODE_LEN); + } + + ExReleaseSpinLock(&SegmentLocksTable[segment], oldirql); + + // increment the nr of hops in the packet + *(hdrp + IPXH_XPORTCTL) += 1; + + // queue the packet at the sending nic and send it + SendPacket(pktp); + + return; +} diff --git a/private/ntos/tdi/isn/rip/rtdefs.h b/private/ntos/tdi/isn/rip/rtdefs.h new file mode 100644 index 000000000..a19457954 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rtdefs.h @@ -0,0 +1,327 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: rtdefs.h +// +// Description: Defines private structures and types for the ipx router +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + + + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include +#include +#include +#include +#include + +#include "debug.h" +#include "packet.h" +#include "utils.h" + +#ifndef _RTDEFS_ +#define _RTDEFS_ + +// +//*** NIC Control Block *** +// + +typedef enum _NIC_STATE { + + NIC_CLOSED, // line disconnected for this Nic and clean up done + NIC_CLOSING, // line down received and in the process of cleaning up + NIC_ACTIVE, // Nic active + NIC_PENDING_OPEN + } NIC_STATE; + +typedef enum _NIC_CLOSE_STATUS { // returned by NicClose + + NIC_CLOSE_SUCCESS, + NIC_CLOSE_FAILURE, + NIC_CLOSE_PENDING + } NIC_CLOSE_STATUS; + +typedef enum _NIC_RESOURCES_STATUS { // returned by NicFreeResources + + NIC_RESOURCES_FREED, + NIC_RESOURCES_PENDING + } NIC_RESOURCES_STATUS; + +typedef enum _NIC_OPEN_STATUS { // returned by NicOpen + + NIC_OPEN_SUCCESS, + NIC_OPEN_FAILURE, + } NIC_OPEN_STATUS; + +struct _RIP_SNDREQ; + +// close completion options +#define SIGNAL_CLOSE_COMPLETION_EVENT 0x00001 +#define CALL_CLOSE_COMPLETION_ROUTINE 0x00002 + +typedef struct _NICCB { + + LIST_ENTRY SendQueue; // queue of packets being sent + LIST_ENTRY ReceiveQueue; // queue of packets being received + LIST_ENTRY RipSendQueue; // RIP responses to send on this nic + struct _RIP_SNDREQ *RipSndReqp; // RIP request presently being sent + LIST_ENTRY RipSndPktsList; + NDIS_SPIN_LOCK NicLock; + NIC_STATE NicState; // NIC_ACTIVE, NIC_CLOSING, NIC_CLOSED + USHORT NicId; + USHORT CloseCompletionOptions; + UCHAR Network[4]; + UCHAR Node[6]; // local node address + UCHAR RemoteNode[6]; // address of remote node, for WAN links + BOOLEAN WanRoutingDisabled; + BOOLEAN WanConnectionClient; + USHORT TickCount; // this is derived from link speed + UINT LinkSpeed; + UINT MaximumPacketSize; + UINT MacOptions; + NDIS_MEDIUM DeviceType; + WORK_QUEUE_ITEM RipSndReqWorkItem; + KTIMER InterPktGapTimer; + KDPC InterPktGapDpc; + KTIMER NicCloseTimer; + KDPC NicCloseDpc; + KEVENT NicClosedEvent; + KTIMER WanGenRequestTimer; + KDPC WanGenRequestDpc; + UINT WanGenRequestCount; + LIST_ENTRY WanHtLinkage; // linkage in the WAN nodes hash table + + //*** count of send packets queued for send on this nic *** + + ULONG SendPktsQueuedCount; + + //*** statistics kept here *** + + ULONG StatBadReceived; + ULONG StatRipReceived; + ULONG StatRipSent; + ULONG StatRoutedReceived; + ULONG StatRoutedSent; + ULONG StatType20Received; + ULONG StatType20Sent; + + } NICCB, *PNICCB; + +// +//*** Rcv Packet Pool Segment Entry *** +// + +typedef struct _RCVPKT_SEGMENT { + + LIST_ENTRY SegmentLinkage; // linkage in the segment list + LIST_ENTRY PacketList; // list of packets + UINT MaxPktCount; // total nr of pkts in this segment + UINT AvailablePktCount; // available pkts in this segment + UINT AgingTimer; // incremented by the scavenger + // segment removed when 3 ticks + + NDIS_HANDLE RcvPktDescrPoolHandle; // Rcv packets descr pool + ULONG RcvPktDescrPoolSize; + + NDIS_HANDLE RcvPktBuffDescrPoolHandle;// Rcv buffer descriptors pool + ULONG RcvPktBuffDescrPoolSize; + + ULONG DataBuffer[1]; + } RCVPKT_SEGMENT, *PRCVPKT_SEGMENT; + + +// +//*** Packet Tag Structure *** +// + +typedef enum _PACKET_TYPE { + + RCV_PACKET, // Used for RIP requests/ specific replies and routing. + // These packets are allocated from the rcv pools and + // charged to the Nic which received them + + RIP_SEND_PACKET, // packets used for the send rip response/request + + PROPAGATED_BCAST_PACKET // used for Netbios bcasts. These packets are taken + // from the rcv pkt pool + } PACKET_TYPE; + +typedef struct _PACKET_TAG { + + UCHAR Identifier; // this should be IDENTIFIER_RIP + + UCHAR ReservedUchar[3]; // ensure alignment of ReservedPvoid + PVOID ReservedPvoid[2]; // needed by ipx for padding on ethernet + + LIST_ENTRY PacketLinkage; // links this packet in send/receive queues + // and the rcv pkt pool queue + PACKET_TYPE PacketType; // see above + + PRCVPKT_SEGMENT RcvPktSegmentp; // for RCV_PACKET types, the rcv pkt + // pool segment where it belongs + + PUCHAR DataBufferp; // data buffer + UINT DataBufferLength; // original length of the data buffer + + PNICCB QueueOwnerNicCbp; // points to the NicCb where it is queued + PNICCB PacketOwnerNicCbp; // ptr to NicCb charged for this packet + + IPX_LOCAL_TARGET RemoteAddress; // remote address to send this packet + // on (used by send routines) + + PNDIS_BUFFER HeaderBuffDescrp; // ptr to buff descr for the MAC header + // NULL if the buff descr is chained in + // the pkt descr (after send completes) + UCHAR MacHeader[40]; // 40 bytes of Mac Header for sends + } PACKET_TAG, *PPACKET_TAG; + +// +//*** RIP Send Requests *** +// + +// for each rip send packet, there is a structure used to keep the pkt descr +// pool, buff descr pool and data buffer. + +typedef struct _RIP_SNDPKT_BUFF { + + NDIS_HANDLE PktDescrPoolHandle; // Rcv packets descr pool + ULONG PktDescrPoolSize; + + NDIS_HANDLE BuffDescrPoolHandle;// Rcv buffer descriptors pool + ULONG BuffDescrPoolSize; + + ULONG IpxPacket[8]; // 32 -> ipx rip header + } RIP_SNDPKT_BUFF, *PRIP_SNDPKT_BUFF; + +#define RIP_SNDPKT_MAXLEN 400 // max length to be added to ipx rip header +#define RIP_SNDPKT_MINLEN 8 // one network entry size + +// each rip send request is made of a control block set up by the module +// requesting the send (rip response, timer, etc.) + +typedef enum _RIP_SNDREQ_ID { + + RIP_GEN_RESPONSE, // Send the whole routing table as viewed from this nic + + RIP_UPDATE, // Bcast the associated list of changes + + RIP_GEN_REQUEST // bcast this request on all LANs. This is sent when the + // router starts and requests info about all the other + // routers. + + } RIP_SNDREQ_ID; + + + +typedef struct _RIP_SNDREQ { + + LIST_ENTRY GlobalLinkage; // linkage in the Rip global dispatch queue. + // the request stays in this queue until sent on + // all nics + LIST_ENTRY NicLinkage; // linkage in the Rip send request queue at + // the Nic Cb + RIP_SNDREQ_ID SndReqId; // rip send request type + BOOLEAN SendOnAllNics; // how to dispatch this request: + // TRUE - send on all nics, FALSE - send only + // on the sender nic cb + UCHAR DestNode[6]; // destination node: + // if different of bcast address represents + // the address to send this request to + USHORT DestSock; // destination socket to receive the resp + PNICCB DoNotSendNicCbp; // do not send on this nic + PNICCB SenderNicCbp; // ptr to the nic on which this is sent + PKEVENT SndCompleteEventp; // ptr to send complete event + } RIP_SNDREQ, *PRIP_SNDREQ; + +typedef struct _RIP_UPDATE_SNDREQ { + + RIP_SNDREQ RipSndReq; + RIP_SNDPKT_BUFF RipSndPktBuff; +} RIP_UPDATE_SNDREQ, *PRIP_UPDATE_SNDREQ; + + +// +//*** Miscellaneous Global Defs & Constants *** +// + +#include "globals.h" + +// default configuration values + +// default maximum frame size +#define DEF_MAX_FRAME_SIZE 1518 + +// default RIP bcast frame size (IPX header + 50 network entries) +#define DEF_BCAST_FRAME_SIZE 432 + +// define rcv pkt pool sizes + +// small pool -> 100 pkts / per nic +#define RCVPKT_SMALL_POOL_SIZE 1 + +// medium pool -> 250 pkts per nic +#define RCVPKT_MEDIUM_POOL_SIZE 2 + +// large pool -> unlimited nr of pkts per nic +#define RCVPKT_LARGE_POOL_SIZE 3 + +// the number of rcv packets per segment (config parameter) +#define MIN_RCV_PKTS_PER_SEGMENT 2 +#define DEF_RCV_PKTS_PER_SEGMENT 16 +#define MAX_RCV_PKTS_PER_SEGMENT 32 + +// the default timeout interval for rip bcasts and aging +#define RIP_TIMEOUT 60 + +// the default limit on the number of send pkts queued on one nic +#define MAX_SEND_PKTS_QUEUED 100 + +// +//*** Definitions for the WAN nodes hash table used in routing packets LAN -> WAN for the +//*** case when the router is configured with a unique WAN network number +// + +#define NODE_HTSIZE 37 + +//*** tick count associated with the wan global net. Because that's a global value, +//*** it was picked to reflect an average on an async net +#define DEFAULT_WAN_GLOBAL_NET_TICKCOUNT 20 + +//*** invalid device type -> to distinguish between LAN, WAN and non configured devices + +#define IPX_ROUTER_INVALID_DEVICE_TYPE 0xFFFF + +// +//*** Definitions for the netbios routing flags in the NetbiosRouting parameter *** +// + +#define NETBIOS_ROUTING_LAN_TO_LAN 0x00000001 +#define NETBIOS_ROUTING_WAN_TO_LAN 0x00000002 +#define NETBIOS_ROUTING_LAN_TO_WAN 0x00000004 + +// +// Allocate Pool With Tag +// + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag((type), (size), 'XPIR') + +#endif // _RTDEFS_ diff --git a/private/ntos/tdi/isn/rip/rttest/makefile b/private/ntos/tdi/isn/rip/rttest/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/rip/rttest/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/rip/rttest/nwsap.h b/private/ntos/tdi/isn/rip/rttest/nwsap.h new file mode 100644 index 000000000..c99a84063 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rttest/nwsap.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation +Copyright (c) 1993 Micro Computer Systems, Inc. + +Module Name: + + net\inc\nwsap.h + +Abstract: + + This is the public include file for the Nw Sap Agent API. + +Author: + + Brian Walker (MCS) 06-30-1993 + +Revision History: + +--*/ + +#ifndef _NWSAP_ +#define _NWSAP_ + +/** Return codes for Advertise API and BindLib API **/ + +#define SAPRETURN_SUCCESS 0 +#define SAPRETURN_NOMEMORY 1 +#define SAPRETURN_EXISTS 2 +#define SAPRETURN_NOTEXIST 3 +#define SAPRETURN_NOTINIT 4 +#define SAPRETURN_INVALIDNAME 5 + +/** Function Prototypes **/ + +INT +SapAddAdvertise( + IN PUCHAR ServerName, + IN USHORT ServerType, + IN PUCHAR ServerAddr); + +INT +SapRemoveAdvertise( + IN PUCHAR ServerName, + IN USHORT ServerType); + +DWORD +SapLibInit( + VOID); + +DWORD +SapLibShutdown( + VOID); + +INT +SapGetObjectID( + IN PUCHAR ObjectName, + IN USHORT ObjectType, + IN PULONG ObjectID); + +INT +SapGetObjectName( + IN ULONG ObjectID, + IN PUCHAR ObjectName, + IN PUSHORT ObjectType, + IN PUCHAR ObjectAddr); + +INT +SapScanObject( + IN PULONG ObjectID, + IN PUCHAR ObjectName, + IN PUSHORT ObjectType, + IN USHORT ScanType); + +#endif diff --git a/private/ntos/tdi/isn/rip/rttest/rttest.c b/private/ntos/tdi/isn/rip/rttest/rttest.c new file mode 100644 index 000000000..941e93511 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rttest/rttest.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "..\driver.h" +#include "utils.h" +#include "nwsap.h" + +typedef struct _IPX_ROUTE_ENTRY { + UCHAR Network[4]; + USHORT NicId; + UCHAR NextRouter[6]; + PVOID NdisBindingContext; + USHORT Flags; + USHORT Timer; + UINT Segment; + USHORT TickCount; + USHORT HopCount; + PVOID AlternateRoute[2]; + PVOID NicLinkage[2]; + struct { + PVOID Linkage[2]; + ULONG Reserved[1]; + } PRIVATE; +} IPX_ROUTE_ENTRY, * PIPX_ROUTE_ENTRY; + + + +INT +IsDigit(UCHAR *string) { + if (*string < '0' || *string >'9') { + return(0); + } + return(1); +} + + +IPX_ROUTE_ENTRY rte; + +VOID +RouterDisplayTable( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock) { + + ULONG netnr; + NTSTATUS Status; + + printf("<------------ ROUTING TABLE START ------------->\n\n"); + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SNAPROUTES, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + if (IoStatusBlock->Status != STATUS_SUCCESS) { + + printf("Ioctl snap routes failed\n"); + return; + } + + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_GETNEXTROUTE, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + &rte, // Output Buffer + sizeof(IPX_ROUTE_ENTRY)); // Output Buffer Length + + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + + printf("\n\n<------------ ROUTING TABLE END ------------->\n\n"); + return; + } + + + if(Status != STATUS_SUCCESS) { + + printf("Ioctl failure\n"); + return; + } + + // get net nr in "on the wire" order + + GETLONG2ULONG(&netnr, rte.Network); + + printf("<-- net=%.8x, nic=%d, hops=%d, ticks=%d, flags=0x%x -->\n", + netnr, rte.NicId, rte.HopCount, rte.TickCount, rte.Flags); + } +} + +PUCHAR DeviceType[2] = { "LAN", "WAN" }; +PUCHAR NicState[4] = { "CLOSED", "CLOSING", "ACTIVE", "PENDING_OPEN" }; + + +VOID +RouterShowNicInfo( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock) { + + SHOW_NIC_INFO nis; + USHORT index, i; + + NTSTATUS Status; + + printf("\n"); + index = 0; + + while(TRUE) { + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SHOWNICINFO, // IoControlCode + &index, // Input Buffer + sizeof(USHORT), // Input Buffer Length + &nis, // Output Buffer + sizeof(nis)); // Output Buffer Length + + index++; + printf("\n"); + + if(IoStatusBlock->Status == STATUS_NO_MORE_ENTRIES) { + + return; + } + + + if(Status != STATUS_SUCCESS) { + + printf("Ioctl failure\n"); + return; + } + + printf("NicId = %d\n", nis.NicId); + printf("DeviceType = %s\n", DeviceType[nis.DeviceType]); + printf("NicState = %s\n", NicState[nis.NicState]); + printf("Network = "); + for(i=0; i<4; i++) { + + printf("%.2x", nis.Network[i]); + } + printf("\nNode = "); + for(i=0; i<6; i++) { + + printf("%.2x", nis.Node[i]); + } + printf("\nTickCount = %d\n", nis.TickCount); + + printf("RIP Packets Traffic: Received = %d, Sent = %d\n", + nis.StatRipReceived, nis.StatRipSent); + + printf("Routed Packets Traffic: Received = %d, Sent = %d\n", + nis.StatRoutedReceived, nis.StatRoutedSent); + + printf("Type 20 Packets Traffic: Received = %d, Sent = %d\n", + nis.StatType20Received, nis.StatType20Sent); + + printf("Bad Packets Received = %d\n", nis.StatBadReceived); + + } +} + +VOID +RouterShowMemStat( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock) { + + SHOW_MEM_STAT sms; + + NTSTATUS Status; + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_SHOWMEMSTATISTICS, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + &sms, // Output Buffer + sizeof(sms)); // Output Buffer Length + + if(Status != STATUS_SUCCESS) { + + printf("Ioctl failure\n"); + return; + } + + printf("\n\nPeak receive packets allocation: %d pkts\n", sms.PeakPktAllocCount); + printf("Current receive packets allocation: %d pkts\n", sms.CurrentPktAllocCount); + printf("Current packet pool size (alloc'ed + free) %d pkts , %dk\n", + sms.CurrentPktPoolCount, (sms.CurrentPktPoolCount * sms.PacketSize)/1024 + 1); + printf("Packet Size %d\n", sms.PacketSize); +} + + +VOID +RouterClearStatistics( + PHANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock) { + + NTSTATUS Status; + + Status = NtDeviceIoControlFile( + *FileHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPXROUTER_ZERONICSTATISTICS, // IoControlCode + NULL, // Input Buffer + 0, // Input Buffer Length + NULL, // Output Buffer + 0); // Output Buffer Length + + if(Status != STATUS_SUCCESS) { + + printf("Ioctl failure\n"); + return; + } +} + +VOID +SapShowAllServers() +{ + INT rc; + ULONG ObjectID = 0xFFFFFFFF; + UCHAR ObjectName[100]; + USHORT ObjectType; + USHORT ScanType = 0xFFFF; + + memset(&ObjectName, 0, 100); + + while((rc = SapScanObject(&ObjectID, + ObjectName, + &ObjectType, + ScanType)) == SAPRETURN_SUCCESS) { + + printf("%-48s ServerType=%d\n", &ObjectName, ObjectType); + } +} + +VOID +SapShowFileServers() +{ + INT rc; + ULONG ObjectID = 0xFFFFFFFF; + UCHAR ObjectName[100]; + USHORT ObjectType; + USHORT ScanType = 0x4; + UCHAR IpxAddress[12]; + USHORT i; + + memset(&ObjectName, 0, 100); + + printf("\nServer Name IPX Address\n"); + printf("-------------------------------------------\n"); + + printf("\n"); + while((rc = SapScanObject(&ObjectID, + ObjectName, + &ObjectType, + ScanType)) == SAPRETURN_SUCCESS) { + + // get object address + SapGetObjectName(ObjectID, + ObjectName, + &ObjectType, + IpxAddress); + + + printf("%-30s", &ObjectName); + for(i=0; i<4; i++) { + + printf("%.2x", IpxAddress[i]); + } + printf("."); + for(i=4; i<10; i++) { + + printf("%.2x", IpxAddress[i]); + } + printf("\n"); + } +} + +VOID _cdecl +main( + IN WORD argc, + IN LPSTR argv[] + ) + +{ + HANDLE RouterFileHandle; + OBJECT_ATTRIBUTES RouterObjectAttributes; + IO_STATUS_BLOCK RouterIoStatusBlock; + UNICODE_STRING RouterFileString; + WCHAR RouterFileName[] = L"\\Device\\Ipxroute"; + NTSTATUS Status; + + PVOID Memory; + int choice; + + RtlInitUnicodeString (&RouterFileString, RouterFileName); + + InitializeObjectAttributes( + &RouterObjectAttributes, + &RouterFileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &RouterFileHandle, // HANDLE of file + SYNCHRONIZE | GENERIC_READ, + &RouterObjectAttributes, + &RouterIoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, // Share Access + FILE_SYNCHRONOUS_IO_NONALERT); // Open Options + + if (!NT_SUCCESS(Status)) { + printf("Open of IPX Router returned %lx\n", Status); + return; + } + else + { + printf("Open of %Z was successful!\n",&RouterFileString); + } + + // + // Allocate storage to hold all this. + // + + Memory = malloc (200 * sizeof(ULONG)); + if (Memory == NULL) { + printf("Malloc failed.\n"); + return; + } + + SapLibInit(); + + + do { + printf("\n"); + printf("----------- IPX ROUTER TEST MENU -------------\n"); + printf("\n"); + printf(" 1. Show Routing Table\n"); + printf(" 2. Show Network Interfaces Statistics\n"); + printf(" 3. Show Packet Pool Statistics\n"); + printf(" 4. Clear Router Statistics\n"); + printf(" 5. Show All Servers (of all types) in SAP Table\n"); + printf(" 6. Show NetWare File Servers in SAP Table\n"); + printf(" 99.Exit\n"); + printf("\n"); + printf("Enter your choice -->"); + + scanf("%d", &choice); + + switch (choice) { + case 1: + RouterDisplayTable(&RouterFileHandle, &RouterIoStatusBlock); + break; + case 2: + RouterShowNicInfo(&RouterFileHandle, &RouterIoStatusBlock); + break; + case 3: + RouterShowMemStat(&RouterFileHandle, &RouterIoStatusBlock); + break; + case 4: + RouterClearStatistics(&RouterFileHandle, &RouterIoStatusBlock); + break; + case 5: + SapShowAllServers(); + break; + case 6: + SapShowFileServers(); + break; + + + case 99: + break; + default: + printf("Bad choice !!\n"); + } + + } while (choice != 99); + + + Status = NtClose(RouterFileHandle); + + if (!NT_SUCCESS(Status)) { + printf("Router Close returned %lx\n", Status); + } else { + printf("Router Close successful\n"); + } + + free (Memory); + +} + + diff --git a/private/ntos/tdi/isn/rip/rttest/sources b/private/ntos/tdi/isn/rip/rttest/sources new file mode 100644 index 000000000..fc42df2d6 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rttest/sources @@ -0,0 +1,42 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +UMLIBS=obj\*\hubtest.lib \nt\public\sdk\lib\*\setargv.obj +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ndis +MINORCOMP=rttest + +TARGETNAME=rttest +TARGETPATH=obj +TARGETTYPE=PROGRAM + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\nwsaplib.lib + +INCLUDES= + +SOURCES=rttest.c + + +UMTYPE=console +UMLIBS= + diff --git a/private/ntos/tdi/isn/rip/rttest/utils.h b/private/ntos/tdi/isn/rip/rttest/utils.h new file mode 100644 index 000000000..afacd2a35 --- /dev/null +++ b/private/ntos/tdi/isn/rip/rttest/utils.h @@ -0,0 +1,55 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: utils.h +// +// Description: Contains miscellaneous utilities +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _UTILS_ +#define _UTILS_ + +/* + * The following macros deal with on-the-wire short and long values + * + * On the wire format is big-endian i.e. a long value of 0x01020304 is + * represented as 01 02 03 04. + * Similarly a short value of 0x0102 is represented as 01 02. + * + * The host format is not assumed since it will vary from processor to + * processor. + */ + +// Get a short from on-the-wire format to a USHORT in the host format +#define GETSHORT2USHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 8) + \ + (*((PUCHAR)(SrcPtr)+1) )) + +// Get a long from on-the-wire format to a ULONG in the host format +#define GETLONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 24) + \ + (*((PUCHAR)(SrcPtr)+1) << 16) + \ + (*((PUCHAR)(SrcPtr)+2) << 8) + \ + (*((PUCHAR)(SrcPtr)+3) )) + +// Put a USHORT from the host format to a short to on-the-wire format +#define PUTUSHORT2SHORT(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((USHORT)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR)(Src) + +// Put a ULONG from the host format to an array of 4 UCHARs on-the-wire format +#define PUTULONG2LONG(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((ULONG)(Src) >> 24), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR) ((ULONG)(Src) >> 16), \ + *((PUCHAR)(DstPtr)+2) = (UCHAR) ((ULONG)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+3) = (UCHAR) (Src) + +#endif // _UTILS_ diff --git a/private/ntos/tdi/isn/rip/send.c b/private/ntos/tdi/isn/rip/send.c new file mode 100644 index 000000000..22810fe6c --- /dev/null +++ b/private/ntos/tdi/isn/rip/send.c @@ -0,0 +1,524 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: send.c +// +// Description: send packet routines +// +// Author: Stefan Solomon (stefans) October 11, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +VOID +SendPacketComplete(PPACKET_TAG pktp); + +UINT +SendPropagatedPacketComplete(PPACKET_TAG pktp); + +VOID +UpdateSendStatistics(PNICCB niccb, + PPACKET_TAG pktp); + +VOID +SetType20PktDestNet(PPACKET_TAG pktp); + +VOID +UpdateRipResponseTickCount(PUCHAR hdrp, + PNICCB niccbp); + + +//*** max send pkts queued limit: over this limit the send pkts get discarded + +ULONG MaxSendPktsQueued = MAX_SEND_PKTS_QUEUED; + +//*** +// +// Function: SendPacket +// +// Descr: enqueues the packet in the requested Nic queue and initiates +// the send +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +SendPacket(PPACKET_TAG pktp) +{ + PNDIS_PACKET pktdescrp; + NDIS_STATUS NdisStatus; + UINT BufferCount; + PNDIS_BUFFER FirstBufferp; + USHORT pktlen; + PNICCB niccbp; + PUCHAR hdrp; + + RtPrint(DBG_SEND, ("IpxRouter: SendPacket: pktp=0x%x\n", pktp)); + + // get the packet length + hdrp = pktp->DataBufferp; + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + // get the pkt descr ptr + pktdescrp = CONTAINING_RECORD(pktp, NDIS_PACKET, ProtocolReserved); + + // adjust the length of the packet to send + NdisQueryPacket(pktdescrp, + NULL, + &BufferCount, + &FirstBufferp, + NULL); + + NdisAdjustBufferLength(FirstBufferp, pktlen); + + // chain the mac hdr buff descr at the front (the mac hdr buff descr + // already points at the Mac header in the packet tag) + NdisChainBufferAtFront(pktdescrp, pktp->HeaderBuffDescrp); + + // get the Nic Cb ptr where we should send this packet + niccbp = NicCbPtrTab[pktp->RemoteAddress.NicId]; + + // Do not send if it doesn't fit the max frame size + // for this adapter + if(pktlen > niccbp->MaximumPacketSize) { + + SendPacketComplete(pktp); + return; + } + + // if the packet is a RIP Response, update the tick count in the + // network entry fields + UpdateRipResponseTickCount(hdrp, niccbp); + + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + // check Nic Status. Do not send on a closed Nic + if(niccbp->NicState != NIC_ACTIVE) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + SendPacketComplete(pktp); + return; + } + + // check if we are allowed to queue this packet (we aren't over limit) + if(niccbp->SendPktsQueuedCount >= MaxSendPktsQueued) { + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + SendPacketComplete(pktp); + return; + } + + + // Nic is active, we can send + UpdateSendStatistics(niccbp, pktp); + + // set the QUEUE owner of the packet + pktp->QueueOwnerNicCbp = niccbp; + + // enqueue the packet in the NIC's send list and wait for the + // transfer to complete + InsertTailList(&niccbp->SendQueue, &pktp->PacketLinkage); + + // increment the queued pkts counter + niccbp->SendPktsQueuedCount++; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // send the packet + NdisStatus = IpxSendPacket(&pktp->RemoteAddress, + pktdescrp, + pktlen, + 0); + + + if(NdisStatus != NDIS_STATUS_PENDING) { + + RtSendComplete(pktdescrp, NdisStatus); + } +} + +//*** +// +// Function: RtSendComplete +// +// Descr: called by the IPX driver when send completed +// +// Params: +// +// Returns: +// +//*** + +VOID +RtSendComplete(PNDIS_PACKET pktdescrp, + NDIS_STATUS NdisStatus) +{ + PNICCB niccbp; + PPACKET_TAG pktp; + + pktp = (PPACKET_TAG)pktdescrp->ProtocolReserved; + + RtPrint(DBG_SEND, ("IpxRouter: RtSendComplete: pktp=0x%x\n", pktp)); + + niccbp = pktp->QueueOwnerNicCbp; + +#if DBG + // this is for debugging purposes + if(NdisStatus != NDIS_STATUS_SUCCESS) { + + RtPrint(DBG_NOTIFY, ("IpxRouter: SendPacket: FAILED with %xl\n", NdisStatus)); + } +#endif + + // dequeue the packet from the send queue and complete processing + ACQUIRE_SPIN_LOCK(&niccbp->NicLock); + + RemoveEntryList(&pktp->PacketLinkage); + + // decrement queued send pkts counter + niccbp->SendPktsQueuedCount--; + + RELEASE_SPIN_LOCK(&niccbp->NicLock); + + // complete the send packet processing + SendPacketComplete(pktp); +} + +//*** +// +// Function: SendPacketComplete +// +// Descr: post send processing +// +// Params: Packet +// +// Returns: None +// +//*** + +VOID +SendPacketComplete(PPACKET_TAG pktp) +{ + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer = NULL; + UINT BufferCount; + + RtPrint(DBG_SEND, ("IpxRouter: SendPacketComplete: Entered\n")); + + // unchain the first buffer descriptor (MacHeader) + NdisPacket = CONTAINING_RECORD(pktp, NDIS_PACKET, ProtocolReserved); + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ASSERT(NdisBuffer == pktp->HeaderBuffDescrp); + + // readjust the original buffer descriptor length + // get the pkt descr ptr + NdisQueryPacket(NdisPacket, + NULL, + &BufferCount, + &NdisBuffer, + NULL); + + NdisAdjustBufferLength(NdisBuffer, pktp->DataBufferLength); + NdisRecalculatePacketCounts(NdisPacket); + + // what to do next is controlled by the packet type + switch(pktp->PacketType) { + + case RCV_PACKET: + + // this has been a routed packet or a directed rip reply + // free it to the rcv pkt pool and discharge the nic + + FreeRcvPkt(pktp); + + break; + + case RIP_SEND_PACKET: + + // call the completion routines for these guys + SendRipPktCompleted(pktp); + + break; + + case PROPAGATED_BCAST_PACKET: + + // send the packet on the next available net (if any left) or return it + // to the pool + if(SendPropagatedPacketComplete(pktp)) { + + RtPrint(DBG_NETBIOS, ("IpxRouter: SendPacketComplete: free propagated pkt 0x%x\n", pktp)); + + // can't propagate further, return to rcv pkt pool + FreeRcvPkt(pktp); + } + + break; + + default: + + // !!! break + ASSERT(FALSE); + break; + } +} + +//*** +// +// Function: SendPropagatedPacket +// +// Descr: marks the packet as a propagated packet type and sends it +// starting at the beginning of the LanNics list +// +// Params: Packet +// +// Returns: none +// +//*** + +VOID +SendPropagatedPacket(PPACKET_TAG pktp) +{ + USHORT i; + PNICCB niccbp; + + // check if there is an active Nic to send on next and if this Nic is + // active and is not the packet owner nic + for(i=0; iNicState == NIC_ACTIVE) && + (IsNetbiosRoutingAllowed(pktp->PacketOwnerNicCbp, niccbp)) && + (!IsNetInNbPacket(pktp, niccbp))) { + + // send the packet on this nic + pktp->PacketType = PROPAGATED_BCAST_PACKET; + pktp->RemoteAddress.NicId = niccbp->NicId; + memcpy(pktp->RemoteAddress.MacAddress, bcastaddress, 6); + + RtPrint(DBG_NETBIOS, ("IpxRouter: SendPropagatedPacket: send pkt 0x%x on Nic %d\n", pktp, niccbp->NicId)); + SetType20PktDestNet(pktp); + SendPacket(pktp); + + return; + } + } + + // can't propagate this packet, free it + FreeRcvPkt(pktp); +} + +//*** +// +// Function: SendPropagatedPacketComplete +// +// Descr: If there is a next active NicCb to send the packet on, queues +// the packet in the propagation list and queues a Dpc to send the +// packet on this Nic. +// +// Params: Packet +// +// Returns: 0 - packet queued for propagation, 1 - can't propagate further +// +//*** + +UINT +SendPropagatedPacketComplete(PPACKET_TAG pktp) +{ + PNICCB niccbp; // nic that has sent the packet + USHORT i; + + // check if there is another active Nic to send on next and if this Nic is + // active and is not the packet owner nic + for(i=pktp->RemoteAddress.NicId+1; // next Nic Id + iNicState == NIC_ACTIVE) && + (IsNetbiosRoutingAllowed(pktp->PacketOwnerNicCbp, niccbp)) && + (!IsNetInNbPacket(pktp, niccbp))) { + + // We have found the next nic. + pktp->RemoteAddress.NicId = i; + + // queue the packet for a propagated send. + ACQUIRE_SPIN_LOCK(&PropagatedPktsListLock); + + InsertTailList(&PropagatedPktsList, &pktp->PacketLinkage); + + if(!PropagatedPktsDpcQueued) { + + // queue a Dpc to send the packet + KeInsertQueueDpc(&PropagatedPktsDpc, NULL, NULL); + PropagatedPktsDpcQueued = TRUE; + } + + RELEASE_SPIN_LOCK(&PropagatedPktsListLock); + + return 0; + } + } + + // we are done with this packet + return 1; +} + +//*** +// +// Function: SendNextPropagatedPacket +// +// Descr: DPC called routine which dequeues the next packet from the +// propagated packets list and sends it +// +// Params: none +// +// Returns: none +// +//*** + + +VOID +SendNextPropagatedPkt(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PLIST_ENTRY lep; + PPACKET_TAG pktp; + LIST_ENTRY sendlist; + + InitializeListHead(&sendlist); + + // get next item from the propagated bcast queue and send it + + ACQUIRE_SPIN_LOCK(&PropagatedPktsListLock); + + PropagatedPktsDpcQueued = FALSE; + + while(!IsListEmpty(&PropagatedPktsList)) { + + lep = RemoveHeadList(&PropagatedPktsList); + InsertTailList(&sendlist, lep); + } + + RELEASE_SPIN_LOCK(&PropagatedPktsListLock); + + while(!IsListEmpty(&sendlist)) { + + lep = RemoveHeadList(&sendlist); + pktp = CONTAINING_RECORD(lep, PACKET_TAG, PacketLinkage); + SetType20PktDestNet(pktp); + SendPacket(pktp); + } +} + +VOID +UpdateSendStatistics(PNICCB niccbp, + PPACKET_TAG pktp) +{ + PUCHAR hdrp; + USHORT srcsock; + + switch(pktp->PacketType) { + + case RCV_PACKET: + + hdrp = pktp->DataBufferp; + GETSHORT2USHORT(&srcsock, hdrp + IPXH_DESTSOCK); + + if(srcsock == IPX_RIP_SOCKET) { + + niccbp->StatRipSent++; + } + else + { + niccbp->StatRoutedSent++; + } + + break; + + case RIP_SEND_PACKET: + + niccbp->StatRipSent++; + break; + + case PROPAGATED_BCAST_PACKET: + + niccbp->StatType20Sent++; + break; + + default: + + break; + + } +} + +VOID +SetType20PktDestNet(PPACKET_TAG pktp) +{ + PNICCB niccbp; + PUCHAR hdrp; + + // get the Nic Cb ptr where we should send this packet + niccbp = NicCbPtrTab[pktp->RemoteAddress.NicId]; + + // set the destination net in the packet + hdrp = pktp->DataBufferp; + + memcpy(hdrp + IPXH_DESTNET, niccbp->Network, 4); +} + + +VOID +UpdateRipResponseTickCount(PUCHAR hdrp, + PNICCB niccbp) +{ + USHORT resplen; + USHORT pktlen; + USHORT srcsock; + USHORT opcode; + USHORT nrofticks; + + // check if this is a RIP Response packet + GETSHORT2USHORT(&srcsock, hdrp + IPXH_SRCSOCK); + if(srcsock != IPX_RIP_SOCKET) { + + return; + } + + GETSHORT2USHORT(&opcode, hdrp + RIP_OPCODE); + if(opcode != RIP_RESPONSE) { + + return; + } + + //*** RIP Response Packet *** + + // get the response packet length + GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH); + + // for each network entry, increment the number of ticks so that + // we add the nr of ticks for the nic to send the packet on + for(resplen = RIP_INFO; + resplen < pktlen; + resplen += NE_ENTRYSIZE) { + + GETSHORT2USHORT(&nrofticks, hdrp + resplen + NE_NROFTICKS); + nrofticks += niccbp->TickCount; + PUTUSHORT2SHORT(hdrp + resplen + NE_NROFTICKS, nrofticks); + } +} diff --git a/private/ntos/tdi/isn/rip/sources.inc b/private/ntos/tdi/isn/rip/sources.inc new file mode 100644 index 000000000..d49c538bd --- /dev/null +++ b/private/ntos/tdi/isn/rip/sources.inc @@ -0,0 +1,60 @@ +!IF 0 + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ndis + +TARGETNAME=nwlnkrip +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc;..\..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\driver.c \ + ..\globals.c \ + ..\ipxbind.c \ + ..\init.c \ + ..\start.c \ + ..\nicman.c \ + ..\rcvpkt.c \ + ..\timer.c \ + ..\rcvind.c \ + ..\route.c \ + ..\send.c \ + ..\ripproc.c \ + ..\ripsend.c \ + ..\riptimer.c \ + ..\ripaux.c \ + ..\stubs.c \ + ..\registry.c \ + ..\lineind.c \ + ..\netbios.c \ + ..\nwlnkrip.rc + +RELATIVE_DEPTH=..\.. diff --git a/private/ntos/tdi/isn/rip/start.c b/private/ntos/tdi/isn/rip/start.c new file mode 100644 index 000000000..d9409f1a6 --- /dev/null +++ b/private/ntos/tdi/isn/rip/start.c @@ -0,0 +1,193 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: start.c +// +// Description: starts the router +// +// Author: Stefan Solomon (stefans) October 5, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +KEVENT BcastSndReqEvent; + +//*** +// +// Function: RouterStart +// +// Descr: Starts the router +// +// Parameters: +// +// Returns: STATUS_SUCCESS +// STATUS_INSUFFICIENT_RESOURCES +// +//*** + +NTSTATUS +RouterStart(VOID) +{ + PRIP_SNDREQ sndrqp; + + //*** Send a general RIP bcast to all active nets *** + //*** announcing our routes *** + + if((sndrqp = ExAllocatePool(NonPagedPool, sizeof(RIP_SNDREQ))) == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + BroadcastRipGeneralResponse(sndrqp); + + //*** Send a general RIP request to all active nets *** + //*** requesting their routing tables *** + + if((sndrqp = ExAllocatePool(NonPagedPool, sizeof(RIP_SNDREQ))) == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + sndrqp->SndReqId = RIP_GEN_REQUEST; + sndrqp->SendOnAllNics = TRUE; // send to everybody + memcpy(sndrqp->DestNode, bcastaddress, IPX_NODE_LEN); + sndrqp->DestSock = IPX_RIP_SOCKET; + sndrqp->DoNotSendNicCbp = NULL; // do no except any nic + sndrqp->SenderNicCbp = NULL; + sndrqp->SndCompleteEventp = NULL; + + RipDispatchSndReq(sndrqp); + + return STATUS_SUCCESS; +} + + +VOID +RouterStop(VOID) +{ + LIST_ENTRY DownRoutesList; + KIRQL oldirql; + PIPX_ROUTE_ENTRY rtep; + UINT seg; + BOOLEAN FirstRoute; + PRIP_SNDREQ sndreqp; + USHORT NicId; + PNICCB niccbp; + USHORT tmp; + PRIP_UPDATE_SNDREQ respcbp = NULL; // ptr to changes response to bcast + + RtPrint(DBG_INIT, ("IpxRouter: RouterStop: Entered\n")); + + KeInitializeEvent(&BcastSndReqEvent, NotificationEvent, FALSE); + + // if configured with a wan global net, send bcast update that it will + // go down now. + if(WanGlobalNetworkEnabled) { + + WanGlobalNetworkEnabled = FALSE; + + seg = IpxGetSegment(WanGlobalNetwork); + + // LOCK THE ROUTING TABLE + ExAcquireSpinLock(&SegmentLocksTable[seg], &oldirql); + + if(rtep = IpxGetRoute(seg, WanGlobalNetwork)) { + + IpxDeleteRoute(seg, rtep); + + // set hop count to "unreachable" + rtep->HopCount = 16; + RtPrint(DBG_INIT, ("IpxRouter: DeleteGlobalWanNet: Deleted wan global net route entry\n")); + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + if(rtep == NULL) { + + goto wan_global_done; + } + + // Broadcast the route entry down on all the LAN segments + BroadcastWanNetUpdate(rtep, NULL, &BcastSndReqEvent); + + ExFreePool(rtep); + } + +wan_global_done: + + + // scan the routing table and remove all route entries (except the + // permanent ones). + // For all route entries (permanent or not) make the snd req pkts for + // bcast. + + InitializeListHead(&DownRoutesList); + + for(seg=0; segFlags & IPX_ROUTER_PERMANENT_ENTRY) { + + // local route - do not delete it + + // mark it as down - temporarily + tmp = rtep->HopCount; + rtep->HopCount = 16; + + // add the route to the packets we prepare for bcast + AddRouteToBcastSndReq(&DownRoutesList, rtep); + + // restore the hop count + rtep->HopCount = tmp; + } + else + { + // non local route - delete it + + IpxDeleteRoute(seg, rtep); + + // mark it as down + rtep->HopCount = 16; + + // add the route to the packets we prepare for bcast + AddRouteToBcastSndReq(&DownRoutesList, rtep); + + // finally, free the route entry + ExFreePool(rtep); + } + } + + // UNLOCK THE ROUTING TABLE + ExReleaseSpinLock(&SegmentLocksTable[seg], oldirql); + + } // for all segments + + // broadcast all the deleted routes + while((sndreqp = GetBcastSndReq(&DownRoutesList, &NicId)) != NULL) { + + // get the nic ptr for this snd req + niccbp = NicCbPtrTab[NicId]; + + // set up the request to send a bcast response with the changes + // to all the nics except this one + // The send is made with WAIT ON EVENT option so that we don't download + // until all sends are completed. + BroadcastRipUpdate(sndreqp, niccbp, &BcastSndReqEvent); + } +} diff --git a/private/ntos/tdi/isn/rip/stubs.c b/private/ntos/tdi/isn/rip/stubs.c new file mode 100644 index 000000000..00eb42f8f --- /dev/null +++ b/private/ntos/tdi/isn/rip/stubs.c @@ -0,0 +1,38 @@ + +#include "rtdefs.h" + +// define stubs for functions not implemented + +VOID +RtStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength +) +{ + return; +} + +VOID +RtFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute +) +{ + return; +} + +VOID +NicCloseComplete(PNICCB niccbp) +{ + return; +} + +VOID +RtScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry +) +{ + return; +} diff --git a/private/ntos/tdi/isn/rip/timer.c b/private/ntos/tdi/isn/rip/timer.c new file mode 100644 index 000000000..de65d6131 --- /dev/null +++ b/private/ntos/tdi/isn/rip/timer.c @@ -0,0 +1,140 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: timer.c +// +// Description: global timer manager +// +// Author: Stefan Solomon (stefans) October 18, 1993. +// +// Revision History: +// +//*** + +#include "rtdefs.h" + +//*** Definitions of time intervals *** + +// global timer tick +#define TIME_1SEC 1L + +// time to scavenge the rcv pkt pool in seconds +#define RCVPKT_POOL_TIMEOUT 2 + +// time to increment the wan Inactivity timer (1 minute) +#define WAN_INACTIVITY_TIMEOUT 60 + +VOID +WanInactivityTimer(VOID); + + + +KTIMER GlobalTimer; +KDPC GlobalTimerDpc; + +UINT RcvPktPoolTimerCount; +UINT WanInactivityTimerCount; + +VOID +RtTimer(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + + +VOID +InitRtTimer(VOID) +{ + // initialize the 5 secs global timer + KeInitializeDpc(&GlobalTimerDpc, RtTimer, NULL); + KeInitializeTimer(&GlobalTimer); + + // init the timeout for the rcv pkt pool scavenger + RcvPktPoolTimerCount = RCVPKT_POOL_TIMEOUT; + + // init rip aging and bcast timer + InitRipTimer(); + + // init the timeout for the Wan Inactivity timer + WanInactivityTimerCount = WAN_INACTIVITY_TIMEOUT; +} + +VOID +StartRtTimer(VOID) +{ + LARGE_INTEGER timeout; + + timeout.LowPart = (ULONG)(-TIME_1SEC * 10000000L); + timeout.HighPart = -1; + + KeSetTimer(&GlobalTimer, timeout, &GlobalTimerDpc); +} + +VOID +StopRtTimer(VOID) +{ + BOOLEAN rc = FALSE; + + while(rc == FALSE) { + + rc = KeCancelTimer(&GlobalTimer); + } +} + +VOID +RtTimer(PKDPC Dpc, + PVOID DefferedContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + // call the rcv pkt pool scavenger every 2 secs + if(--RcvPktPoolTimerCount == 0) { + + RcvPktPoolTimerCount = RCVPKT_POOL_TIMEOUT; + RcvPktPoolScavenger(); + } + + // call the rip aging and bcast timer + RipTimer(); + + // call the wan Inactivity timer every 60 seconds + if(--WanInactivityTimerCount == 0) { + + WanInactivityTimerCount = WAN_INACTIVITY_TIMEOUT; + WanInactivityTimer(); + } + + // re-start the timer + StartRtTimer(); +} + +//*** +// +// Function: WanInactivityTimer +// +// Descr: Scans the niccbs and increments the inactivity counter for +// all WAN nics. +// +//*** + +VOID +WanInactivityTimer(VOID) +{ + PNICCB niccbp; + USHORT i; + + // increment the Inactivity counter for all WAN nics which are active + for(i=0; iNicState == NIC_ACTIVE) && + (niccbp->DeviceType == NdisMediumWan)) { + + IpxIncrementWanInactivity(niccbp->NicId); + } + } +} diff --git a/private/ntos/tdi/isn/rip/up/makefile b/private/ntos/tdi/isn/rip/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/rip/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/rip/up/sources b/private/ntos/tdi/isn/rip/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isn/rip/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/rip/utils.h b/private/ntos/tdi/isn/rip/utils.h new file mode 100644 index 000000000..90a1f9762 --- /dev/null +++ b/private/ntos/tdi/isn/rip/utils.h @@ -0,0 +1,77 @@ +/*******************************************************************/ +/* Copyright(c) 1993 Microsoft Corporation */ +/*******************************************************************/ + +//*** +// +// Filename: utils.h +// +// Description: Contains miscellaneous utilities +// +// Author: Stefan Solomon (stefans) October 4, 1993. +// +// Revision History: +// +//*** + +#ifndef _UTILS_ +#define _UTILS_ + +//*** General Spin Lock Utilities *** + +#define INITIALIZE_SPIN_LOCK(lockp) \ + NdisAllocateSpinLock(lockp) + +#define DEINITIALIZE_SPIN_LOCK(lock) \ + NdisFreeSpinLock(lock) + +#define ACQUIRE_SPIN_LOCK(lockp) \ + NdisAcquireSpinLock(lockp) + +#define RELEASE_SPIN_LOCK(lockp) \ + NdisReleaseSpinLock(lockp) + +//*** Routing Table Spin Locks *** + +#define ACQUIRE_RTSEGMENT_SPIN_LOCK(segment, oldirqlp) \ + ExAcquireSpinLock(SegmentLocksTable + segment, oldirqlp) + +#define RELEASE_RTSEGEMENT_SPIN_LOCK(segment, oldirqlp) \ + ExReleaseSpinLock(SegmentLocksTable + segment, oldirqlp) + +/* + * The following macros deal with on-the-wire short and long values + * + * On the wire format is big-endian i.e. a long value of 0x01020304 is + * represented as 01 02 03 04. + * Similarly a short value of 0x0102 is represented as 01 02. + * + * The host format is not assumed since it will vary from processor to + * processor. + */ + +// Get a short from on-the-wire format to a USHORT in the host format +#define GETSHORT2USHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 8) + \ + (*((PUCHAR)(SrcPtr)+1) )) + +// Get a long from on-the-wire format to a ULONG in the host format +#define GETLONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PUCHAR)(SrcPtr)+0) << 24) + \ + (*((PUCHAR)(SrcPtr)+1) << 16) + \ + (*((PUCHAR)(SrcPtr)+2) << 8) + \ + (*((PUCHAR)(SrcPtr)+3) )) + +// Put a USHORT from the host format to a short to on-the-wire format +#define PUTUSHORT2SHORT(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((USHORT)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR)(Src) + +// Put a ULONG from the host format to an array of 4 UCHARs on-the-wire format +#define PUTULONG2LONG(DstPtr, Src) \ + *((PUCHAR)(DstPtr)+0) = (UCHAR) ((ULONG)(Src) >> 24), \ + *((PUCHAR)(DstPtr)+1) = (UCHAR) ((ULONG)(Src) >> 16), \ + *((PUCHAR)(DstPtr)+2) = (UCHAR) ((ULONG)(Src) >> 8), \ + *((PUCHAR)(DstPtr)+3) = (UCHAR) (Src) + +#endif // _UTILS_ diff --git a/private/ntos/tdi/isn/sockhelp/makefile b/private/ntos/tdi/isn/sockhelp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/sockhelp/sources b/private/ntos/tdi/isn/sockhelp/sources new file mode 100644 index 000000000..cfa9e8e30 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/sources @@ -0,0 +1,32 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=wshisn +DLLBASE=0x75a00000 + +TARGETNAME=wshisn +TARGETPATH=obj +TARGETPATHLIB=$(BASEDIR)\public\sdk\lib +TARGETTYPE=DYNLINK +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\wsock32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib + +USE_NTDLL=1 + +!IF 1 +INCLUDES=..\inc;..\..\..\..\inc +!ELSE +INCLUDES=..\inc;$(BASEDIR)\private\inc +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=wshisn.c \ + wshelper.c \ + wshutil.c \ + wshisn.rc + diff --git a/private/ntos/tdi/isn/sockhelp/wshelper.c b/private/ntos/tdi/isn/sockhelp/wshelper.c new file mode 100644 index 000000000..5895c4791 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/wshelper.c @@ -0,0 +1,1778 @@ +/**************************************************************************** +* (c) Copyright 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX WinSock Helper DLL for Windows NT +* +* Module: ipx/sockhelp/wshelper.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +* +***************************************************************************** +* +* Functional Description: +* +****************************************************************************/ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(UNICODE) +#define NWLNKSPX_SERVICE_NAME L"nwlnkspx" +#else +#define NWLNKSPX_SERVICE_NAME "nwlnkspx" +#endif + + +typedef struct _IPX_OLD_ADDRESS_DATA { + UINT adapternum; + UCHAR netnum[4]; + UCHAR nodenum[6]; +} IPX_OLD_ADDRESS_DATA, *PIPX_OLD_ADDRESS_DATA; + + +/** Device names for IPX sockets **/ + +#define ISNDGRAM_DEVNAME L"\\Device\\NwlnkIpx" + +/** Device names for SPX/SPXII sockets **/ + +#define ISNSTREAM_DEVNAME L"\\Device\\NwlnkSpx\\SpxStream" +#define ISNSEQPKT_DEVNAME L"\\Device\\NwlnkSpx\\Spx" + +#define ISNSTREAMII_DEVNAME L"\\Device\\NwlnkSpx\\Stream" +#define ISNSEQPKTII_DEVNAME L"\\Device\\NwlnkSpx" + +/** Friendly names for IPX and SPX. **/ + +#define SPX_NAME L"SPX" +#define SPX2_NAME L"SPX II" +#define IPX_NAME L"IPX" + +/** Start for IPX protocol families **/ + +#define MCSBASE_DGRAM NSPROTO_IPX + +/** **/ + +UCHAR wsh_bcast[6] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +// SPX Loaded flag, set for each process +BOOLEAN SpxLoaded = FALSE; + +// +// IPX/SPX provider GUIDs. +// + +GUID IpxProviderGuid = + { /* 11058240-be47-11cf-95c8-00805f48a192 */ + 0x11058240, + 0xbe47, + 0x11cf, + { 0x95, 0xc8, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92} + }; + +GUID SpxProviderGuid = + { /* 11058241-be47-11cf-95c8-00805f48a192 */ + 0x11058241, + 0xbe47, + 0x11cf, + { 0x95, 0xc8, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92} + }; + +/** Forward Decls/External Prototypes **/ +DWORD +WshLoadSpx( + VOID); + +extern +INT +do_tdi_action( + HANDLE, + ULONG, + PUCHAR, + INT, + BOOLEAN, + PHANDLE OPTIONAL); + +/*page**************************************************************** + These are the triples we support. +*********************************************************************/ +typedef struct _MAPPING_TRIPLE { + INT triple_addrfam; + INT triple_socktype; + INT triple_protocol; +} MAPPING_TRIPLE, *PMAPPING_TRIPLE; +#define MAPPING_NUM_COLUMNS 3 + +extern MAPPING_TRIPLE stream_triples[]; +extern int stream_num_triples; +extern int stream_table_size; + +extern MAPPING_TRIPLE dgram_triples[]; +extern int dgram_num_triples; +extern int dgram_table_size; + +/** Forward declarations on internal routines **/ + +BOOLEAN is_triple_in_list(PMAPPING_TRIPLE, ULONG, INT, INT, INT); + +/** + There is one of these structures allocated for every + socket that is created for us. +**/ + +typedef struct _WSHIPX_SOCKET_CONTEXT { + INT con_addrfam; + INT con_socktype; + INT con_pcol; + INT con_flags; + UCHAR con_sendptype; /* Current send packet type */ + UCHAR con_recvptype; /* Recv ptype we are filtering on */ + UCHAR con_dstype; /* Datastream type */ +} WSHIPX_SOCKET_CONTEXT, *PWSHIPX_SOCKET_CONTEXT; + +/** Values for con_flags **/ + +#define WSHCON_FILTER 0x0001 /* We are filtering on recv pkt type */ +#define WSHCON_EXTADDR 0x0002 /* Extended addressing is on */ +#define WSHCON_SENDHDR 0x0004 /* Send header flag */ +#define WSHCON_RCVBCAST 0x0008 /* It does receive broadcasts */ +#define WSHCON_IMM_SPXACK 0x0020 /* Immediate spx acks no piggyback */ + +/*page*************************************************************** + W S H O p e n S o c k e t + + This is called for the socket call. We make sure that + we support the address family/socket type/protocol triple + given and then we will allocate some memory to keep track + of the socket. + + Arguments - addrfam = Entry: Address family from socket call + Exit: Filled in address family + socktype = Entry: Socket type from socket call + Exit: Filled in socket type + pcol = Entry: Protocol from socket call + Exit: Filled in protocol + devname = Ptr to where to store device name + pcontext = Where to store context value + events = Bitmask for events we want to know about + + Returns - NO_ERROR = OK + Else = WinSock Error Code +*********************************************************************/ +INT WSHOpenSocket(PINT addrfam, PINT socktype, PINT pcol, + PUNICODE_STRING devname, PVOID *pcontext, PDWORD events) +{ + PWSHIPX_SOCKET_CONTEXT context; + + /** Determine whether this is DGRAM or STREAM or SEQPACKET **/ + + if (is_triple_in_list(stream_triples, stream_num_triples, + *addrfam, *socktype, *pcol)) { + + if (*socktype == SOCK_SEQPACKET) { + if (*pcol == NSPROTO_SPX) + RtlInitUnicodeString(devname, ISNSEQPKT_DEVNAME); + else + RtlInitUnicodeString(devname, ISNSEQPKTII_DEVNAME); + } + else { + if (*pcol == NSPROTO_SPX) + RtlInitUnicodeString(devname, ISNSTREAM_DEVNAME); + else + RtlInitUnicodeString(devname, ISNSTREAMII_DEVNAME); + } + + if (!SpxLoaded) { + + WshLoadSpx(); + + } + } + + /** Check for DGRAM **/ + + else if (is_triple_in_list(dgram_triples, dgram_num_triples, + *addrfam, *socktype, *pcol)) { + + RtlInitUnicodeString(devname, ISNDGRAM_DEVNAME); + } + + /** + All others are errors. This should never happen unless + the registry information is wrong. + **/ + + else + return WSAEINVAL; + + /** Allocate context for the socket **/ + + context = RtlAllocateHeap(RtlProcessHeap(), 0L, sizeof(*context)); + if (context == NULL) + return WSAENOBUFS; + + /** Init the context **/ + + context->con_addrfam = *addrfam; + context->con_socktype = *socktype; + context->con_pcol = *pcol; + context->con_flags = WSHCON_RCVBCAST; + context->con_sendptype = (UCHAR)(*pcol - MCSBASE_DGRAM); + context->con_recvptype = 0; + context->con_dstype = 0; + + /** + Tell the Windows Sockets DLL which state transitions we + are interested in. + **/ + + *events = WSH_NOTIFY_CLOSE | WSH_NOTIFY_BIND | WSH_NOTIFY_CONNECT; + + /** Give WinSock DLL our context pointer **/ + + *pcontext = context; + + /** Everything OK - return OK **/ + + return NO_ERROR; +} + +/*page************************************************************** + W S H G e t S o c k A d d r T y p e + + This routine parses a sockaddr to determine the type + of machine address and endpoint address portions of the + sockaddr. This is called by the WinSock DLL whenever it + needs to interpret a sockaddr. + + Arguments - sockaddr = Ptr to sockaddr struct to evaluate + sockaddrlen = Length of data in the sockaddr + sockaddrinfo = Ptr to structure to recv info + about the sockaddr + + Returns - NO_ERROR = Evaluation OK + Else = WinSock error code +********************************************************************/ +INT WSHGetSockaddrType(PSOCKADDR sockaddr, DWORD sockaddrlen, + PSOCKADDR_INFO sockaddrinfo) +{ + PSOCKADDR_IPX sa = (PSOCKADDR_IPX)sockaddr; + + + /** Make sure the address family is correct **/ + + if (sa->sa_family != AF_NS) + return WSAEAFNOSUPPORT; + + /** Make sure the length is OK **/ + + if (sockaddrlen < sizeof(SOCKADDR_IPX)) + return WSAEFAULT; + + /** Looks like a good addr - determine the type **/ + + if (!memcmp(sa->sa_nodenum, wsh_bcast, 6)) + sockaddrinfo->AddressInfo = SockaddrAddressInfoBroadcast; + else + sockaddrinfo->AddressInfo = SockaddrAddressInfoNormal; + + /** Determine the endpoint **/ + + if (sa->sa_socket == 0) + sockaddrinfo->EndpointInfo = SockaddrEndpointInfoWildcard; + else if (ntohs(sa->sa_socket) < 2000) + sockaddrinfo->EndpointInfo = SockaddrEndpointInfoReserved; + else + sockaddrinfo->EndpointInfo = SockaddrEndpointInfoNormal; + + /** **/ + + return NO_ERROR; +} + +/*page************************************************************** + W S H G e t W i n s o c k M a p p i n g + + Returns the list of address family/socket type/protocol + triples supported by this helper DLL. + + Arguments - mapping = Contect ptr from WSAOpenSocket + maplen = + + Returns - The length in bytes of a eeded OK + Else = WinSock error code +********************************************************************/ +DWORD WSHGetWinsockMapping(PWINSOCK_MAPPING mapping, DWORD maplen) +{ + DWORD len; + + /** + Figure how much data we are going to copy into + the user buffer. + **/ + + len = sizeof(WINSOCK_MAPPING) - sizeof(MAPPING_TRIPLE) + + dgram_table_size + stream_table_size; + + /** + If the buffer passed is too small, then return the size + that is needed. The caller should then call us again + with a buffer of the correct size. + **/ + + if (len > maplen) + return len; + + /** Fill in the output buffer **/ + + mapping->Rows = stream_num_triples + dgram_num_triples; + mapping->Columns = MAPPING_NUM_COLUMNS; + RtlMoveMemory(mapping->Mapping, + stream_triples, + stream_table_size); + + RtlMoveMemory((PCHAR)mapping->Mapping + stream_table_size, + dgram_triples, + dgram_table_size); + + /** Return the number of bytes we filled in **/ + + return len; +} + +/*page*************************************************************** + W S H N o t i f y + + This routine is called for events that we registered at + open socket time. + + Arguments - context = Context ptr from WSAOpenSocket + handle = Socket handle + addrhandle = Datagram Handle + connhandle = Connection Handle + event = What event happened + + Returns - NO_ERROR = Operation succeeded OK + Else = WinSock error code +*********************************************************************/ +INT WSHNotify(PVOID context, SOCKET handle, + HANDLE addrhandle, HANDLE connhandle, + DWORD event) +{ + INT rc; + INT t1; + PWSHIPX_SOCKET_CONTEXT ct; + + /** Get context pointer **/ + + ct = (PWSHIPX_SOCKET_CONTEXT)context; + + /** On close - just free the context structure **/ + + if (event == WSH_NOTIFY_CLOSE) { + RtlFreeHeap(RtlProcessHeap(), 0L, context); + return NO_ERROR; + } + + /** On bind set the send packet type **/ + + if (event == WSH_NOTIFY_BIND) + { + if (ct->con_socktype == SOCK_DGRAM) + { + /** Set the send packet ptype **/ + t1 = (UINT)ct->con_sendptype; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_PTYPE, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + + if (ct->con_flags & WSHCON_EXTADDR) + { + t1 = 1; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_EXTENDED_ADDRESS, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + + /** Set the recv filter packet type **/ + + if (ct->con_flags & WSHCON_FILTER) + { + t1 = (UINT)ct->con_recvptype; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_FILTERPTYPE, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + + /** Set up broadcast reception **/ + + if (ct->con_flags & WSHCON_RCVBCAST) + { + + t1 = 1; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_RECEIVE_BROADCAST, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + + /** Enable send header if we need to **/ + if (ct->con_flags & WSHCON_SENDHDR) + { + t1 = 1; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_RECVHDR, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + } + else if ((ct->con_socktype == SOCK_STREAM) || + (ct->con_socktype == SOCK_SEQPACKET)) + { + if (ct->con_flags & WSHCON_SENDHDR) + { + t1 = 1; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_RECVHDR, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + + if (ct->con_flags & WSHCON_IMM_SPXACK) + { + t1 = 1; + rc = WSHSetSocketInformation( + context, handle, addrhandle, + connhandle, NSPROTO_IPX, + IPX_IMMEDIATESPXACK, (PCHAR)&t1, sizeof(INT)); + + if (rc) + return rc; + } + } + + /** It is OK - return OK **/ + return NO_ERROR; + } + + /** On connect set things not set already **/ + if (event == WSH_NOTIFY_CONNECT) + { + + /** If on DGRAM - just return OK **/ + if (ct->con_socktype == SOCK_DGRAM) + return NO_ERROR; + + /** + If the datastream type has been set - set it + **/ + + if (ct->con_dstype) + { + rc = do_tdi_action(connhandle, MSPX_SETDATASTREAM, &ct->con_dstype, 1, FALSE, NULL); + if (rc) + return rc; + } + + /** It is OK - return OK **/ + return NO_ERROR; + } + + /** All others are bad **/ + return WSAEINVAL; +} + + +/*page************************************************************** + W S H G e t S o c k I n f o r m a t i o n + + This routine retrieves information about a socket for those + socket options supported in this DLL. The options + supported here are SO_KEEPALIVE and SO_DONTROUTE. This + routine is called by the WinSock DLL when a level/option name + combination is passed to getsockopt that the WinSock DLL + does not understand. + + Arguments - context = Context ptr from WSAOpenSocket + handle = Socket handle + addrhandle = Datagram Handle + connhandle = Connection Handle + level = Level from getsockopt call + optname = Option name from getsockopt call + optvalue = Option value ptr from getsockopt call + optlength = Option length field from getsockopt call + + Returns - NO_ERROR = Operation succeeded OK + Else = WinSock error code +********************************************************************/ +INT WSHGetSocketInformation(PVOID context, SOCKET handle, + HANDLE addrhandle, HANDLE connhandle, + INT level, INT optname, PCHAR optvalue, + PINT optlength) +{ + PWSHIPX_SOCKET_CONTEXT ct; + INT rc; + INT ibuf[2]; + PIPX_ADDRESS_DATA p; + + /** Get ptr to context **/ + + ct = (PWSHIPX_SOCKET_CONTEXT)context; + + // + // Check if this is an internal request for context information. + // + + if ( level == SOL_INTERNAL && optname == SO_CONTEXT ) { + + // + // The Windows Sockets DLL is requesting context information + // from us. If an output buffer was not supplied, the Windows + // Sockets DLL is just requesting the size of our context + // information. + // + + if ( optvalue != NULL ) { + + // + // Make sure that the buffer is sufficient to hold all the + // context information. + // + + if ( *optlength < sizeof(*ct) ) { + return WSAEFAULT; + } + + // + // Copy in the context information. + // + + RtlCopyMemory( optvalue, ct, sizeof(*ct) ); + } + + *optlength = sizeof(*ct); + + return NO_ERROR; + } + + /** The only level we support is NSPROTO_IPX **/ + + if (level != NSPROTO_IPX) + return WSAEINVAL; + + /** Fill in the result based on the options name **/ + + switch (optname) { + + /** Get the current send packet type **/ + + case IPX_PTYPE: + + /** Make sure the length is OK **/ + + if (*optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Set the type **/ + + *(UINT *)optvalue = (UINT)ct->con_sendptype; + *optlength = sizeof(UINT); + break; + + /** Get the current recv packet type filter **/ + + case IPX_FILTERPTYPE: + + /** Make sure length is OK **/ + + if (*optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** If option not on - return error **/ + + if (!(ct->con_flags & WSHCON_FILTER)) + return WSAEINVAL; + + /** Save the new value **/ + + *(UINT *)optvalue = (UINT)ct->con_recvptype; + *optlength = sizeof(UINT); + break; + + /** Get the max DGRAM size that can be sent **/ + + case IPX_MAXSIZE: + + /** Make sure length is OK **/ + + if (*optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the value from the driver **/ + + rc = do_tdi_action(addrhandle, MIPX_GETPKTSIZE, (PUCHAR)ibuf, sizeof(INT)*2, TRUE, NULL); + + *(INT *)optvalue = ibuf[1]; + *optlength = sizeof(int); + + /** Return the result **/ + + return rc; + + /** Get the max adapternum that is valid **/ + + case IPX_MAX_ADAPTER_NUM: + + /** Make sure length is OK **/ + + if (*optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the value from the driver **/ + + rc = do_tdi_action(addrhandle, MIPX_ADAPTERNUM, optvalue, sizeof(INT), TRUE, NULL); + + *optlength = sizeof(int); + + /** Return the result **/ + + return rc; + + /** Get SPX statistics **/ + + case IPX_SPXGETCONNECTIONSTATUS: + + /** Make sure data length OK **/ + + if (*optlength < sizeof(IPX_SPXCONNSTATUS_DATA)) + return WSAEFAULT; + + /** Make sure this is for a STREAM socket **/ + + if ((ct->con_socktype != SOCK_STREAM) && + (ct->con_socktype != SOCK_SEQPACKET)) { + + return WSAEINVAL; + } + + /** Send it to the driver **/ + + rc = do_tdi_action( + connhandle, + MSPX_GETSTATS, + optvalue, + *optlength, + FALSE, + NULL); + + if (rc) + return rc; + + *optlength = sizeof(IPX_SPXCONNSTATUS_DATA); + + /** Return OK **/ + + return NO_ERROR; + + /** Get the current datastream type to send pkts with **/ + + case IPX_DSTYPE: + + /** Make sure length is OK **/ + + if (*optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a STREAM socket **/ + + if ((ct->con_socktype != SOCK_STREAM) && + (ct->con_socktype != SOCK_SEQPACKET)) { + + return WSAEINVAL; + } + + /** Save the new value **/ + + *(UINT *)optvalue = (UINT)ct->con_dstype; + *optlength = sizeof(UINT); + break; + + /** Get net information **/ + + case IPX_GETNETINFO: + + /** Make sure data length OK **/ + + if (*optlength < sizeof(IPX_NETNUM_DATA)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Send it to the driver **/ + + rc = do_tdi_action( + addrhandle, + MIPX_GETNETINFO, + optvalue, + *optlength, + TRUE, + NULL); + + if (rc) { + return rc; + } + + *optlength = sizeof(IPX_NETNUM_DATA); + + /** Return OK **/ + + return NO_ERROR; + + /** Get net information without RIPping **/ + + case IPX_GETNETINFO_NORIP: + + /** Make sure data length OK **/ + + if (*optlength < sizeof(IPX_NETNUM_DATA)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Send it to the driver **/ + + rc = do_tdi_action( + addrhandle, + MIPX_GETNETINFO_NR, + optvalue, + *optlength, + TRUE, + NULL); + + if (rc) { + return rc; + } + + *optlength = sizeof(IPX_NETNUM_DATA); + + /** Return OK **/ + + return NO_ERROR; + + /** Like GETNETINFO, but force a re-rip **/ + + case IPX_RERIPNETNUMBER: + + /** Make sure data length OK **/ + + if (*optlength < sizeof(IPX_NETNUM_DATA)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Send it to the driver **/ + + rc = do_tdi_action( + addrhandle, + MIPX_RERIPNETNUM, + optvalue, + *optlength, + TRUE, + NULL); + + if (rc) { + return rc; + } + + *optlength = sizeof(IPX_NETNUM_DATA); + + /** Return OK **/ + + return NO_ERROR; + + /** Get card information **/ + + case IPX_ADDRESS_NOTIFY: + + /** We need the action header, the data, and the event handle **/ + + if (*optlength < (INT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_ADDRESS_DATA) + sizeof(HANDLE))) + return WSAEFAULT; + + /** Otherwise just fall through **/ + + case IPX_ADDRESS: + + /** Make sure data length OK **/ + + if (*optlength < sizeof(IPX_OLD_ADDRESS_DATA)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Send it to the driver **/ + + if (optname == IPX_ADDRESS) { + + rc = do_tdi_action( + addrhandle, + MIPX_GETCARDINFO, + optvalue, + *optlength, + TRUE, + NULL); + + } else { + + rc = do_tdi_action( + addrhandle, + MIPX_NOTIFYCARDINFO, + optvalue, + *optlength - sizeof(HANDLE), + TRUE, + (PHANDLE)(optvalue + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_ADDRESS_DATA))); + } + + if (rc) { + p = (PIPX_ADDRESS_DATA)optvalue; + memset(p->netnum, 0xFF, 4); + memset(p->nodenum, 0xFF, 6); + return rc; + } + + /** Return OK **/ + + if (*optlength < sizeof(IPX_ADDRESS_DATA)) { + *optlength = sizeof(IPX_OLD_ADDRESS_DATA); + } else if (*optlength < sizeof(IPX_ADDRESS_DATA)) { + *optlength = sizeof(IPX_ADDRESS_DATA); + } + + return NO_ERROR; + + /** All others are error **/ + + default: + return WSAENOPROTOOPT; + } + + /** All is OK **/ + + return NO_ERROR; +} + +/*page*************************************************************** + W S H S e t S o c k e t I n f o r m a t i o n + + This routine sets information about a socket for those + options supported in this helper DLL. This routine + is called when a setsockopt call is made and the option/level + passed is unknown to the WinSock DLL. + + Arguments - context = Context ptr from WSAOpenSocket + handle = Socket handle + addrhandle = Datagram Handle + connhandle = Connection Handle + level = Level from getsockopt call + optname = Option name from getsockopt call + optvalue = Option value ptr from getsockopt call + optlength = Option length field from getsockopt call + + Returns - NO_ERROR = Operation succeeded OK + Else = WinSock error code +*********************************************************************/ +INT WSHSetSocketInformation(PVOID context, SOCKET handle, + HANDLE addrhandle, HANDLE connhandle, + INT level, INT optname, PCHAR optvalue, + INT optlength) +{ + PWSHIPX_SOCKET_CONTEXT ct; + INT rc; + + /** Get ptr to context **/ + + ct = (PWSHIPX_SOCKET_CONTEXT)context; + + // + // Check if this is an internal request for context information. + // + + if ( level == SOL_INTERNAL && optname == SO_CONTEXT ) { + + // + // The Windows Sockets DLL is requesting that we set context + // information for a new socket. If the new socket was + // accept()'ed, then we have already been notified of the socket + // and HelperDllSocketContext will be valid. If the new socket + // was inherited or duped into this process, then this is our + // first notification of the socket and HelperDllSocketContext + // will be equal to NULL. + // + // Insure that the context information being passed to us is + // sufficiently large. + // + + if ( optlength < sizeof(*ct) ) { + return WSAEINVAL; + } + + if ( ct == NULL ) { + + // + // This is our notification that a socket handle was + // inherited or duped into this process. Allocate a context + // structure for the new socket. + // + + ct = RtlAllocateHeap( RtlProcessHeap( ), 0, sizeof(*ct) ); + if ( ct == NULL ) { + return WSAENOBUFS; + } + + // + // Copy over information into the context block. + // + + RtlCopyMemory( ct, optvalue, sizeof(*ct) ); + + // + // Tell the Windows Sockets DLL where our context information is + // stored so that it can return the context pointer in future + // calls. + // + + *(PWSHIPX_SOCKET_CONTEXT *)optvalue = ct; + + return NO_ERROR; + + } else { + + PWSHIPX_SOCKET_CONTEXT parentContext; + INT one = 1; + + // + // The socket was accept()'ed and it needs to have the same + // properties as it's parent. The OptionValue buffer + // contains the context information of this socket's parent. + // + + parentContext = (PWSHIPX_SOCKET_CONTEXT)optvalue; + + ASSERT( ct->con_addrfam == parentContext->con_addrfam ); + ASSERT( ct->con_socktype == parentContext->con_socktype ); + ASSERT( ct->con_pcol == parentContext->con_pcol ); + + return NO_ERROR; + } + } + + /** We only support level NSPROTO_IPX **/ + + if (level != NSPROTO_IPX) + return WSAEINVAL; + + /** Handle the options **/ + + switch (optname) { + + /** Set the send packet type **/ + + case IPX_PTYPE: + + /** Make sure length is OK **/ + + if (optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the value and check it **/ + + rc = *(INT *)optvalue; + if ((rc < 0) || (rc > 255)) + return WSAEINVAL; + + /** Save the new value **/ + + ct->con_sendptype = (UCHAR)rc; + + /** Send the new value down to the driver **/ + + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_SETSENDPTYPE, &ct->con_sendptype, 1, TRUE, NULL); + else + rc = NO_ERROR; + + return rc; + + /** Set the recv filter for packet type **/ + + case IPX_FILTERPTYPE: + + /** Make sure length is OK **/ + + if (optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the value and check it **/ + + rc = *(INT *)optvalue; + if ((rc < 0) || (rc > 255)) + return WSAEINVAL; + + /** Save the new value **/ + + ct->con_recvptype = (UCHAR)rc; + ct->con_flags |= WSHCON_FILTER; + + /** Send the new value down to the driver **/ + + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_FILTERPTYPE, &ct->con_recvptype, 1, TRUE, NULL); + else + rc = NO_ERROR; + + /** **/ + + return rc; + + /** Stop filtering recv on pkt type **/ + + case IPX_STOPFILTERPTYPE: + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Turn off the flag **/ + + ct->con_flags &= ~WSHCON_FILTER; + + /** Tell the driver **/ + + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_NOFILTERPTYPE, NULL, 0, TRUE, NULL); + else + rc = NO_ERROR; + break; + + /** Set piggyback wait for backtraffic flag **/ + case IPX_IMMEDIATESPXACK: + + /** Get the optvalue as an INT **/ + + rc = *(INT *)optvalue; + + /** **/ + + if (rc) + { + /** Turn it ON **/ + rc = WSAEINVAL; + if ((ct->con_socktype == SOCK_STREAM) || + (ct->con_socktype == SOCK_SEQPACKET)) + { + rc = NO_ERROR; + + ct->con_flags |= WSHCON_IMM_SPXACK; + + if (addrhandle) + rc = do_tdi_action(addrhandle, MSPX_NOACKWAIT, NULL, 0, TRUE, NULL); + } + } + else + { + /** Turn it OFF **/ + rc = WSAEINVAL; + if ((ct->con_socktype == SOCK_STREAM) || + (ct->con_socktype == SOCK_SEQPACKET)) + { + rc = NO_ERROR; + + ct->con_flags &= ~WSHCON_IMM_SPXACK; + + if (addrhandle) + rc = do_tdi_action(addrhandle, MSPX_ACKWAIT, NULL, 0, TRUE, NULL); + } + } + + /** Return the result **/ + return rc; + + /** Set to recv pcol hdrs with data **/ + + case IPX_RECVHDR: + + /** Get the optvalue as an INT **/ + rc = *(INT *)optvalue; + + if (rc) + { + /** Turn it ON **/ + ct->con_flags |= WSHCON_SENDHDR; + + /** Send it to the driver **/ + rc = WSAEINVAL; + if (ct->con_socktype == SOCK_DGRAM) + { + rc = NO_ERROR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_SENDHEADER, NULL, 0, TRUE, NULL); + } + else if ((ct->con_socktype == SOCK_STREAM) || + (ct->con_socktype == SOCK_SEQPACKET)) + { + /** Do this on address handle **/ + rc = NO_ERROR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MSPX_SENDHEADER, NULL, 0, TRUE, NULL); + } + } + else + { + + /** Turn it OFF **/ + ct->con_flags &= ~WSHCON_SENDHDR; + + /** Send it to the driver **/ + rc = WSAEINVAL; + if (ct->con_socktype == SOCK_DGRAM) + { + rc = NO_ERROR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_NOSENDHEADER, NULL, 0, TRUE, NULL); + } + else if ((ct->con_socktype == SOCK_STREAM) || + (ct->con_socktype == SOCK_SEQPACKET)) + { + rc = NO_ERROR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MSPX_NOSENDHEADER, NULL, 0, TRUE, NULL); + } + } + + /** Return the result **/ + return rc; + + /** Set the Datastream type to send pkts with **/ + + case IPX_DSTYPE: + + /** Make sure length is OK **/ + + if (optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a STREAM socket **/ + + if ((ct->con_socktype != SOCK_STREAM) && + (ct->con_socktype != SOCK_SEQPACKET)) { + + return WSAEINVAL; + } + + /** Get the value and check it **/ + + rc = *(INT *)optvalue; + if ((rc < 0) || (rc > 255)) + return WSAEINVAL; + + /** Save the new value **/ + + ct->con_dstype = (UCHAR)rc; + + /** Send the new value down to the driver **/ + + if (connhandle) + rc = do_tdi_action(connhandle, MSPX_SETDATASTREAM, &ct->con_dstype, 1, FALSE, NULL); + else + rc = 0; + + /** **/ + + return rc; + + /** Set the extended address option **/ + + case IPX_EXTENDED_ADDRESS: + + /** Make sure length is OK **/ + + if (optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the optvalue as an INT **/ + + rc = *(INT *)optvalue; + + /** **/ + + if (rc) { + + /** Send the option down to the driver **/ + + ct->con_flags |= WSHCON_EXTADDR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_SENDADDROPT, NULL, 0, TRUE, NULL); + else + rc = NO_ERROR; + } + else { + + /** Send the option down to the driver **/ + + ct->con_flags &= ~WSHCON_EXTADDR; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_NOSENDADDROPT, NULL, 0, TRUE, NULL); + else + rc = NO_ERROR; + } + return rc; + + + /** Set the broadcast reception **/ + + case IPX_RECEIVE_BROADCAST: + + /** Make sure length is OK **/ + + if (optlength < sizeof(INT)) + return WSAEFAULT; + + /** Make sure this is for a DGRAM socket **/ + + if (ct->con_socktype != SOCK_DGRAM) + return WSAEINVAL; + + /** Get the optvalue as an INT **/ + + rc = *(INT *)optvalue; + + /** **/ + + if (rc) { + + /** Send the option down to the driver **/ + + ct->con_flags |= WSHCON_RCVBCAST; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_RCVBCAST, NULL, 0, TRUE, NULL); + else + rc = NO_ERROR; + } + else { + + /** Send the option down to the driver **/ + + ct->con_flags &= ~WSHCON_RCVBCAST; + if (addrhandle) + rc = do_tdi_action(addrhandle, MIPX_NORCVBCAST, NULL, 0, TRUE, NULL); + else + rc = NO_ERROR; + } + return rc; + + /** All others return error **/ + + default: + return WSAENOPROTOOPT; + } + + /** All Done OK **/ + + return NO_ERROR; +} + +/*page*************************************************************** + W S H G e t W i l d c a r d S o c k a d d r + + This routing returns a wilcard socket address for the + sockets DLL to use. + + Arguments - context = Context ptr from WSAOpenSocket + addrp = Ptr to where to store the address + addrlen = Ptr to where to store length of address + + Returns - NO_ERROR = Operation succeeded OK + Else = WinSock error code +*********************************************************************/ +INT WSHGetWildcardSockaddr(PVOID context, PSOCKADDR addrp, PINT addrlen) +{ + + /** + Setup the address as the address family + + all 0's for the rest. + **/ + + memset(addrp, 0, sizeof(SOCKADDR)); + addrp->sa_family = AF_NS; + + /** Set the address length **/ + + *addrlen = sizeof(SOCKADDR); + + /** Return OK **/ + + return NO_ERROR; +} + +/*page*************************************************************** + i s _ t r i p l e _ i n _ l i s t + + Check to see if the given triple is in the given + triple list. + + Arguments - tlist = Ptr to the triple list + tlen = Num entries in the triple list + addrfam = Address family to look for + socktype = Socket Type to look for + pcol = Protocol to look for + + Returns - TRUE = Yes + FALSE = No +*********************************************************************/ +BOOLEAN is_triple_in_list(PMAPPING_TRIPLE tlist, ULONG tlen, + INT addrfam, INT socktype, INT pcol) +{ + ULONG i; + + /** + Go thru the list and search to see if we can + find the given triple in the list. + **/ + + for (i = 0 ; i < tlen ; i++,tlist++) { + + /** If it matches - return OK **/ + + if ((addrfam == tlist->triple_addrfam) && + (socktype == tlist->triple_socktype) && + (pcol == tlist->triple_protocol)) + + return TRUE; + } + + /** Not Found **/ + + return FALSE; +} + +/*page*************************************************************** + W S H E n u m P r o t o c o l s + + Enumerates IPX/SPX protocols. + + Returns - NO_ERROR or an error code. +*********************************************************************/ +INT +WSHEnumProtocols ( + IN LPINT lpiProtocols, + IN LPTSTR lpTransportKeyName, + IN OUT LPVOID lpProtocolBuffer, + IN OUT LPDWORD lpdwBufferLength + ) +{ + DWORD bytesRequired; + PPROTOCOL_INFOW protocolInfo; + BOOL useSpx = FALSE; + BOOL useSpx2 = FALSE; + BOOL useIpx = FALSE; + BOOL spxString; + DWORD i; + PWCHAR namePtr; + INT entriesReturned = 0; + + // + // Determine whether we should return information for IPX or SPX. + // + + if ( _wcsicmp( L"NwlnkIpx", (LPWSTR)lpTransportKeyName ) == 0 ) { + spxString = FALSE; + } else { + spxString = TRUE; + } + + // + // Make sure that the caller cares about SPX, SPX2, and/or IPX. + // + + if ( ARGUMENT_PRESENT( lpiProtocols ) ) { + + for ( i = 0; lpiProtocols[i] != 0; i++ ) { + if ( lpiProtocols[i] == NSPROTO_SPX && spxString ) { + useSpx = TRUE; + } + if ( lpiProtocols[i] == NSPROTO_SPXII && spxString ) { + useSpx2 = TRUE; + } + if ( lpiProtocols[i] == NSPROTO_IPX && !spxString ) { + useIpx = TRUE; + } + } + + } else { + + useSpx = FALSE; + useSpx2 = spxString; + useIpx = !spxString; + } + + if ( !useSpx && !useSpx2 && !useIpx ) { + *lpdwBufferLength = 0; + return 0; + } + + // + // Make sure that the caller has specified a sufficiently large + // buffer. + // + + bytesRequired = (sizeof(PROTOCOL_INFO) * 3) + + ( (wcslen( SPX_NAME ) + 1) * sizeof(WCHAR)) + + ( (wcslen( SPX2_NAME ) + 1) * sizeof(WCHAR)) + + ( (wcslen( IPX_NAME ) + 1) * sizeof(WCHAR)); + + if ( bytesRequired > *lpdwBufferLength ) { + *lpdwBufferLength = bytesRequired; + return -1; + } + + // + // Initialize local variables. + // + + protocolInfo = lpProtocolBuffer; + namePtr = (PWCHAR)( (PCHAR)lpProtocolBuffer + *lpdwBufferLength ); + + // + // Fill in SPX info, if requested. + // + + if ( useSpx ) { + + entriesReturned += 1; + + protocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY | + XP_MESSAGE_ORIENTED | + XP_PSEUDO_STREAM | + XP_GUARANTEED_ORDER | + XP_FRAGMENTATION; + protocolInfo->iAddressFamily = AF_IPX; + protocolInfo->iMaxSockAddr = 0x10; + protocolInfo->iMinSockAddr = 0xE; + protocolInfo->iSocketType = SOCK_SEQPACKET; + protocolInfo->iProtocol = NSPROTO_SPX; + protocolInfo->dwMessageSize = 0xFFFFFFFF; + + namePtr = namePtr - (wcslen( SPX_NAME) + 1); + protocolInfo->lpProtocol = namePtr; + wcscpy( protocolInfo->lpProtocol, SPX_NAME ); + + protocolInfo += 1; + } + + // + // Fill in SPX II info, if requested. + // + + if ( useSpx2 ) { + + entriesReturned += 1; + + protocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY | + XP_MESSAGE_ORIENTED | + XP_PSEUDO_STREAM | + XP_GRACEFUL_CLOSE | + XP_GUARANTEED_ORDER | + XP_FRAGMENTATION; + protocolInfo->iAddressFamily = AF_IPX; + protocolInfo->iMaxSockAddr = 0x10; + protocolInfo->iMinSockAddr = 0xE; + protocolInfo->iSocketType = SOCK_SEQPACKET; + protocolInfo->iProtocol = NSPROTO_SPXII; + protocolInfo->dwMessageSize = 0xFFFFFFFF; + + namePtr = namePtr - (wcslen( SPX2_NAME) + 1); + protocolInfo->lpProtocol = namePtr; + wcscpy( protocolInfo->lpProtocol, SPX2_NAME ); + + protocolInfo += 1; + } + + // + // Fill in IPX info, if requested. + // + + if ( useIpx ) { + + entriesReturned += 1; + + protocolInfo->dwServiceFlags = XP_CONNECTIONLESS | + XP_MESSAGE_ORIENTED | + XP_SUPPORTS_BROADCAST | + XP_SUPPORTS_MULTICAST | + XP_FRAGMENTATION; + protocolInfo->iAddressFamily = AF_IPX; + protocolInfo->iMaxSockAddr = 0x10; + protocolInfo->iMinSockAddr = 0xE; + protocolInfo->iSocketType = SOCK_DGRAM; + protocolInfo->iProtocol = NSPROTO_IPX; + protocolInfo->dwMessageSize = 576; + + namePtr = namePtr - (wcslen( IPX_NAME) + 1); + protocolInfo->lpProtocol = namePtr; + wcscpy( protocolInfo->lpProtocol, IPX_NAME ); + } + + *lpdwBufferLength = bytesRequired; + + return entriesReturned; + +} // WSHEnumProtocols + + +#define _IPX_CONTROL_CODE(request,method) \ + CTL_CODE(FILE_DEVICE_TRANSPORT, request, method, FILE_ANY_ACCESS) +#define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED ) + +DWORD +WshLoadSpx( + VOID + ) +/*++ + +Routine Description: + + Starts the nwlnkspx.sys driver by submitting a special ioctl + to ipx, which calls ZwLoadDriver() for us. + +Arguments: + + none + +Returns: + + Error return from the load operation. + +++*/ +{ + DWORD err = NO_ERROR; + HANDLE FileHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING FileString; + WCHAR FileName[] = L"\\Device\\NwlnkIpx"; + NTSTATUS Status; + + RtlInitUnicodeString (&FileString, FileName); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &FileHandle, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + + err = ERROR_FILE_NOT_FOUND; + + } else { + + Status = NtDeviceIoControlFile( + FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_IPX_LOAD_SPX, + NULL, + 0, + NULL, + 0); + + if (Status == STATUS_IMAGE_ALREADY_LOADED) { + + err = ERROR_SERVICE_ALREADY_RUNNING; + + // + // #36451 + // If the service controller loads SPX ("net start nwlnkspx", or due to dependency of RPC on SPX) + // then we get this error the first time too. Keep a note of that. + // + // BUGBUG: we still leak a handle per process since the handle to the driver is actually created + // in the system process' context. The ideal way to fix this should be to have IPX associate the + // handle with the current process (so handle is destroyed when the process dies) or to have the + // dll tell IPX to close the handle it opened earlier. + // + SpxLoaded = TRUE; + + } else if (!NT_SUCCESS(Status)) { + + err = ERROR_IO_DEVICE; + + } else { + SpxLoaded = TRUE; + } + + NtClose (FileHandle); + + } + + return(err); +} + +/*page*************************************************************** + W S H G e t P r o v i d e r G u i d + + Queries the GUID identifier for this protocol. + + Returns - NO_ERROR or an error code. +*********************************************************************/ +INT +WINAPI +WSHGetProviderGuid ( + IN LPWSTR ProviderName, + OUT LPGUID ProviderGuid + ) +{ + + if( ProviderName == NULL || + ProviderGuid == NULL ) { + + return WSAEFAULT; + + } + + if( _wcsicmp( ProviderName, L"NwlnkIpx" ) == 0 ) { + + RtlCopyMemory( + ProviderGuid, + &IpxProviderGuid, + sizeof(GUID) + ); + + return NO_ERROR; + + } + + if( _wcsicmp( ProviderName, L"NwlnkSpx" ) == 0 ) { + + RtlCopyMemory( + ProviderGuid, + &SpxProviderGuid, + sizeof(GUID) + ); + + return NO_ERROR; + + } + + return WSAEINVAL; + +} // WSHGetProviderGuid + diff --git a/private/ntos/tdi/isn/sockhelp/wshisn.c b/private/ntos/tdi/isn/sockhelp/wshisn.c new file mode 100644 index 000000000..75cb70f40 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/wshisn.c @@ -0,0 +1,323 @@ +/**************************************************************************** +* (c) Copyright 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX WinSock Helper DLL for Windows NT +* +* Module: ipx/sockhelp/wshnwlnk.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +* +***************************************************************************** +* +* Functional Description: +* +****************************************************************************/ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +/*page**************************************************************** + These are the triples we support. +*********************************************************************/ +typedef struct _MAPPING_TRIPLE { + INT triple_addrfam; + INT triple_socktype; + INT triple_protocol; +} MAPPING_TRIPLE, *PMAPPING_TRIPLE; + +MAPPING_TRIPLE stream_triples[] = { + { AF_NS, SOCK_STREAM, NSPROTO_SPX }, + { AF_NS, SOCK_SEQPACKET, NSPROTO_SPX }, + { AF_NS, SOCK_STREAM, NSPROTO_SPXII }, + { AF_NS, SOCK_SEQPACKET, NSPROTO_SPXII }, +}; +int stream_num_triples = 4; /* When SPXII - set to 4 */ +int stream_table_size = sizeof(stream_triples); + +/** + For IPX we assign the default packet type according to the + protocol type used. The user can also we setsockopt + to set the packet type. +**/ + +MAPPING_TRIPLE dgram_triples[] = { + { AF_NS, SOCK_DGRAM, NSPROTO_IPX }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+1 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+2 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+3 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+4 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+5 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+6 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+7 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+8 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+9 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+10 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+11 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+12 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+13 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+14 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+15 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+16 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+17 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+18 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+19 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+20 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+21 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+22 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+23 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+24 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+25 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+26 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+27 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+28 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+29 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+30 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+31 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+32 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+33 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+34 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+35 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+36 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+37 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+38 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+39 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+40 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+41 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+42 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+43 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+44 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+45 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+46 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+47 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+48 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+49 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+50 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+51 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+52 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+53 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+54 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+55 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+56 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+57 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+58 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+59 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+60 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+61 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+62 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+63 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+64 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+65 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+66 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+67 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+68 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+69 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+70 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+71 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+72 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+73 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+74 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+75 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+76 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+77 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+78 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+79 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+80 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+81 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+82 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+83 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+84 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+85 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+86 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+87 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+88 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+89 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+90 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+91 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+92 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+93 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+94 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+95 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+96 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+97 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+98 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+99 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+100 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+101 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+102 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+103 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+104 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+105 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+106 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+107 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+108 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+109 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+110 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+111 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+112 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+113 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+114 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+115 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+116 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+117 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+118 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+119 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+120 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+121 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+122 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+123 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+124 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+125 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+126 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+127 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+128 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+129 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+130 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+131 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+132 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+133 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+134 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+135 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+136 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+137 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+138 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+139 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+140 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+141 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+142 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+143 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+144 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+145 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+146 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+147 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+148 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+149 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+150 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+151 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+152 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+153 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+154 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+155 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+156 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+157 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+158 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+159 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+160 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+161 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+162 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+163 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+164 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+165 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+166 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+167 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+168 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+169 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+170 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+171 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+172 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+173 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+174 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+175 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+176 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+177 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+178 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+179 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+180 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+181 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+182 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+183 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+184 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+185 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+186 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+187 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+188 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+189 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+190 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+191 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+192 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+193 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+194 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+195 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+196 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+197 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+198 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+199 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+200 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+201 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+202 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+203 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+204 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+205 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+206 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+207 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+208 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+209 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+210 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+211 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+212 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+213 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+214 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+215 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+216 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+217 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+218 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+219 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+220 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+221 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+222 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+223 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+224 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+225 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+226 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+227 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+228 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+229 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+230 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+231 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+232 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+233 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+234 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+235 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+236 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+237 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+238 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+239 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+240 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+241 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+242 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+243 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+244 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+245 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+246 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+247 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+248 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+249 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+250 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+251 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+252 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+253 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+254 }, + { AF_NS, SOCK_DGRAM, NSPROTO_IPX+255 } +}; +int dgram_num_triples = 256; +int dgram_table_size = sizeof(dgram_triples); diff --git a/private/ntos/tdi/isn/sockhelp/wshisn.def b/private/ntos/tdi/isn/sockhelp/wshisn.def new file mode 100644 index 000000000..c9eecc2a4 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/wshisn.def @@ -0,0 +1,21 @@ +LIBRARY WSHISN + +DESCRIPTION 'ISN Windows NT Sockets Helper DLL' + +CODE PRELOAD FIXED +DATA PRELOAD FIXED SINGLE + +HEAPSIZE 1024 +STACKSIZE 16384 + +EXPORTS + WSHGetSockaddrType + WSHGetSocketInformation + WSHGetWinsockMapping + WSHNotify + WSHOpenSocket + WSHSetSocketInformation + WSHGetWildcardSockaddr + WSHEnumProtocols + WSHGetProviderGuid + diff --git a/private/ntos/tdi/isn/sockhelp/wshisn.rc b/private/ntos/tdi/isn/sockhelp/wshisn.rc new file mode 100644 index 000000000..e58fcaaf7 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/wshisn.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLINK2 Socket Helper DLL" +#define VER_INTERNALNAME_STR "wshisn.DLL" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/sockhelp/wshutil.c b/private/ntos/tdi/isn/sockhelp/wshutil.c new file mode 100644 index 000000000..4559bd6d0 --- /dev/null +++ b/private/ntos/tdi/isn/sockhelp/wshutil.c @@ -0,0 +1,189 @@ +/**************************************************************************** +* (c) Copyright 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX WinSock Helper DLL for Windows NT +* +* Module: ipx/sockhelp/wshutil.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +* +***************************************************************************** +* +* Functional Description: +* +****************************************************************************/ +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/*page******************************************************* + d o _ t d i _ a c t i o n + + Generate a TDI_ACTION down to the streams + driver. + + Arguments - fd = Handle to send on + cmd = Command to send down + optbuf = Ptr to options buffer + optlen = Ptr to options length + addrflag = TRUE = This is for DG/STREAM socket on addr handle + FALSE = This is for conn handle + + Returns - A WinSock error code (NO_ERROR = OK) +************************************************************/ +INT do_tdi_action(HANDLE fd, ULONG cmd, PUCHAR optbuf, INT optlen, BOOLEAN addrflag, PHANDLE eventhandle OPTIONAL) +{ + NTSTATUS status; + PSTREAMS_TDI_ACTION tdibuf; + ULONG tdilen; + IO_STATUS_BLOCK iostat; + HANDLE event; + + + /** If the eventhandle is passed, it also means that the **/ + /** NWLINK_ACTION header is pre-allocated in the buffer, **/ + /** although we still have to fill the header in here. **/ + + if (eventhandle == NULL) { + + /** Get the length of the buffer we need to allocate **/ + + tdilen = FIELD_OFFSET(STREAMS_TDI_ACTION,Buffer) + sizeof(ULONG) + optlen; + + /** Allocate a buffer to use for the action **/ + + tdibuf = RtlAllocateHeap(RtlProcessHeap(), 0, tdilen); + if (tdibuf == NULL) { + return WSAENOBUFS; + } + + } else { + + tdilen = optlen; + tdibuf = (PSTREAMS_TDI_ACTION)optbuf; + + } + + /** Set the datagram option **/ + + RtlMoveMemory(&tdibuf->Header.TransportId, "MISN", 4); + tdibuf->DatagramOption = addrflag; + + /** + Fill out the buffer, the buffer looks like this: + + ULONG cmd + data passed. + **/ + + memcpy(tdibuf->Buffer, &cmd, sizeof(ULONG)); + + if (eventhandle == NULL) { + + tdibuf->BufferLength = sizeof(ULONG) + optlen; + + RtlMoveMemory(tdibuf->Buffer + sizeof(ULONG), optbuf, optlen); + + /** Create an event to wait on **/ + + status = NtCreateEvent( + &event, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE); + + /** If no event - then return error **/ + + if (!NT_SUCCESS(status)) { + RtlFreeHeap(RtlProcessHeap(), 0, tdibuf); + return WSAENOBUFS; + } + + } else { + + tdibuf->BufferLength = sizeof(ULONG) + optlen - FIELD_OFFSET (NWLINK_ACTION, Data[0]); + + /** Use the event handle passed in **/ + + event = *eventhandle; + + } + + /** **/ + + status = NtDeviceIoControlFile( + fd, + event, + NULL, + NULL, + &iostat, + IOCTL_TDI_ACTION, + NULL, + 0, + tdibuf, + tdilen); + + + if (eventhandle == NULL) { + + /** If pending - wait for it to finish **/ + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject(event, FALSE, NULL); + ASSERT(status == 0); + status = iostat.Status; + } + + /** Close the event **/ + + NtClose(event); + + } + + /** If we get an error - return it **/ + + if (!NT_SUCCESS(status)) { + if (eventhandle == NULL) { + RtlFreeHeap(RtlProcessHeap(), 0, tdibuf); + } + return WSAEINVAL; + } + + if (eventhandle == NULL) { + + /** Copy the returned back to optbuf if needed */ + + if (optlen) { + RtlMoveMemory (optbuf, tdibuf->Buffer + sizeof(ULONG), optlen); + } + + RtlFreeHeap(RtlProcessHeap(), 0, tdibuf); + + } + + /** Return OK **/ + + return NO_ERROR; +} diff --git a/private/ntos/tdi/isn/spx/dirs b/private/ntos/tdi/isn/spx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isn/spx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isn/spx/globals.c b/private/ntos/tdi/isn/spx/globals.c new file mode 100644 index 000000000..51fc80803 --- /dev/null +++ b/private/ntos/tdi/isn/spx/globals.c @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Global values +PDEVICE SpxDevice = NULL; +UNICODE_STRING IpxDeviceName = {0}; +HANDLE IpxHandle = NULL; + +LARGE_INTEGER Magic100000 = { + 0x1b478424, + 0xa7c5ac47 + }; +// Line info +IPX_LINE_INFO IpxLineInfo = {0}; +USHORT IpxMacHdrNeeded = 0; +USHORT IpxInclHdrOffset= 0; + +// Entry Points into the IPX stack +IPX_INTERNAL_SEND IpxSendPacket = NULL; +IPX_INTERNAL_FIND_ROUTE IpxFindRoute = NULL; +IPX_INTERNAL_QUERY IpxQuery = NULL; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData = NULL; + +#if DBG +ULONG SpxDebugDump = 0; +LONG SpxDumpInterval = DBG_DUMP_DEF_INTERVAL; +ULONG SpxDebugLevel = DBG_LEVEL_ERR; +ULONG SpxDebugSystems = DBG_COMP_MOST; +#endif + +// Unload event triggered when ref count on device goes to zero. +KEVENT SpxUnloadEvent = {0}; + +// Maximum packet size quanta used during packet size negotiation +ULONG SpxMaxPktSize[] = { + 576 - MIN_IPXSPX2_HDRSIZE, + 1024 - MIN_IPXSPX2_HDRSIZE, + 1474 - MIN_IPXSPX2_HDRSIZE, + 1492 - MIN_IPXSPX2_HDRSIZE, + 1500 - MIN_IPXSPX2_HDRSIZE, + 1954 - MIN_IPXSPX2_HDRSIZE, + 4002 - MIN_IPXSPX2_HDRSIZE, + 8192 - MIN_IPXSPX2_HDRSIZE, + 17314 - MIN_IPXSPX2_HDRSIZE, + 65535 - MIN_IPXSPX2_HDRSIZE + }; + +ULONG SpxMaxPktSizeIndex = sizeof(SpxMaxPktSize)/sizeof(ULONG); + + +// Global interlock +CTELock SpxGlobalInterlock = {0}; + +// Another one, used only for global queues for addr/conn +CTELock SpxGlobalQInterlock = {0}; +PSPX_CONN_FILE SpxGlobalConnList = NULL; +PSPX_ADDR_FILE SpxGlobalAddrList = NULL; + +SPX_CONNFILE_LIST SpxPktConnList = {NULL, NULL}; +SPX_CONNFILE_LIST SpxRecvConnList = {NULL, NULL}; + +// Timer globals +LONG SpxTimerCurrentTime = 0; diff --git a/private/ntos/tdi/isn/spx/h/fwddecls.h b/private/ntos/tdi/isn/spx/h/fwddecls.h new file mode 100644 index 000000000..feda4e76b --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/fwddecls.h @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + fwddecls.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +struct _SPX_ADDR ; +struct _SPX_ADDR_FILE ; +struct _SPX_CONN_FILE ; +struct _SPX_SEND_RESD ; diff --git a/private/ntos/tdi/isn/spx/h/globals.h b/private/ntos/tdi/isn/spx/h/globals.h new file mode 100644 index 000000000..e4fcf39a8 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/globals.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +extern PDEVICE SpxDevice; +extern UNICODE_STRING IpxDeviceName; +extern HANDLE IpxHandle; + +extern LARGE_INTEGER Magic100000; + +#if 1 // DBG +extern ULONG SpxDebugDump; +extern LONG SpxDumpInterval; +extern ULONG SpxDebugLevel; +extern ULONG SpxDebugSystems; + +#endif + +// More IPX info. +extern IPX_LINE_INFO IpxLineInfo; +extern USHORT IpxMacHdrNeeded; +extern USHORT IpxInclHdrOffset; + +// Entry Points into the IPX stack +extern IPX_INTERNAL_SEND IpxSendPacket; +extern IPX_INTERNAL_FIND_ROUTE IpxFindRoute; +extern IPX_INTERNAL_QUERY IpxQuery; +extern IPX_INTERNAL_TRANSFER_DATA IpxTransferData; + +// Unload event +extern KEVENT SpxUnloadEvent; + +extern ULONG SpxMaxPktSize[]; +extern ULONG SpxMaxPktSizeIndex; + +extern CTELock SpxGlobalInterlock; + + +extern CTELock SpxGlobalQInterlock; +extern PSPX_CONN_FILE SpxGlobalConnList; +extern PSPX_ADDR_FILE SpxGlobalAddrList; + +extern SPX_CONNFILE_LIST SpxPktConnList; +extern SPX_CONNFILE_LIST SpxRecvConnList; + +extern LONG SpxTimerCurrentTime; diff --git a/private/ntos/tdi/isn/spx/h/isnspx.h b/private/ntos/tdi/isn/spx/h/isnspx.h new file mode 100644 index 000000000..6080b0423 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/isnspx.h @@ -0,0 +1,363 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnspx.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include +#include +#include +#ifndef CTE_TYPEDEFS_DEFINED +#include +#endif +#include + +#include "wsnwlink.h" + +#define SPX_DEVICE_SIGNATURE (USHORT)(*(PUSHORT)"SD") +#define SPX_ADDRESS_SIGNATURE (USHORT)(*(PUSHORT)"AD") +#define SPX_ADDRESSFILE_SIGNATURE (USHORT)(*(PUSHORT)"AF") +#define SPX_CONNFILE_SIGNATURE (USHORT)(*(PUSHORT)"CF") + +#define SPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + +#define SPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +typedef UCHAR BYTE, *PBYTE; +typedef ULONG DWORD, *PDWORD; + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + +// +// PREQUEST +// SpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define SpxAllocateRequest(_Device,_Irp) \ + (_Irp) + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// SpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define SpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_TDI_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the TDI buffer chain associated with a request. +// + +#define REQUEST_TDI_BUFFER(_Request) \ + ((PVOID)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// PUNICODE_STRING +// REQUEST_OPEN_NAME( +// IN PREQUEST Request +// ); +// +// Used to access the RemainingName field of a request. +// + +#define REQUEST_OPEN_NAME(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->FileObject->FileName)) + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// SpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define SpxCompleteRequest(_Request) \ + { \ + CTELockHandle _CancelIrql; \ + DBGPRINT(TDI, INFO, \ + ("SpxCompleteRequest: Completing %lx with %lx\n", \ + (_Request), REQUEST_STATUS(_Request))); \ + \ + IoAcquireCancelSpinLock( &_CancelIrql ); \ + (_Request)->CancelRoutine = NULL; \ + IoReleaseCancelSpinLock( _CancelIrql ); \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT); \ + } + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +#include "fwddecls.h" + +// BUGBUG: This should go in ntddk.h? +#ifndef _NTIOAPI_ +#include "spxntdef.h" +#endif + +#include "spxreg.h" +#include "spxdev.h" +#include "spxbind.h" +#include "spxtimer.h" +#include "spxpkt.h" +#include "spxerror.h" +#include "spxaddr.h" +#include "spxconn.h" +#include "spxrecv.h" +#include "spxsend.h" +#include "spxquery.h" +#include "spxmem.h" +#include "spxutils.h" + + +// Globals +#include "globals.h" + + + + diff --git a/private/ntos/tdi/isn/spx/h/spxaddr.h b/private/ntos/tdi/isn/spx/h/spxaddr.h new file mode 100644 index 000000000..b49a4791e --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxaddr.h @@ -0,0 +1,426 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.h + +Abstract: + + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define DYNSKT_RANGE_START 0x4000 +#define DYNSKT_RANGE_END 0x7FFF +#define SOCKET_UNIQUENESS 1 + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. + +#define AFREF_CREATE 0 +#define AFREF_VERIFY 1 +#define AFREF_INDICATION 2 +#define AFREF_CONN_ASSOC 3 + +#define AFREF_TOTAL 4 + +typedef struct _SPX_ADDR_FILE { + +#if DBG + ULONG saf_RefTypes[AFREF_TOTAL]; +#endif + + CSHORT saf_Type; + CSHORT saf_Size; + + // number of references to this object. + ULONG saf_RefCount; + + // Linkage in address list. + struct _SPX_ADDR_FILE * saf_Next; + struct _SPX_ADDR_FILE * saf_GlobalNext; + + // List of associated connection/active or otherwise + struct _SPX_CONN_FILE * saf_AssocConnList; + + // the current state of the address file structure; this is either open or + // closing + USHORT saf_Flags; + + // address to which we are bound, pointer to its lock. + struct _SPX_ADDR * saf_Addr; + CTELock * saf_AddrLock; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT saf_FileObject; +#endif + + // device to which we are attached. + struct _DEVICE * saf_Device; + + // This holds the request used to close this address file, + // for pended completion. + PREQUEST saf_CloseReq; + + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + PTDI_IND_CONNECT saf_ConnHandler; + PVOID saf_ConnHandlerCtx; + + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. + PTDI_IND_DISCONNECT saf_DiscHandler; + PVOID saf_DiscHandlerCtx; + + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. + PTDI_IND_RECEIVE saf_RecvHandler; + PVOID saf_RecvHandlerCtx; + + // Send possible handler + PTDI_IND_SEND_POSSIBLE saf_SendPossibleHandler; + PVOID saf_SendPossibleHandlerCtx; + + // !!!We do not do datagrams or expedited data!!! + + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. + PTDI_IND_ERROR saf_ErrHandler; + PVOID saf_ErrHandlerCtx; + PVOID saf_ErrHandlerOwner; + + +} SPX_ADDR_FILE, *PSPX_ADDR_FILE; + +#define SPX_ADDRFILE_OPENING 0x0000 // not yet open for business +#define SPX_ADDRFILE_OPEN 0x0001 // open for business +#define SPX_ADDRFILE_CLOSING 0x0002 // closing +#define SPX_ADDRFILE_STREAM 0x0004 // Opened for stream mode operation +#define SPX_ADDRFILE_CONNIND 0x0008 // Connect ind in progress +#define SPX_ADDRFILE_SPX2 0x0010 // Attempt SPX2 address file +#define SPX_ADDRFILE_NOACKWAIT 0x0020 // Dont delay acks on assoc connections +#define SPX_ADDRFILE_IPXHDR 0x0040 // Pass ipx hdr on all assoc connections +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** +// If you are adding any more states to this beyond 0x0080, MAKE SURE to go +// in code and change statements like (Flags & SPX_***) to +// ((Flags & SPX_**) != 0)!!! I dont want to make that change that at this stage. +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** + +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. + +#define AREF_ADDR_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _SPX_ADDR { + +#if DBG + ULONG sa_RefTypes[AREF_TOTAL]; +#endif + + USHORT sa_Size; + CSHORT sa_Type; + + // number of references to this object. + ULONG sa_RefCount; + + // next address/this device object. + struct _SPX_ADDR * sa_Next; + + // The following fields are used to maintain state about this address. + // attributes of the address. + ULONG sa_Flags; + + // Next addressfile for this address + struct _SPX_ADDR_FILE * sa_AddrFileList; + + // List of inactive connections and active connections on this address file. + struct _SPX_CONN_FILE * sa_InactiveConnList; + struct _SPX_CONN_FILE * sa_ActiveConnList; + + // This is the list of connections which have a POST_LISTEN on them. They + // do not have a local connection id at this point. But will, when they move + // from here to the ActiveConnList, when the listen is satisfied (no matter + // if the accept has not been posted yet, in the case of non-autoaccept listens) + struct _SPX_CONN_FILE * sa_ListenConnList; + + CTELock sa_Lock; + + // the socket this address corresponds to. + USHORT sa_Socket; + + // device context to which we are attached. + struct _DEVICE * sa_Device; + CTELock * sa_DeviceLock; + +#ifdef ISN_NT + + // These two can be a union because they are not used + // concurrently. + union { + + // This structure is used for checking share access. + SHARE_ACCESS sa_ShareAccess; + + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + WORK_QUEUE_ITEM sa_DestroyAddrQueueItem; + + } u; + + // This structure is used to hold ACLs on the address. + PSECURITY_DESCRIPTOR sa_SecurityDescriptor; + +#endif + +} SPX_ADDR, *PSPX_ADDR; + +#define SPX_ADDR_CLOSING 0x00000001 + + +// ROUTINE PROTOTYPES + +VOID +SpxAddrRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address); + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile); + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile); + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest); + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile); + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address); + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request); + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT struct _SPX_CONN_FILE **ppSpxConnFile); + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrDestroy( + IN PVOID Parameter); + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device); + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +#if DBG +#define SpxAddrReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type],\ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrRef (_Address); \ + } + +#define SpxAddrLockReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrLockRef (_Address); \ + } + +#define SpxAddrDereference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + }\ + } + + +#define SpxAddrFileReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileRef (_AddressFile); \ + } + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileLockRef (_AddressFile); \ + } + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxAddrFileDeref (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxAddrReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock) + +#define SpxAddrLockReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock); + +#define SpxAddrDereference(_Address, _Type) \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + } + +#define SpxAddrFileReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock) + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock); + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + if (SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + (ULONG)-1, \ + (_AddressFile)->saf_AddrLock) == 1) { \ + SpxAddrFileDestroy (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) + +#endif // DBG diff --git a/private/ntos/tdi/isn/spx/h/spxbind.h b/private/ntos/tdi/isn/spx/h/spxbind.h new file mode 100644 index 000000000..81ad6ac58 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxbind.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +NTSTATUS +SpxInitBindToIpx( + VOID); + +VOID +SpxUnbindFromIpx( + VOID); + diff --git a/private/ntos/tdi/isn/spx/h/spxconn.h b/private/ntos/tdi/isn/spx/h/spxconn.h new file mode 100644 index 000000000..bb1173432 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxconn.h @@ -0,0 +1,1666 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +// Minimum value for RTT in ms. +// BUGBUG: Have these be a derivate of registry values. +#define SPX_T1_MIN 200 +#define MAX_RETRY_DELAY 5000 // 5 seconds +#define SPX_DEF_RENEG_RETRYCOUNT 1 // All reneg pkts except min sent once + +// Some types +typedef enum +{ + SPX_CALL_RECVLEVEL, + SPX_CALL_TDILEVEL +} SPX_CALL_LEVEL; + +typedef enum +{ + SPX_REQ_DATA, + SPX_REQ_ORDREL, + SPX_REQ_DISC + +} SPX_SENDREQ_TYPE; + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Connection. + +#define CFREF_CREATE 0 +#define CFREF_VERIFY 1 +#define CFREF_INDICATION 2 +#define CFREF_BYCTX 3 +#define CFREF_BYID 4 +#define CFREF_ADDR 5 +#define CFREF_REQ 6 +#define CFREF_TIMER 7 +#define CFREF_PKTIZE 8 +#define CFREF_RECV 9 +#define CFREF_ABORTPKT 10 +#define CFREF_ERRORSTATE 11 +#define CFREF_FINDROUTE 12 + +// +// New state added to reflect an SPXI connection which is waiting for +// a local disconnect after having indicated a RELEASE to AFD. +// +#define CFREF_DISCWAITSPX 13 + +#define CFREF_TOTAL 14 + +#define CFMAX_STATES 20 + +typedef struct _SPX_CONN_FILE +{ + +#if DBG + ULONG scf_RefTypes[CFREF_TOTAL]; + +#if 0 +// +// Disabled for now - to enable logging of states, move this array *after* the Type/Size; +// a change in their offset can cause problems since we assume the offset to be less than +// the size of an AddressFile structure. (see SpxTdiQueryInformation) +// + ULONG scf_StateBuffer[CFMAX_STATES]; + ULONG scf_NextStatePtr; +#endif + +#endif + + CSHORT scf_Type; + CSHORT scf_Size; + + // number of references to this object. + ULONG scf_RefCount; + + // Linkage in device address file list. The connection can be on the device + // connection list, address inactive/listen/active list. + struct _SPX_CONN_FILE * scf_Next; + struct _SPX_CONN_FILE * scf_AssocNext; + struct _SPX_CONN_FILE * scf_GlobalActiveNext; + + // Queued in a global list, stays here from creation to destroy. + struct _SPX_CONN_FILE * scf_GlobalNext; + struct _SPX_CONN_FILE * scf_PktNext; + struct _SPX_CONN_FILE * scf_ProcessRecvNext; + + // the current state of the connection. One main state and multiple substates. + ULONG scf_Flags; + + // More information + ULONG scf_Flags2; + +#if DBG + // Save the state of flags/flags2 before reinit. Overwritten every reinit. + ULONG scf_GhostFlags; + ULONG scf_GhostFlags2; + ULONG scf_GhostRefCount; + PREQUEST scf_GhostDiscReq; +#endif + + // Connection retry counts, or watchdog timer count when the connection goes + // active + union + { + LONG scf_CRetryCount; + LONG scf_WRetryCount; + }; + LONG scf_RRetryCount; + USHORT scf_RRetrySeqNum; + + union + { + ULONG scf_CTimerId; + ULONG scf_RTimerId; // Only after we turn active + }; + + ULONG scf_WTimerId; // Watchdog timer + ULONG scf_TTimerId; // TDI Connect/Disconnect timer + ULONG scf_ATimerId; // Ack timer id + + // Variables used to manage the Retry timer tick value + // Note our timer subsytem fires at 100ms granularity. + int scf_BaseT1; + int scf_AveT1; + int scf_DevT1; + + // Stored in HOST-ORDER + // LOCAL variables + USHORT scf_LocalConnId; + USHORT scf_SendSeqNum; // Debug dw +9a + USHORT scf_SentAllocNum; // dw +9c + + // REMOTE variables + USHORT scf_RecvSeqNum; // dw +9e + USHORT scf_RecdAckNum; // dw +a0 + USHORT scf_RecdAllocNum; // dw +a2 + + // RETRY sequence number + USHORT scf_RetrySeqNum; + + // Saved ack number to be used in building the reneg ack packet. + // Note that our RecvSeqNum which we normally use is overwritten + // when we receive a renegotiate request. + USHORT scf_RenegAckAckNum; + + // Stored in NETWORK-ORDER. scf_RemAckAddr contains the remote address + // for a data packet that had the ack bit set, buildAck will use this + // address. + BYTE scf_RemAddr[12]; + BYTE scf_RemAckAddr[12]; + USHORT scf_RemConnId; // Debug dw +be + + // Maximum packet size (or size of first) reneg packet. + USHORT scf_RenegMaxPktSize; + + // Local target to use in when sending acks. This is set to received + // data's indicated local target. + IPX_LOCAL_TARGET scf_AckLocalTarget; + + // Maximum packet size to use for this connection + USHORT scf_MaxPktSize; + UCHAR scf_DataType; + + // Local target to use in sends, initialized upon connect indication + // or when find_route completes + IPX_LOCAL_TARGET scf_LocalTarget; + + // Connection lock + CTELock scf_Lock; + + // address to which we are bound + struct _SPX_ADDR_FILE * scf_AddrFile; + + // Connection context + CONNECTION_CONTEXT scf_ConnCtx; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT scf_FileObject; +#endif + + // LIST_ENTRY of disconnect irps waiting for completion. There could be + // multiple disconnect inform irps. + LIST_ENTRY scf_DiscLinkage; + + // LIST_ENTRY of send requests (intially contains connect/listen/accept also) + // on this connection. + LIST_ENTRY scf_ReqLinkage; + + // Queue for completed requests awaiting completion + LIST_ENTRY scf_ReqDoneLinkage; + LIST_ENTRY scf_RecvDoneLinkage; + + // Queue for pending receives + LIST_ENTRY scf_RecvLinkage; + PREQUEST scf_CurRecvReq; + ULONG scf_CurRecvOffset; + ULONG scf_CurRecvSize; + + // Current request packetize info + PREQUEST scf_ReqPkt; + ULONG scf_ReqPktOffset; + ULONG scf_ReqPktSize; + ULONG scf_ReqPktFlags; + SPX_SENDREQ_TYPE scf_ReqPktType; + + // Single linked list of sequenced send/disc packets + PSPX_SEND_RESD scf_SendSeqListHead; + PSPX_SEND_RESD scf_SendSeqListTail; + + // Single linked list of send (unsequenced) packets + PSPX_SEND_RESD scf_SendListHead; + PSPX_SEND_RESD scf_SendListTail; + + // Single linked list of buffered recv packets. + PSPX_RECV_RESD scf_RecvListHead; + PSPX_RECV_RESD scf_RecvListTail; + + // Connect request + PREQUEST scf_ConnectReq; + + // This holds the request used to close this address file, + // for pended completion. We also pend cleanup requests for connections. + PREQUEST scf_CleanupReq; + PREQUEST scf_CloseReq; + +#if DBG + + // Packet being indicated, seq num, flags/flags2 + USHORT scf_PktSeqNum; + ULONG scf_PktFlags; + ULONG scf_PktFlags2; + + ULONG scf_IndBytes; + ULONG scf_IndLine; +#endif + +#if DBG_WDW_CLOSE + + // Keep track of how long the window was closed on this connection. + ULONG scf_WdwCloseAve; + LARGE_INTEGER scf_WdwCloseTime; // Time when wdw was closed +#endif + + // device to which we are attached. + struct _DEVICE * scf_Device; + +} SPX_CONN_FILE, *PSPX_CONN_FILE; + + +// Basic states +// Least significant byte of flags is used. +// Mutually exclusive states are coded as numbers, others are bit flags. +// Only main states are currently in form of numbers. Also, send and receive. +// +// Once we go active, we need SEND/RECEIVE/DISC substates to be mutually +// exclusive with each other. As all three could be active at the same time. + +// Connection MAIN states. These are all mutually exclusive. +#define SPX_CONNFILE_MAINMASK 0x00000007 +#define SPX_CONNFILE_ACTIVE 0x00000001 +#define SPX_CONNFILE_CONNECTING 0x00000002 +#define SPX_CONNFILE_LISTENING 0x00000003 +#define SPX_CONNFILE_DISCONN 0x00000004 + +// Connecting states (VALID when CONNFILE_CONNECTING) +#define SPX_CONNECT_MASK 0x000000F0 +#define SPX_CONNECT_SENTREQ 0x00000010 +#define SPX_CONNECT_NEG 0x00000020 +#define SPX_CONNECT_W_SETUP 0x00000030 + +// Listening states (VALID when CONNFILE_LISTENING) +#define SPX_LISTEN_MASK 0x000000F0 +#define SPX_LISTEN_RECDREQ 0x00000010 +#define SPX_LISTEN_SENTACK 0x00000020 +#define SPX_LISTEN_NEGACK 0x00000030 +#define SPX_LISTEN_SETUP 0x00000040 + +// Connection SUB states +// Send machine states (VALID when CONNFILE_ACTIVE) +#define SPX_SEND_MASK 0x000000F0 +#define SPX_SEND_IDLE 0x00000000 +#define SPX_SEND_PACKETIZE 0x00000010 +#define SPX_SEND_RETRY 0x00000020 +#define SPX_SEND_RETRYWD 0x00000030 +#define SPX_SEND_RENEG 0x00000040 +#define SPX_SEND_RETRY2 0x00000050 +#define SPX_SEND_RETRY3 0x00000060 +#define SPX_SEND_WD 0x00000070 // We dont reneg pkt size on wdog + // Also we change to this state only + // 2nd time wdog fires w/out ack. +#define SPX_SEND_NAK_RECD 0x00000080 + +// Receive machine states (VALID when CONNFILE_ACTIVE) +#define SPX_RECV_MASK 0x00000F00 +#define SPX_RECV_IDLE 0x00000000 +#define SPX_RECV_POSTED 0x00000100 +#define SPX_RECV_PROCESS_PKTS 0x00000200 + +// Disconnect states (VALID when CONNFILE_DISCONN/CONNFILE_ACTIVE) +// These are valid when either ACTIVE/DISCONN is set. We use these when +// active for a orderly release, i.e. we receive pkt from remote, but we +// stay active (setting SPX_DISC_RECV_ORDREL) until our client posts a +// disconnect, which is when we move to disconnecting. +#define SPX_DISC_MASK 0x0000F000 +#define SPX_DISC_IDLE 0x00000000 +#define SPX_DISC_ABORT 0x00001000 +#define SPX_DISC_SENT_IDISC 0x00002000 +#define SPX_DISC_POST_ORDREL 0x00003000 +#define SPX_DISC_SENT_ORDREL 0x00004000 +#define SPX_DISC_ORDREL_ACKED 0x00005000 +#define SPX_DISC_POST_IDISC 0x00006000 + +// [SA] bug #14655 added flag to indicate that SpxConnInactivate already called for +// this disconnecting connection +// +#define SPX_DISC_INACTIVATED 0x00007000 + +// The following are not mutually exclusive. +#define SPX_CONNFILE_RECVQ 0x00010000 // Process completed receives/pkts +#define SPX_CONNFILE_RENEG_SIZE 0x00020000 // Size changed in renegotiate pkt +#define SPX_CONNFILE_ACKQ 0x00040000 // Waiting to piggyback ack queue +#define SPX_CONNFILE_PKTQ 0x00080000 // Waiting to packetize queue + +#define SPX_CONNFILE_ASSOC 0x00100000 // associated +#define SPX_CONNFILE_NEG 0x00200000 // CR had neg set (for delayed accept) +#define SPX_CONNFILE_SPX2 0x00400000 +#define SPX_CONNFILE_STREAM 0x00800000 +#define SPX_CONNFILE_R_TIMER 0x01000000 // Retry timer (only after ACTIVE) +#define SPX_CONNFILE_C_TIMER 0x01000000 // Connect timer +#define SPX_CONNFILE_W_TIMER 0x02000000 // Watchdog timer +#define SPX_CONNFILE_T_TIMER 0x04000000 // tdi connect/disc timer specified +#define SPX_CONNFILE_RENEG_PKT 0x08000000 // Renegotiate changed size, repacketize +#define SPX_CONNFILE_IND_IDISC 0x10000000 // Indicated abortive disc to afd +#define SPX_CONNFILE_IND_ODISC 0x20000000 // Indicated orderly release to afd + +#define SPX_CONNFILE_STOPPING 0x40000000 +#define SPX_CONNFILE_CLOSING 0x80000000 // closing + +#define SPX_CONNFILE2_PKT_NOIND 0x00000001 +#define SPX_CONNFILE2_RENEGRECD 0x00000002 // A renegotiate was received. + // scf_RenegAckAckNum set. +#define SPX_CONNFILE2_PKT 0x00000004 +#define SPX_CONNFILE2_FINDROUTE 0x00000010 // A find route in progress on conn. +#define SPX_CONNFILE2_NOACKWAIT 0x00000020 // Dont delay acks on connection, option +#define SPX_CONNFILE2_IMMED_ACK 0x00000040 // Send an immediate ack,no back traffic +#define SPX_CONNFILE2_IPXHDR 0x00000080 // Pass ipxhdr in receives + +// +// [SA] Saves the IDisc flag passed to AbortiveDisc; this is TRUE only if there was +// a remote disconnect on an SPX connection (in which case, we indicate TDI_DISCONNECT_RELEASE +// else we indicate TDI_DISCONNECT_ABORT) +// +#define SPX_CONNFILE2_IDISC 0x00000100 + +// +// Indicates an SPXI connfile waiting for a local disconnect in response +// to a TDI_DISCONNECT_RELEASE to AFD. +// +#define SPX_CONNFILE2_DISC_WAIT 0x00000200 + +// FindRoute request structure +typedef struct _SPX_FIND_ROUTE_REQUEST +{ + // !!!!This must be the first element in the structure + IPX_FIND_ROUTE_REQUEST fr_FindRouteReq; + PVOID fr_Ctx; + +} SPX_FIND_ROUTE_REQUEST, *PSPX_FIND_ROUTE_REQUEST; + +typedef struct _SPX_CONNFILE_LIST +{ + PSPX_CONN_FILE pcl_Head; + PSPX_CONN_FILE pcl_Tail; + +} SPX_CONNFILE_LIST, *PSPX_CONNFILE_LIST; + +// Exported routines + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT pConnCtx, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT NTSTATUS * pStatus); + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile); + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn); + +#if DBG +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile); +#endif + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus); + +BOOLEAN +SpxConnDequeuePktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pAckHdr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +// LOCAL functions +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr); + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile); + +USHORT +spxConnGetId( + VOID); + +VOID +spxConnInsertIntoActiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoInactiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoListenList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile); + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt); + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt); + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize); + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size); + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest); + +NTSTATUS +spxConnProcessRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnProcessIndData( + IN PSPX_CONN_FILE pSpxConnFile, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnOrderlyDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnInformedDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN Flag); // [SA] Bug #15249 + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +// +// MACROS +// +#define SHIFT100000 16 + +#define SPX_CONVERT100NSTOCENTISEC(Li) \ + RtlExtendedMagicDivide((Li), Magic100000, SHIFT100000) + +#define UNSIGNED_BETWEEN_WITH_WRAP(Low, High, Target) \ + ((Low <= High) ? ((Target >= Low) && (Target <= High)) : \ + ((Target >= Low) || (Target <= High))) + +// This is with the assumption that the window size will never be greater +// than the difference of 0x8000 and 0x1000. If High is < 1000 and Low +// is > 8000 then we can assume a wrap happened. Otherwise, we assume no +// wrap and do a straight compare. +#define MAX_WINDOW_SIZE 0x6000 +#define DEFAULT_WINDOW_SIZE 8 + +#define UNSIGNED_GREATER_WITH_WRAP(High, Low) \ + (((High < 0x1000) && (Low > 0x8000)) ? TRUE : (High > Low)) + +#define SPX_SET_ACKNUM(pSpxConnFile, RecdAckNum, RecdAllocNum) \ + { \ + DBGPRINT(SEND, DBG, \ + ("SPX_SET_ACKNUM: %lx.%lx = %lx.%lx (%s.%d)\n", \ + (RecdAckNum), (RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAllocNum), \ + __FILE__, __LINE__)); \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAckNum))) \ + { \ + (pSpxConnFile)->scf_RecdAckNum = (RecdAckNum); \ + } \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAllocNum)))\ + { \ + (pSpxConnFile)->scf_RecdAllocNum = (RecdAllocNum); \ + } \ + } + +#define BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum) \ + { \ + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT); \ + } + +#define END_PROCESS_PACKET(pSpxConnFile, fBuffered, fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, \ + (SPX_CONNFILE2_PKT |SPX_CONNFILE2_RENEGRECD)); \ + if (fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); \ + SPX_SET_RECVNUM(pSpxConnFile, fBuffered); \ + } \ + } + +#define INCREMENT_WINDOW(pSpxConnFile) \ + ((pSpxConnFile)->scf_SentAllocNum++) + +#define ADD_TO_WINDOW(pSpxConnFile, numPkts) \ + ((pSpxConnFile)->scf_SentAllocNum += (numPkts)) + +#if DBG_WDW_CLOSE +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + \ + if (fBuffered && \ + (UNSIGNED_GREATER_WITH_WRAP( \ + (pSpxConnFile)->scf_RecvSeqNum, \ + (pSpxConnFile)->scf_SentAllocNum))) \ + { \ + KeQuerySystemTime( \ + (PLARGE_INTEGER)&pSpxConnFile->scf_WdwCloseTime); \ + } \ + } +#else +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + } +#endif + + +#define SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest) \ + { \ + RemoveEntryList(REQUEST_LINKAGE((pRequest))); \ + pSpxConnFile->scf_CurRecvReq = NULL; \ + pSpxConnFile->scf_CurRecvOffset = 0; \ + pSpxConnFile->scf_CurRecvSize = 0; \ + if (!IsListEmpty(&(pSpxConnFile)->scf_RecvLinkage)) \ + { \ + PTDI_REQUEST_KERNEL_RECEIVE _p; \ + DBGPRINT(RECEIVE, DBG, \ + ("spxConnProcessRecv: CURRECV %lx\n", pRequest)); \ + \ + (pSpxConnFile)->scf_CurRecvReq = \ + LIST_ENTRY_TO_REQUEST( \ + (pSpxConnFile)->scf_RecvLinkage.Flink); \ + \ + _p = (PTDI_REQUEST_KERNEL_RECEIVE) \ + REQUEST_PARAMETERS((pSpxConnFile)->scf_CurRecvReq); \ + \ + (pSpxConnFile)->scf_CurRecvOffset = 0; \ + (pSpxConnFile)->scf_CurRecvSize = (_p)->ReceiveLength; \ + } \ + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || \ + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) \ + { \ + SPX_RECV_SETSTATE( \ + pSpxConnFile, \ + (pSpxConnFile->scf_CurRecvReq == NULL) ? \ + SPX_RECV_IDLE : SPX_RECV_POSTED); \ + } \ + } + +#define SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ActiveConnList; \ + (pSpxAddr)->sa_ActiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_INACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_InactiveConnList; \ + (pSpxAddr)->sa_InactiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ListenConnList; \ + (pSpxAddr)->sa_ListenConnList = pSpxConnFile; \ + } + + +// +// STATE MANIPULATION +// + +#if 0 +// +// Disabled for now +// +#define SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_StateBuffer[(pSpxConnFile)->scf_NextStatePtr++] = \ + (pSpxConnFile)->scf_Flags; \ + (pSpxConnFile)->scf_NextStatePtr %= CFMAX_STATES; +#else + +#define SPX_STORE_LAST_STATE(pSpxConnFile) + +#endif + +#define SPX_MAIN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNFILE_MAINMASK) + +// #define SPX_CONN_IDLE(pSpxConnFile) \ +// ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == 0)) + +#define SPX_CONN_IDLE(pSpxConnFile) \ + ((BOOLEAN)((SPX_MAIN_STATE(pSpxConnFile) == 0) || \ + ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && \ + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)))) + +#define SPX_CONN_ACTIVE(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE)) + +#define SPX_CONN_CONNECTING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_CONNECTING)) + +#define SPX_CONN_LISTENING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_LISTENING)) + +#define SPX_CONN_DISC(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN)) + +#if DBG + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#else + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#endif + +#define SPX_CONN_FLAG(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags & (Flag)) != 0)) + +#define SPX_CONN_FLAG2(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags2 & (Flag)) != 0)) + +#if DBG + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) +#else + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) + +#endif + +#define SPX_CONN_SETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 |= (Flag)) + +#define SPX_CONN_RESETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags &= ~(Flag)) + +#define SPX_CONN_RESETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 &= ~(Flag)) + +#define SPX2_CONN(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_SPX2)) + +#define SPX_CONN_STREAM(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_CONN_MSG(pSpxConnFile) \ + (!SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_LISTEN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_LISTEN_MASK) + +#define SPX_CONNECT_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNECT_MASK) + +#define SPX_SEND_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_SEND_MASK) + +#define SPX_RECV_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_RECV_MASK) + +#define SPX_DISC_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_DISC_MASK) + +#if DBG + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } + +#else + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } +#endif //DBG +#define SpxConnQueueSendPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendListTail->sr_Next = _pSendResd; \ + (pSpxConnFile)->scf_SendListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } + +#define SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendSeqListTail->sr_Next = _pSendResd;\ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendSeqPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendSeqListHead;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } + +#define SpxConnQueueRecvPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + (pSpxConnFile)->scf_RecvListTail->rr_Next = _pRecvResd; \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } \ + } + +#define SpxConnQueueRecvPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + _pRecvResd->rr_Next = (pSpxConnFile)->scf_RecvListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd; \ + } \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } + +#if DBG +#define SpxConnFileReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileRef (_ConnFile); \ + } + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileLockRef (_ConnFile); \ + } + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxConnFileDeref (_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _l; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_l)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, _l); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxConnFileReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock) + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock); + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + SpxConnFileDeref(_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_lockHandle)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, (_lockHandle)); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) + +#endif // DBG + + +// Set the packet size. If we are spx1 or spx2 and !neg, check if we are different +// nets, set to min then, else use the size indicated by IPX. If we are spx2, just +// set it to our local max. +// +// Also always even out packet size and round down. This solves an issue with +// data size needing to be even for some novell 802.2 clients. +// +// Fix after beta2 for tokring using receive size. Only if spx2 and neg. +#if defined(_PNP_POWER) +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + &(pSpxConnFile)->scf_LocalTarget.NicHandle, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#else +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + (pSpxConnFile)->scf_LocalTarget.NicId, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#endif _PNP_POWER + +#if DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#else // DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#endif // DBG + +#define SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile) \ + { \ + if (!SPX_CONN_FLAG( \ + (pSpxConnFile), \ + SPX_CONNFILE_RECVQ)) \ + { \ + SPX_CONN_SETFLAG((pSpxConnFile), SPX_CONNFILE_RECVQ); \ + SpxConnFileLockReference(pSpxConnFile, CFREF_RECV); \ + SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile); \ + } \ + } + +#define SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile) \ + { \ + if (SpxPktConnList.pcl_Tail) \ + { \ + SpxPktConnList.pcl_Tail->scf_PktNext = pSpxConnFile; \ + SpxPktConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxPktConnList.pcl_Tail = \ + SpxPktConnList.pcl_Head = pSpxConnFile; \ + } \ + } + +#define SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile) \ + { \ + if (SpxRecvConnList.pcl_Tail) \ + { \ + SpxRecvConnList.pcl_Tail->scf_ProcessRecvNext = pSpxConnFile; \ + SpxRecvConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxRecvConnList.pcl_Tail = \ + SpxRecvConnList.pcl_Head = pSpxConnFile; \ + } \ + } + + diff --git a/private/ntos/tdi/isn/spx/h/spxdev.h b/private/ntos/tdi/isn/spx/h/spxdev.h new file mode 100644 index 000000000..30c0adae5 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxdev.h @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +// Hash buckets for SPX_ADDR done using socket number +#define NUM_SPXADDR_HASH_BUCKETS 8 +#define NUM_SPXADDR_HASH_MASK 7 +#define NUM_SPXCONN_HASH_BUCKETS 8 +#define NUM_SPXCONN_HASH_MASK 7 + +// This structure defines the per-device structure for SPX +// (one of these is allocated globally). +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_ORPHAN 4 + +#define DREF_TOTAL 5 + +typedef struct _DEVICE { + + DEVICE_OBJECT dev_DevObj; // the I/O system's device object. + +#if DBG + ULONG dev_RefTypes[DREF_TOTAL]; +#endif + + CSHORT dev_Type; // type of this structure + USHORT dev_Size; // size of this structure + +#if DBG + UCHAR dev_Signature1[4]; // contains "SPX1" +#endif + + // activity count/this provider. + LONG dev_RefCount; + UCHAR dev_State; + + // number of adapters IPX is bound to. + USHORT dev_Adapters; + + // GLOBAL lock for reference count (used in ExInterlockedXxx calls). + CTELock dev_Interlock; + CTELock dev_Lock; + + // Hash table of lists of addresses opened on this device + struct _SPX_ADDR * dev_AddrHashTable[NUM_SPXADDR_HASH_BUCKETS]; + + // List of all active connections, later this be a tree. + struct _SPX_CONN_FILE * dev_GlobalActiveConnList[NUM_SPXCONN_HASH_BUCKETS]; + USHORT dev_NextConnId; + + // Other configuration parameters. + // Where the current socket allocation is. + USHORT dev_CurrentSocket; + + // Our node and network. + UCHAR dev_Network[4]; + UCHAR dev_Node[6]; + + // Pointer to the config information from registry + PCONFIG dev_ConfigInfo; + + // Control channel identifier + ULONG dev_CcId; + + // These are kept around for error logging, and stored right + // after this structure. + PWCHAR dev_DeviceName; +#if defined(_PNP_POWER) + USHORT dev_DeviceNameLen; +#else + ULONG dev_DeviceNameLen; +#endif _PNP_POWER + +#if DBG + UCHAR dev_Signature2[4]; // contains "SPX2" +#endif + + // Handle to ndis buffer pool for spx stack. + NDIS_HANDLE dev_NdisBufferPoolHandle; + + // registration handle with tdi clients. +#if defined(_PNP_POWER) + HANDLE dev_TdiRegistrationHandle; +#endif _PNP_POWER + + // This interlock is used to guard access to the statistics + // define below. + KSPIN_LOCK dev_StatInterlock; // for ULONG quantities + KSPIN_LOCK dev_StatSpinLock; // for LARGE_INTEGER quantities + + // Counters for most of the statistics that SPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + TDI_PROVIDER_STATISTICS dev_Stat; + + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + ERESOURCE dev_AddrResource; + + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + TDI_PROVIDER_INFO dev_ProviderInfo; // information about this provider. + +} DEVICE, * PDEVICE; + +// device state definitions +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +// SPX device name +#define SPX_DEVICE_NAME L"\\Device\\NwlnkSpx" + +#define SPX_TDI_RESOURCES 9 + + +// MACROS +#if DBG + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxDerefDevice (_Device); \ + } + +#else + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + SpxDerefDevice (_Device) + +#endif + +// EXPORTED ROUTINES + +VOID +SpxDestroyDevice( + IN PDEVICE Device); + +VOID +SpxDerefDevice( + IN PDEVICE Device); + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr); diff --git a/private/ntos/tdi/isn/spx/h/spxerror.h b/private/ntos/tdi/isn/spx/h/spxerror.h new file mode 100644 index 000000000..761342512 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxerror.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxerror.h + +Abstract: + + This module contains some error definitions for spx. + +Author: + + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + +Notes: Tab stop: 4 +--*/ + +// Define the modules names for SPX - use the high bits. +#define SPXDRVR 0x00010000 +#define SPXREG 0x00020000 +#define SPXDEV 0x00030000 +#define SPXBIND 0x00040000 +#define SPXRECV 0x00050000 +#define SPXSEND 0x00060000 +#define SPXTIMER 0x00070000 +#define SPXERROR 0x00080000 +#define SPXPKT 0x00090000 +#define SPXUTILS 0x000a0000 +#define SPXCPKT 0x000b0000 +#define SPXCONN 0x000c0000 +#define SPXADDR 0x000d0000 +#define SPXCUTIL 0x000e0000 +#define SPXINIT 0x000f0000 +#define SPXMEM 0x00100000 +#define SPXQUERY 0x00200000 + + +// DEBUGGING SUPPORT: +// Debugging messages are provided per-subsystem defined here, and within +// the subsystems, there are 4 levels of messages. +// +// The four levels of debug messages are: +// +// INFO: Informational messages, eg., entry exit in routines +// DBG: Used when debugging some msgs are turned from info to dbg +// WARN: Something went wrong, but its not an error, eg., packet was not ours +// ERR: Error situations, but we can still run if a retry happens +// FATAL: In this situation, the driver is not operational + +#define DBG_LEVEL_INFO 0x4000 +#define DBG_LEVEL_DBG 0x5000 +#define DBG_LEVEL_DBG1 0x5001 +#define DBG_LEVEL_DBG2 0x5002 +#define DBG_LEVEL_DBG3 0x5003 +#define DBG_LEVEL_WARN 0x6000 +#define DBG_LEVEL_ERR 0x7000 +#define DBG_LEVEL_FATAL 0x8000 + +// SUBSYSTEMS +#define DBG_COMP_DEVICE 0x00000001 +#define DBG_COMP_CREATE 0x00000002 +#define DBG_COMP_ADDRESS 0x00000004 +#define DBG_COMP_SEND 0x00000008 +#define DBG_COMP_NDIS 0x00000010 +#define DBG_COMP_RECEIVE 0x00000020 +#define DBG_COMP_CONFIG 0x00000040 +#define DBG_COMP_PACKET 0x00000080 +#define DBG_COMP_RESOURCES 0x00000100 +#define DBG_COMP_BIND 0x00000200 +#define DBG_COMP_UNLOAD 0x00000400 +#define DBG_COMP_DUMP 0x00000800 +#define DBG_COMP_REFCOUNTS 0x00001000 +#define DBG_COMP_SYSTEM 0x00002000 +#define DBG_COMP_CRITSEC 0x00004000 +#define DBG_COMP_UTILS 0x00008000 +#define DBG_COMP_TDI 0x00010000 +#define DBG_COMP_CONNECT 0x00020000 +#define DBG_COMP_DISC 0x00040000 +#define DBG_COMP_ACTION 0x00080000 +#define DBG_COMP_STATE 0x00100000 + +#define DBG_COMP_MOST (DBG_COMP_DEVICE | \ + DBG_COMP_CREATE | \ + DBG_COMP_ADDRESS | \ + DBG_COMP_SEND | \ + DBG_COMP_NDIS | \ + DBG_COMP_RECEIVE | \ + DBG_COMP_CONFIG | \ + DBG_COMP_PACKET | \ + DBG_COMP_RESOURCES | \ + DBG_COMP_BIND | \ + DBG_COMP_UNLOAD | \ + DBG_COMP_DUMP | \ + DBG_COMP_REFCOUNTS | \ + DBG_COMP_SYSTEM | \ + DBG_COMP_CRITSEC | \ + DBG_COMP_UTILS | \ + DBG_COMP_TDI | \ + DBG_COMP_CONNECT | \ + DBG_COMP_DISC | \ + DBG_COMP_ACTION | \ + DBG_COMP_STATE) + + +// More debugging support. These values define the dumping components. +// There are a max of 32 such components that can be defined. Each of +// these are associated with a dump routine. It one is specified and +// enabled, periodically it is called. It is upto that component to +// decide what it wants to do + +#define DBG_DUMP_DEF_INTERVAL 30 // In Seconds + +// This defines the number of times an error has to happen consecutively before +// it gets logged again. +#define ERROR_CONSEQ_FREQ 200 +#define ERROR_CONSEQ_TIME (60*30) // 30 minutes + +#ifdef DBG +typedef VOID (*DUMP_ROUTINE)(VOID); + +extern +BOOLEAN +SpxDumpComponents( + IN PVOID Context); + +#endif + +// +// PROTOTYPES +// + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue); + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); + + +// +// MACROS +// + +#if DBG +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#else + +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#endif + + +#if DBG + +#define DBGPRINT(Component, Level, Fmt) \ + { \ + if (((DBG_LEVEL_ ## Level) >= SpxDebugLevel) && \ + (SpxDebugSystems & (DBG_COMP_ ## Component))) \ + { \ + DbgPrint("SPX: "); \ + DbgPrint Fmt; \ + } \ + } + +#define DBGBRK(Level) \ + { \ + if ((DBG_LEVEL_ ## Level) >= SpxDebugLevel) \ + DbgBreakPoint(); \ + } + +#define TMPLOGERR() \ + { \ + DBGPRINT(MOST, ERR, \ + ("TempErrLog: %s, Line %ld\n", __FILE__, __LINE__)); \ + } + +#else +#define DBGPRINT(Component, Level, Fmt) +#define DBGBRK(Level) +#define TMPLOGERR() +#endif + +extern +VOID +SpxWriteErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); diff --git a/private/ntos/tdi/isn/spx/h/spxmem.h b/private/ntos/tdi/isn/spx/h/spxmem.h new file mode 100644 index 000000000..717c69a6b --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxmem.h @@ -0,0 +1,142 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.h + +Abstract: + + This module contains memory management routines. + +Author: + + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +#define QWORDSIZEBLOCK(Size) (((Size)+sizeof(LARGE_INTEGER)-1) & ~(sizeof(LARGE_INTEGER)-1)) +#define SPX_MEMORY_SIGNATURE *(PULONG)"SPXM" +#define ZEROED_MEMORY_TAG 0xF0000000 +#define SPX_TAG *((PULONG)"SPX ") + +// +// Definitions for the block management package +// +typedef UCHAR BLKID; + +// Add a BLKID_xxx and an entry to atalkBlkSize for every block client +#define BLKID_TIMERLIST (BLKID)0 +#define BLKID_NDISSEND (BLKID)1 +#define BLKID_NDISRECV (BLKID)2 +#define NUM_BLKIDS (BLKID)3 + +typedef struct _BLK_CHUNK +{ + struct _BLK_CHUNK * bc_Next; // Pointer to next in the link + SHORT bc_NumFrees; // Number of free blocks in the chunk + UCHAR bc_Age; // Number of invocations since the chunk free + BLKID bc_BlkId; // Id of the block + struct _BLK_HDR * bc_FreeHead; // Head of the list of free blocks + +#ifndef SPX_OWN_PACKETS + PVOID bc_ChunkCtx; // Used to store pool header if not own + // packets +#else + PVOID bc_Padding; // Keep the header 16 bytes +#endif + + // This is followed by an array of N blks of size M such that the block header + // is exactly spxChunkSize[i] + +} BLK_CHUNK, *PBLK_CHUNK; + +typedef struct _BLK_HDR +{ + union + { + struct _BLK_HDR * bh_Next; // Valid when it is free + struct _BLK_CHUNK * bh_pChunk; // The parent chunk to which this blocks belong + // valid when it is allocated + }; + PVOID bh_Padding; // Make the header 8 bytes +} BLK_HDR, *PBLK_HDR; + +#define BC_OVERHEAD (8+8) // LARGE_INTEGER for SpxAllocMemory() header and + // POOL_HEADER for ExAllocatePool() header + +#define BLOCK_POOL_TIMER 1000 // Check interval (1 sec) +#define MAX_BLOCK_POOL_AGE 3 // # of timer invocations before free + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + + +#ifdef TRACK_MEMORY_USAGE + +#define SpxAllocateMemory(Size) SpxAllocMem((Size), FILENUM | __LINE__) + +extern +PVOID +SpxAllocMem( + IN ULONG Size, + IN ULONG FileLine +); + +extern +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine +); + +#else + +#define SpxAllocateMemory(Size) SpxAllocMem(Size) +#define SpxTrackMemoryUsage(pMem, Alloc, FileLine) + +extern +PVOID +SpxAllocMem( + IN ULONG Size +); + +#endif // TRACK_MEMORY_USAGE + +VOID +SpxFreeMemory( + IN PVOID pBuf); + +#define SpxAllocateZeroedMemory(Size) SpxAllocateMemory((Size) | ZEROED_MEMORY_TAG) + + +extern +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice); + +extern +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice); + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId); + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId); + diff --git a/private/ntos/tdi/isn/spx/h/spxntdef.h b/private/ntos/tdi/isn/spx/h/spxntdef.h new file mode 100644 index 000000000..60f9c9e54 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxntdef.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxntdef.h + +Abstract: + + Missing nt definitions in ntddk.h + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +NTSTATUS +NTAPI +NtCreateFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + +NTSTATUS +NTAPI +NtClose( + IN HANDLE Handle + ); + +NTSTATUS +NTAPI +NtDeviceIoControlFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG IoControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength + ); + +NTSTATUS +NTAPI +NtWaitForSingleObject( + IN HANDLE Handle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL + ); + + diff --git a/private/ntos/tdi/isn/spx/h/spxpkt.h b/private/ntos/tdi/isn/spx/h/spxpkt.h new file mode 100644 index 000000000..b643ed95b --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxpkt.h @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// Use our own NDIS packets +#define SPX_OWN_PACKETS 1 + +// Offsets into the IPX header +#define IPX_HDRSIZE 30 // Size of the IPX header +#define IPX_CHECKSUM 0 // Checksum +#define IPX_LENGTH 2 // Length +#define IPX_XPORTCTL 4 // Transport Control +#define IPX_PKTTYPE 5 // Packet Type +#define IPX_DESTADDR 6 // Dest. Address (Total) +#define IPX_DESTNET 6 // Dest. Network Address +#define IPX_DESTNODE 10 // Dest. Node Address +#define IPX_DESTSOCK 16 // Dest. Socket Number +#define IPX_SRCADDR 18 // Source Address (Total) +#define IPX_SRCNET 18 // Source Network Address +#define IPX_SRCNODE 22 // Source Node Address +#define IPX_SRCSOCK 28 // Source Socket Number + +#define IPX_NET_LEN 4 +#define IPX_NODE_LEN 6 + + +#include + +// Definition of the IPX/SPX header. +typedef struct _IPXSPX_HEADER +{ + USHORT hdr_CheckSum; + USHORT hdr_PktLen; + UCHAR hdr_XportCtrl; + UCHAR hdr_PktType; + UCHAR hdr_DestNet[4]; + UCHAR hdr_DestNode[6]; + USHORT hdr_DestSkt; + UCHAR hdr_SrcNet[4]; + UCHAR hdr_SrcNode[6]; + USHORT hdr_SrcSkt; + + // SPX Header Elements + UCHAR hdr_ConnCtrl; + UCHAR hdr_DataType; + USHORT hdr_SrcConnId; + USHORT hdr_DestConnId; + USHORT hdr_SeqNum; + USHORT hdr_AckNum; + USHORT hdr_AllocNum; + + // For non-CR SPXII packets only + USHORT hdr_NegSize; + +} IPXSPX_HDR, *PIPXSPX_HDR; + +#include + +// NDIS Packet size +#define NDIS_PACKET_SIZE 48 + +// Minimum header size (doesnt include neg size) +#define MIN_IPXSPX_HDRSIZE (sizeof(IPXSPX_HDR) - sizeof(USHORT)) +#define MIN_IPXSPX2_HDRSIZE sizeof(IPXSPX_HDR) +#define SPX_CR_PKTLEN 42 + +// SPX packet type +#define SPX_PKT_TYPE 0x5 + +// Connection control fields +#define SPX_CC_XHD 0x01 +#define SPX_CC_RES1 0x02 +#define SPX_CC_NEG 0x04 +#define SPX_CC_SPX2 0x08 +#define SPX_CC_EOM 0x10 +#define SPX_CC_ATN 0x20 +#define SPX_CC_ACK 0x40 +#define SPX_CC_SYS 0x80 + +#define SPX_CC_CR (SPX_CC_ACK | SPX_CC_SYS) + +// Data stream types +#define SPX2_DT_ORDREL 0xFD +#define SPX2_DT_IDISC 0xFE +#define SPX2_DT_IDISC_ACK 0xFF + +// Negotiation size +#define SPX_MAX_PACKET 576 +#define SPX_NEG_MIN SPX_MAX_PACKET +#define SPX_NEG_MAX 65535 + +// No packet references connection. But if the sends are being aborted, and +// the packet happens to be owned by ipx at the time, the pkt is dequeued from +// conn, the ABORT flag is set and conn is referenced for packet. +// +// Send packet states +// ABORT : Used for aborted packet. Calls AbortSendPkt(). +// IPXOWNS : Currently owned by ipx +// FREEDATA: Frees the data associated with second ndis buffer desc +// ACKREQ : Only for sequenced packets. Set by retry timer in packets it wants +// resent (1 for spx1, all pending for spx2) with ack bit set. +// DESTROY : Only for non-sequenced packets, dequeue packet from list and free. +// REQ : For both seq/non-seq. A request is associated with the packet +// SEQ : Packet is a sequenced packet. +// LASTPKT : Packet is last packet comprising the request, if acked req is done. +// EOM : Send EOM with the last packet for this request +// ACKEDPKT: Send completion must only deref req with pkt and complete if zero. +// + +#define SPX_SENDPKT_IDLE 0 +#define SPX_SENDPKT_ABORT 0x0002 +#define SPX_SENDPKT_IPXOWNS 0x0004 +#define SPX_SENDPKT_FREEDATA 0x0008 +#define SPX_SENDPKT_ACKREQ 0x0010 +#define SPX_SENDPKT_DESTROY 0x0020 +#define SPX_SENDPKT_REQ 0x0040 +#define SPX_SENDPKT_SEQ 0x0080 +#define SPX_SENDPKT_LASTPKT 0x0100 +#define SPX_SENDPKT_ACKEDPKT 0x0200 +#define SPX_SENDPKT_EOM 0x0400 +#define SPX_SENDPKT_REXMIT 0x0800 + +// Packet types +#define SPX_TYPE_CR 0x01 +#define SPX_TYPE_CRACK 0x02 +#define SPX_TYPE_SN 0x03 +#define SPX_TYPE_SNACK 0x04 +#define SPX_TYPE_SS 0x05 +#define SPX_TYPE_SSACK 0x06 +#define SPX_TYPE_RR 0x07 +#define SPX_TYPE_RRACK 0x08 +#define SPX_TYPE_IDISC 0x09 +#define SPX_TYPE_IDISCACK 0x0a +#define SPX_TYPE_ORDREL 0x0b +#define SPX_TYPE_ORDRELACK 0x0c +#define SPX_TYPE_DATA 0x0d +#define SPX_TYPE_DATAACK 0x0e +#define SPX_TYPE_DATANACK 0x0f +#define SPX_TYPE_PROBE 0x10 + +// Definition of the protocol reserved field of a send packet. +// BUGBUG: Make Len/HdrLen USHORTS, move to the end before the +// sr_SentTime so we dont use padding space. +typedef struct _SPX_SEND_RESD +{ + UCHAR sr_Id; // Set to SPX + UCHAR sr_Type; // What kind of packet + USHORT sr_State; // State of send packet + PVOID sr_Reserved1; // Needed by IPX + PVOID sr_Reserved2; // Needed by IPX +#if defined(_PNP_POWER) + PVOID sr_Reserved[SEND_RESERVED_COMMON_SIZE-2]; // needed by IPX for local target +#endif _PNP_POWER + ULONG sr_Len; // Length of packet + ULONG sr_HdrLen; // Included header length + + struct _SPX_SEND_RESD * sr_Next; // Points to next packet + // in send queue in conn. + PREQUEST sr_Request; // request associated + ULONG sr_Offset; // Offset in mdl for sends + +#ifndef SPX_OWN_PACKETS + PVOID sr_FreePtr; // Ptr to use in free chunk +#endif + + struct _SPX_CONN_FILE * sr_ConnFile; // that this send is on + USHORT sr_SeqNum; // Seq num for seq pkts + + // Quad word aligned. + LARGE_INTEGER sr_SentTime; // Time packet was sent + // Only valid for data pkt + // with ACKREQ set. + +} SPX_SEND_RESD, *PSPX_SEND_RESD; + + + +// Recv packet states +#define SPX_RECVPKT_IDLE 0 +#define SPX_RECVPKT_BUFFERING 0x0001 +#define SPX_RECVPKT_IDISC 0x0002 +#define SPX_RECVPKT_ORD_DISC 0x0004 +#define SPX_RECVPKT_INDICATED 0x0008 +#define SPX_RECVPKT_SENDACK 0x0010 +#define SPX_RECVPKT_EOM 0x0020 +#define SPX_RECVPKT_IMMEDACK 0x0040 + +#define SPX_RECVPKT_DISCMASK (SPX_RECVPKT_ORD_DISC | SPX_RECVPKT_IDISC) + +// Definition of the protocol reserved field of a receive packet. +typedef struct _SPX_RECV_RESD +{ + UCHAR rr_Id; // Set to SPX + USHORT rr_State; // State of receive packet + struct _SPX_RECV_RESD * rr_Next; // Points to next packet + ULONG rr_DataOffset; // To indicate/copy from + +#ifndef SPX_OWN_PACKETS + PVOID rr_FreePtr; // Ptr to use in free chunk +#endif + +#if DBG + USHORT rr_SeqNum; // Seq num of packet +#endif + + PREQUEST rr_Request; // request waiting on xfer + struct _SPX_CONN_FILE * rr_ConnFile; // that this recv is on + +} SPX_RECV_RESD, *PSPX_RECV_RESD; + + +// Destination built as an assign of 3 ulongs. +#define SpxBuildIpxHdr(pIpxSpxHdr, PktLen, pRemAddr, SrcSkt) \ + { \ + PBYTE pDestIpxAddr = (PBYTE)pIpxSpxHdr->hdr_DestNet; \ + (pIpxSpxHdr)->hdr_CheckSum = 0xFFFF; \ + PUTSHORT2SHORT((PUSHORT)(&(pIpxSpxHdr)->hdr_PktLen), (PktLen)); \ + (pIpxSpxHdr)->hdr_XportCtrl = 0; \ + (pIpxSpxHdr)->hdr_PktType = SPX_PKT_TYPE; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNet))= \ + *((UNALIGNED ULONG *)(SpxDevice->dev_Network)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNode)) = \ + *((UNALIGNED ULONG *)SpxDevice->dev_Node); \ + *((UNALIGNED USHORT *)((pIpxSpxHdr)->hdr_SrcNode+4)) = \ + *((UNALIGNED USHORT *)(SpxDevice->dev_Node+4)); \ + *((UNALIGNED USHORT *)&((pIpxSpxHdr)->hdr_SrcSkt)) = \ + SrcSkt; \ + } + +#define SpxCopyIpxAddr(pIpxSpxHdr, pDestIpxAddr) \ + { \ + PBYTE pRemAddr = (PBYTE)pIpxSpxHdr->hdr_SrcNet; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + } + +#ifndef SPX_OWN_PACKETS +#define SpxAllocSendPacket(_Device,_SendPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_SEND_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket), (_Handle)); \ + SEND_RESD(_SendPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_RECV_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_RecvPacket), (_Handle)); \ + RECV_RESD(_RecvPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxReInitSendPacket(_Packet) + { + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; + NdisReInitializePacket(_Packet); + SEND_RESD(_Packet)->sr_PoolHandle = (_Handle); + } + +#define SpxReInitRecvPacket(_Packet) + { + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; + NdisReInitializePacket(_Packet); + RECV_RESD(_Packet)->rr_PoolHandle = (_Handle); + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#else + +#define SpxAllocSendPacket(_Device, _SendPacket, _Status) \ + { \ + if (*(_SendPacket) = SpxBPAllocBlock(BLKID_NDISSEND)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + if (*(_RecvPacket) = SpxBPAllocBlock(BLKID_NDISRECV)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISSEND); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISRECV); \ + } + +#define SpxReInitSendPacket(_Packet) \ + { \ + } + +#define SpxReInitRecvPacket(_Packet) \ + { \ + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#endif + + +// +// Routine Prototypes +// + +VOID +SpxPktBuildCr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildCrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildSn( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSs( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSsAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSnAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildRr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State); + +VOID +SpxPktBuildRrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize); + +VOID +SpxPktBuildProbe( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildData( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length); + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ); + +VOID +SpxPktBuildAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend); + +VOID +SpxPktBuildDisc( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType); + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt); + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt); diff --git a/private/ntos/tdi/isn/spx/h/spxquery.h b/private/ntos/tdi/isn/spx/h/spxquery.h new file mode 100644 index 000000000..68e0a1ca8 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxquery.h @@ -0,0 +1,54 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define SPX_TDI_PROVIDERINFO_VERSION 0x0001 +#define SPX_PINFOSENDSIZE 0xFFFFFFFF +#define SPX_PINFOMINMAXLOOKAHEAD 128 + +// +// Bug #14498: Indicate to AFD that we are capable of orderly disc so AFD can follow +// these semantics. +// In order to have SPXI connections work correctly, we OR this bit in at query time. +// (see SpxTdiQueryInformation) +// +#define SPX_PINFOSERVICEFLAGS ( TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_DELAYED_ACCEPTANCE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY) // | \ + // TDI_SERVICE_ORDERLY_RELEASE ) + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo); + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request); + diff --git a/private/ntos/tdi/isn/spx/h/spxrecv.h b/private/ntos/tdi/isn/spx/h/spxrecv.h new file mode 100644 index 000000000..ca1572c64 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxrecv.h @@ -0,0 +1,91 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +BOOLEAN +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterCtx, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl); + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred); + +VOID +SpxReceiveComplete( + IN USHORT NicId); + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize); + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn); + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + diff --git a/private/ntos/tdi/isn/spx/h/spxreg.h b/private/ntos/tdi/isn/spx/h/spxreg.h new file mode 100644 index 000000000..4e0cb469b --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxreg.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.h + +Abstract: + + Private include file for the ISN SPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#define HALFSEC_TO_MS_FACTOR 500 +#define IPX_REG_PATH L"NwlnkIpx\\Linkage" + +// These are used to index into the Parameters array in CONFIG. +#define CONFIG_CONNECTION_COUNT 0 +#define CONFIG_CONNECTION_TIMEOUT 1 +#define CONFIG_INIT_PACKETS 2 +#define CONFIG_MAX_PACKETS 3 +#define CONFIG_INITIAL_RETRANSMIT_TIMEOUT 4 +#define CONFIG_KEEPALIVE_COUNT 5 +#define CONFIG_KEEPALIVE_TIMEOUT 6 +#define CONFIG_WINDOW_SIZE 7 +#define CONFIG_SOCKET_RANGE_START 8 +#define CONFIG_SOCKET_RANGE_END 9 +#define CONFIG_SOCKET_UNIQUENESS 10 +#define CONFIG_MAX_PACKET_SIZE 11 +#define CONFIG_REXMIT_COUNT 12 + +// Hidden parameters +#define CONFIG_DISABLE_SPX2 13 +#define CONFIG_ROUTER_MTU 14 +#define CONFIG_BACKCOMP_SPX 15 + +#define CONFIG_PARAMETERS 16 + +// Main configuration structure. +typedef struct _CONFIG { + + ULONG cf_Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING cf_DeviceName; // device name exported + PWSTR cf_RegistryPathBuffer; // path to config info + +} CONFIG, * PCONFIG; + + +#define PARAM(x) (SpxDevice->dev_ConfigInfo->cf_Parameters[(x)]) + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr); + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config); + diff --git a/private/ntos/tdi/isn/spx/h/spxsend.h b/private/ntos/tdi/isn/spx/h/spxsend.h new file mode 100644 index 000000000..40ef810ea --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxsend.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +VOID +SpxSendComplete( + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus); + +VOID +SpxSendPktRelease( + IN PNDIS_PACKET pPkt, + IN UINT BufCount); diff --git a/private/ntos/tdi/isn/spx/h/spxtimer.h b/private/ntos/tdi/isn/spx/h/spxtimer.h new file mode 100644 index 000000000..225037bd2 --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxtimer.h @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.h + +Abstract: + + This module contains routines to schedule timer events. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + 19 Jun 1992 Initial Version + +Notes: Tab stop: 4 +--*/ + +#define TIMER_DONT_REQUEUE 0 +#define TIMER_REQUEUE_CUR_VALUE 1 + +typedef ULONG (*TIMER_ROUTINE)(IN PVOID Context, IN BOOLEAN TimerShuttingDown); + +extern +NTSTATUS +SpxTimerInit( + VOID); + +extern +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG DeltaTime, // Schedule after this much time + IN PVOID pContext); // Context to pass to the routine + +extern +VOID +SpxTimerFlushAndStop( + VOID); + +extern +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue); + +#define TMR_SIGNATURE *(PULONG)"ATMR" +#if DBG +#define VALID_TMR(pTmr) (((pTmr) != NULL) && \ + ((pTmr)->tmr_Signature == TMR_SIGNATURE)) +#else +#define VALID_TMR(pTmr) ((pTmr) != NULL) +#endif +typedef struct _TimerList +{ +#if DBG + ULONG tmr_Signature; +#endif + struct _TimerList * tmr_Next; // Link to next + struct _TimerList ** tmr_Prev; // Link to prev + struct _TimerList * tmr_Overflow; // Link to overflow entry in hash table + ULONG tmr_AbsTime; // Absolute time, for re-enqueue + ULONG tmr_RelDelta; // Relative to the previous entry + ULONG tmr_Id; // Unique Id for this event + BOOLEAN tmr_Cancelled; // Was the timer cancelled? + TIMER_ROUTINE tmr_Worker; // Real Worker + PVOID tmr_Context; // Real context +} TIMERLIST, *PTIMERLIST; + + +#define SpxGetCurrentTime() (SpxTimerCurrentTime/SPX_TIMER_FACTOR) +#define SpxGetCurrentTick() SpxTimerCurrentTime + +// Keep this at a ONE second level. +#define SPX_TIMER_FACTOR 10 // i.e. 10 ticks per second +#define SPX_MS_TO_TICKS 100 // Divide ms by this to get ticks +#define SPX_TIMER_TICK -1000000L // 100ms in 100ns units +#define SPX_TIMER_WAIT 50 // Time to wait in FlushAndStop in ms +#define TIMER_HASH_TABLE 32 + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +spxTimerWorker( + IN PTIMERLIST pList); + +VOID +spxTimerEnqueue( + PTIMERLIST pListNew); + + diff --git a/private/ntos/tdi/isn/spx/h/spxutils.h b/private/ntos/tdi/isn/spx/h/spxutils.h new file mode 100644 index 000000000..074a1649b --- /dev/null +++ b/private/ntos/tdi/isn/spx/h/spxutils.h @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// For PROTO_SPX, i'd return a device name from the dll of the form +// \Device\NwlnkSpx\SpxStream (for SOCK_STREAM) or +// \Device\NwlnkSpx\Spx (for SOCK_SEQPKT) +// +// and for PROTO_SPXII (the more common case we hope, even if +// internally we degrade to SPX1 cause of the remote client's +// limitations) +// \Device\NwlnkSpx\Stream (for SOCK_STREAM) or +// \Device\NwlnkSpx (for SOCK_SEQPKT) + +#define SOCKET1STREAM_SUFFIX L"\\SpxStream" +#define SOCKET1_SUFFIX L"\\Spx" +#define SOCKET2STREAM_SUFFIX L"\\Stream" +#define SOCKET1_TYPE_SEQPKT 0 +#define SOCKET2_TYPE_SEQPKT 1 +#define SOCKET1_TYPE_STREAM 2 +#define SOCKET2_TYPE_STREAM 3 + +#define IN_RANGE(_S, _RangeStart, _RangeEnd) \ + ((_S >= _RangeStart) && (_S <= _RangeEnd)) + + +// +// The following macros deal with on-the-wire integer and long values +// +// On the wire format is big-endian i.e. a long value of 0x01020304 is +// represented as 01 02 03 04. Similarly an int value of 0x0102 is +// represented as 01 02. +// +// The host format is not assumed since it will vary from processor to +// processor. +// + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = (USHORT) (*(PBYTE)(SrcPtr)) + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = (ULONG) (*(PBYTE)(SrcPtr)) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a dword from on-the-wire format to a dword in the host format +#define GETULONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 24) + \ + (*((PBYTE)(SrcPtr)+1) << 16) + \ + (*((PBYTE)(SrcPtr)+2) << 8) + \ + (*((PBYTE)(SrcPtr)+3) )) + +// Get a dword from on-the-wire format to a dword in the same format but +// also watch out for alignment +#define GETULONG2ULONG_NOCONV(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0); \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1); \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2); \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3); + +// Put a dword from the host format to a short to on-the-wire format +#define PUTBYTE2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = ((USHORT)(Src) % 256) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((USHORT)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE)(Src) + +// Put a dword from the host format to a byte to on-the-wire format +#define PUTULONG2BYTE(DstPtr, Src) \ + *(PBYTE)(DstPtr) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTULONG2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE) (Src) + +// Put a dword from the host format to a dword to on-the-wire format +#define PUTULONG2ULONG(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 24), \ + *((PBYTE)(DstPtr)+1) = (BYTE) ((ULONG)(Src) >> 16), \ + *((PBYTE)(DstPtr)+2) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+3) = (BYTE) (Src) + +// Put a BYTE[4] array into another BYTE4 array. +#define PUTBYTE42BYTE4(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0), \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1), \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2), \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3) + +// MIN/MAX macros +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + + + +// Exported prototypes + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr); + +LONG +SpxRandomNumber( + VOID); + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType); + +VOID +SpxSleep( + IN ULONG TimeInMs); + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket); + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr); + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress); + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength); + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1); diff --git a/private/ntos/tdi/isn/spx/mp/makefile b/private/ntos/tdi/isn/spx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/spx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/spx/mp/sources b/private/ntos/tdi/isn/spx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isn/spx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isn/spx/nwlnkspx.rc b/private/ntos/tdi/isn/spx/nwlnkspx.rc new file mode 100644 index 000000000..02175f21d --- /dev/null +++ b/private/ntos/tdi/isn/spx/nwlnkspx.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 SPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkspx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkspx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isn/spx/precomp.h b/private/ntos/tdi/isn/spx/precomp.h new file mode 100644 index 000000000..d227d02f9 --- /dev/null +++ b/private/ntos/tdi/isn/spx/precomp.h @@ -0,0 +1 @@ +#include "isnspx.h" diff --git a/private/ntos/tdi/isn/spx/sources.inc b/private/ntos/tdi/isn/spx/sources.inc new file mode 100644 index 000000000..419f580f1 --- /dev/null +++ b/private/ntos/tdi/isn/spx/sources.inc @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkspx + +TARGETNAME=nwlnkspx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\h;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\spxdrvr.c \ + ..\spxreg.c \ + ..\spxdev.c \ + ..\spxbind.c \ + ..\spxaddr.c \ + ..\spxconn.c \ + ..\spxcutil.c \ + ..\spxcpkt.c \ + ..\spxrecv.c \ + ..\spxsend.c \ + ..\spxquery.c \ + ..\spxutils.c \ + ..\spxmem.c \ + ..\spxtimer.c \ + ..\spxpkt.c \ + ..\globals.c \ + ..\spxerror.c \ + ..\nwlnkspx.rc + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/isn/spx/spxaddr.c b/private/ntos/tdi/isn/spx/spxaddr.c new file mode 100644 index 000000000..e9e85dc5c --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxaddr.c @@ -0,0 +1,1729 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, SpxAddrFileCreate) +#pragma alloc_text( PAGE, SpxAddrFileClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXADDR + +// Map all generic accesses to the same one. +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +#define REORDER(_Socket) ((((_Socket) & 0xff00) >> 8) | (((_Socket) & 0x00ff) << 8)) + + + + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the ST transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR pAddr; + PSPX_ADDR_FILE pAddrFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED * AddressName; + USHORT Socket, hostSocket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle, LockHandleAddr; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; + +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // The network name is in the EA, passed in the request. + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no EA\n", Request)); + + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // this may be a valid name; parse the name from the EA and use it if OK. + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + for (i=0;iTAAddressCount;i++) + { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + Socket = + ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + + GETSHORT2SHORT(&hostSocket, &Socket); + + DBGPRINT(CREATE, DBG, + ("SpxAddrOpen: Creating socket %lx.h%lx\n", + Socket, hostSocket )); + + found = TRUE; + } + break; + + } + else + { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + } + } + + if (!found) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no IPX Address\n", Request)); + + return STATUS_INVALID_ADDRESS_COMPONENT; + } + +#ifdef SOCKET_RANGE_OPEN_LIMITATION_REMOVED + // Is the socket in our range if its in the range 0x4000-0x7FFF + if (IN_RANGE(hostSocket, DYNSKT_RANGE_START, DYNSKT_RANGE_END)) + { + if (!IN_RANGE( + hostSocket, + PARAM(CONFIG_SOCKET_RANGE_START), + PARAM(CONFIG_SOCKET_RANGE_END))) + { + return(STATUS_INVALID_ADDRESS); + } + } +#endif + + // get an address file structure to represent this address. + status = SpxAddrFileCreate(Device, Request, &pAddrFile); + if (!NT_SUCCESS(status)) + return status; + + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + CTEGetLock (&Device->dev_Lock, &LockHandle); + + // We checkfor/create sockets within the critical section. + if (Socket == 0) + { + Socket = SpxAddrAssignSocket(Device); + + if (Socket == 0) + { + DBGPRINT(ADDRESS, ERR, + ("OpenAddress, no unique socket found\n")); + + CTEFreeLock (&Device->dev_Lock, LockHandle); + ExReleaseResource (&Device->dev_AddrResource); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + } + + pAddr = SpxAddrLookup(Device, Socket); + + if (pAddr == NULL) + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // This address doesn't exist. Create it. + // registering it. It also puts a ref of type ADDR_FILE on address. + pAddr = SpxAddrCreate( + Device, + Socket); + + if (pAddr != (PSPX_ADDR)NULL) + { +#ifdef ISN_NT + + // Initialize the shared access now. We use read access + // to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess); + + + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &pAddr->sa_SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + // Error, return status. + IoRemoveShareAccess (IrpSp->FileObject, &pAddr->u.sa_ShareAccess); + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + return status; + } + +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + // if the adapter isn't ready, we can't do any of this; get out +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) +#else + if (Device->dev_State == DEVICE_STATE_STOPPING) +#endif _PNP_POWER + { + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + status = STATUS_DEVICE_NOT_READY; + } + else + { + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; + + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + status = STATUS_SUCCESS; + } + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + + SpxAddrFileDestroy(pAddrFile); + } + } + else + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + DBGPRINT(ADDRESS, ERR, + ("Add to address %lx\n", pAddr)); + + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + pAddr->sa_SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { +#ifdef ISN_NT + + // Now check that we can obtain the desired share + // access. We use read access to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + SpxAddrLockReference (pAddr, AREF_ADDR_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&Device->dev_Lock, LockHandle); + + status = STATUS_SUCCESS; + } + } + + // Remove the reference from SpxLookupAddress. + SpxAddrDereference (pAddr, AREF_LOOKUP); + } + + return status; + +} // SpxAddrOpen + + + + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest + ) +{ + CTELockHandle lockHandle; + NTSTATUS status = STATUS_SUCCESS; + + PSPX_ADDR_FILE + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + PTDI_REQUEST_KERNEL_SET_EVENT + pParam = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(pRequest); + + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(status); + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + switch (pParam->EventType) + { + + case TDI_EVENT_ERROR: + + break; + + case TDI_EVENT_CONNECT: + + pSpxAddrFile->saf_ConnHandler = + (PTDI_IND_CONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_ConnHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE: + + pSpxAddrFile->saf_RecvHandler = + (PTDI_IND_RECEIVE)(pParam->EventHandler); + pSpxAddrFile->saf_RecvHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_DISCONNECT: + + pSpxAddrFile->saf_DiscHandler = + (PTDI_IND_DISCONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_DiscHandlerCtx = + pParam->EventContext; + + break; + + + case TDI_EVENT_SEND_POSSIBLE : + + pSpxAddrFile->saf_SendPossibleHandler = + (PTDI_IND_SEND_POSSIBLE)(pParam->EventHandler); + pSpxAddrFile->saf_SendPossibleHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + case TDI_EVENT_RECEIVE_EXPEDITED: + default: + + status = STATUS_INVALID_PARAMETER; + } + + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return(status); +} + + + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PSPX_ADDR pAddr; + int index; + CTELockHandle lockHandle; + + pAddr = (PSPX_ADDR)SpxAllocateZeroedMemory (sizeof(SPX_ADDR)); + if (pAddr == NULL) + { + DBGPRINT(ADDRESS, INFO, + ("Create address %lx failed\n", (ULONG)Socket)); + + return NULL; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address %lx (%lx)\n", pAddr, (ULONG)Socket)); + + pAddr->sa_Type = SPX_ADDRESS_SIGNATURE; + pAddr->sa_Size = sizeof (SPX_ADDR); + pAddr->sa_Flags = 0; + + pAddr->sa_Device = Device; + pAddr->sa_DeviceLock = &Device->dev_Lock; + CTEInitLock (&pAddr->sa_Lock); + + // This reference is for the address file that will associated with this addr. + pAddr->sa_RefCount = 1; + +#if DBG + pAddr->sa_RefTypes[AREF_ADDR_FILE] = 1; +#endif + + pAddr->sa_Socket = Socket; + + // Insert address into the device hash table. + index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + CTEGetLock (&Device->dev_Lock, &lockHandle); + pAddr->sa_Next = Device->dev_AddrHashTable[index]; + Device->dev_AddrHashTable[index] = pAddr; + CTEFreeLock (&Device->dev_Lock, lockHandle); + + SpxReferenceDevice (Device, DREF_ADDRESS); + + return pAddr; + +} // SpxAddrCreate + + + + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a SPX_ADDR_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PSPX_ADDR Address; + + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + + try + { + if ((pAddrFile->saf_Size == sizeof (SPX_ADDR_FILE)) && + (pAddrFile->saf_Type == SPX_ADDRESSFILE_SIGNATURE) ) + { + Address = pAddrFile->saf_Addr; + + if ((Address->sa_Size == sizeof (SPX_ADDR)) && + (Address->sa_Type == SPX_ADDRESS_SIGNATURE) ) + { + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if ((Address->sa_Flags & SPX_ADDR_CLOSING) == 0) + { + SpxAddrFileLockReference(pAddrFile, AFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx closing\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx bad signature\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pAddrFile)); + + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxAddrFileVerify: AF %lx exception\n", Address)); + + return GetExceptionCode(); + } + + return status; + +} // SpxAddrFileVerify + + + + +VOID +SpxAddrDestroy( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by SpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by SpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSPX_ADDR pAddr, *ppAddr; + CTELockHandle LockHandle; + + PSPX_ADDR Address = (PSPX_ADDR)Parameter; + PDEVICE Device = Address->sa_Device; + int index = (int)(Address->sa_Socket & NUM_SPXADDR_HASH_MASK); + + DBGPRINT(ADDRESS, INFO, + ("Destroy address %lx\n", Address)); + + SeDeassignSecurity (&Address->sa_SecurityDescriptor); + + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + CTEGetLock (&Device->dev_Lock, &LockHandle); + for (ppAddr = &Device->dev_AddrHashTable[index]; (pAddr = *ppAddr) != NULL;) + { + if (pAddr == Address) + { + *ppAddr = pAddr->sa_Next; + break; + } + + ppAddr = &pAddr->sa_Next; + } + CTEFreeLock (&Device->dev_Lock, LockHandle); + + SpxFreeMemory (Address); + SpxDereferenceDevice (Device, DREF_ADDRESS); + +} + + + + +#if DBG + +VOID +SpxAddrRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} + + + + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} +#endif + + + + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &Address->sa_RefCount, + (ULONG)-1, + Address->sa_DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) + { +#if ISN_NT + ExInitializeWorkItem( + &Address->u.sa_DestroyAddrQueueItem, + SpxAddrDestroy, + (PVOID)Address); + ExQueueWorkItem(&Address->u.sa_DestroyAddrQueueItem, DelayedWorkQueue); +#else + SpxAddrDestroy(Address); +#endif + + } + +} + + + + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + NTSTATUS status; + BYTE socketType; + CTELockHandle LockHandle; + PSPX_ADDR_FILE pAddrFile; + + // What is the address file type? + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) + { + return(status); + } + + pAddrFile = (PSPX_ADDR_FILE)SpxAllocateZeroedMemory (sizeof(SPX_ADDR_FILE)); + if (pAddrFile == NULL) + { + DBGPRINT(ADDRESS, ERR, + ("Create address file failed\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address file %lx\n", pAddrFile)); + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + pAddrFile->saf_Type = SPX_ADDRESSFILE_SIGNATURE; + pAddrFile->saf_Size = sizeof (SPX_ADDR_FILE); + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject = NULL; +#endif + + pAddrFile->saf_Device = Device; + pAddrFile->saf_Flags = SPX_ADDRFILE_OPENING; + if ((socketType == SOCKET1_TYPE_SEQPKT) || + (socketType == SOCKET1_TYPE_STREAM)) + { + if (socketType == SOCKET1_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_SPX2; + if (socketType == SOCKET2_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + pAddrFile->saf_RefCount = 1; + +#if DBG + pAddrFile->saf_RefTypes[AFREF_CREATE] = 1; +#endif + + pAddrFile->saf_CloseReq = (PREQUEST)NULL; + + // Initialize the request handlers. + pAddrFile->saf_ConnHandler = + pAddrFile->saf_ConnHandlerCtx = NULL; + pAddrFile->saf_DiscHandler = + pAddrFile->saf_DiscHandlerCtx = NULL; + pAddrFile->saf_RecvHandler = + pAddrFile->saf_RecvHandlerCtx = NULL; + pAddrFile->saf_ErrHandler = + pAddrFile->saf_ErrHandlerCtx = NULL; + + // Release lock + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // Put in the global list for our reference + spxAddrInsertIntoGlobalList(pAddrFile); + + *ppAddrFile = pAddrFile; + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by SpxAddrFileDereference. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + pAddrFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PSPX_ADDR Address; + PDEVICE Device; + PREQUEST CloseRequest; + PSPX_ADDR_FILE pRemAddr, *ppRemAddr; + + DBGPRINT(ADDRESS, INFO, + ("Destroy address file %lx\n", pAddrFile)); + + Address = pAddrFile->saf_Addr; + Device = pAddrFile->saf_Device; + + if (Address) + { + CTEGetLock (&Device->dev_Lock, &LockHandle1); + + // This addressfile was associated with an address. + CTEGetLock (&Address->sa_Lock, &LockHandle); + + // If the last reference on the address is being removed, set the + // closing flag to prevent further references. + + //if (Address->sa_RefCount == 1) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&Address->sa_RefCount, 0, &Address->sa_Lock) == 1) { + Address->sa_Flags |= SPX_ADDR_CLOSING; + } + + // Dequeue the address file from the address list. + for (ppRemAddr = &Address->sa_AddrFileList; (pRemAddr = *ppRemAddr) != NULL;) + { + if (pRemAddr == pAddrFile) + { + *ppRemAddr = pRemAddr->saf_Next; + break; + } + + ppRemAddr = &pRemAddr->saf_Next; + } + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject->FsContext = NULL; + pAddrFile->saf_FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->sa_Lock, LockHandle); + CTEFreeLock (&Device->dev_Lock, LockHandle1); + + // We will already have been removed from the ShareAccess + // of the owning address. + // + // Now dereference the owning address. + SpxAddrDereference(Address, AREF_ADDR_FILE); + } + + // Save this for later completion. + CloseRequest = pAddrFile->saf_CloseReq; + + // Remove from the global list + spxAddrRemoveFromGlobalList(pAddrFile); + + // return the addressFile to the pool of address files + SpxFreeMemory (pAddrFile); + + if (CloseRequest != (PREQUEST)NULL) + { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + SpxCompleteRequest (CloseRequest); + SpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} + + + + +#if DBG + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} // SpxRefAddressFile + + + + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} +#endif + + + + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddressFile to remove it from the system. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + (ULONG)-1, + pAddrFile->saf_AddrLock); + + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) + { + SpxAddrFileDestroy(pAddrFile); + } + +} + + + + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + SpxAddrLockReference(Address, AREF_LOOKUP); + return Address; + + } + } + + // The specified address was not found. + return NULL; + +} + + + + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + TRUE if so, else FALSE + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match + return TRUE; + } + } + + // The specified address was not found. + return FALSE; + +} // SpxAddrExists + + + + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT PSPX_CONN_FILE *ppSpxConnFile + ) +{ + PSPX_CONN_FILE pSpxConnFile; + NTSTATUS status = STATUS_INVALID_CONNECTION; + + for (pSpxConnFile = pSpxAddr->sa_ActiveConnList; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_Next) + { + if ((pSpxConnFile->scf_RemConnId == SrcConnId) && + (*((UNALIGNED ULONG *)SrcIpxAddr) == + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+4) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+8) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+8))) + { + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + *ppSpxConnFile = pSpxConnFile; + status = STATUS_SUCCESS; + break; + } + } + + return(status); +} + + + + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an pAddrFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + pAddrFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile, pSpxConnFileNext; + CTELockHandle LockHandle; + + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileStop: %lx\n", pAddrFile)); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if (pAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING) + { + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + } + + pAddrFile->saf_Flags |= SPX_ADDRFILE_CLOSING; + + pSpxConnFileNext = NULL; + if (pSpxConnFile = pAddrFile->saf_AssocConnList) + { + pSpxConnFileNext = pSpxConnFile; + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + } + + while (pSpxConnFile) + { + if (pSpxConnFileNext = pSpxConnFile->scf_AssocNext) + { + SpxConnFileReference(pSpxConnFileNext, CFREF_ADDR); + } + CTEFreeLock (&Address->sa_Lock, LockHandle); + + + DBGPRINT(CREATE, INFO, + ("SpxAddrFileClose: Assoc conn stop %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + SpxConnStop(pSpxConnFile); + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + pSpxConnFile = pSpxConnFileNext; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, INFO, + ("SpxAddrFileCleanup: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + if (!NT_SUCCESS (status)) + { + return(status); + } + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + SpxAddrFileStop(pSpxAddrFile, Address); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_SUCCESS; +} + + + + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileClose: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + + if (!NT_SUCCESS (status)) + { + return(status); + } + + pSpxAddrFile->saf_CloseReq = Request; + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + // Remove us from the access info for this address. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + +#ifdef ISN_NT + IoRemoveShareAccess (pSpxAddrFile->saf_FileObject, &Address->u.sa_ShareAccess); +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + + SpxAddrFileDereference (pSpxAddrFile, AFREF_CREATE); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_PENDING; + +} // SpxCloseAddressFile + + + + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + BOOLEAN wrapped = FALSE; + USHORT temp, Socket; + + // We have to auto-assign a socket. + temp = Device->dev_CurrentSocket; + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + while (TRUE) + { + Device->dev_CurrentSocket += (USHORT)PARAM(CONFIG_SOCKET_UNIQUENESS); + if (Device->dev_CurrentSocket > PARAM(CONFIG_SOCKET_RANGE_END)) + { + Device->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + wrapped = TRUE; + } + + if (!SpxAddrExists (Device, Socket)) + { + break; + } + + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + if (wrapped && (Device->dev_CurrentSocket >= temp)) + { + // If we have checked all possible values given SOCKET_UNIQUENESS... + // This may actually return ERROR even if there are + // available socket numbers although they may be + // implicitly in use due to SOCKET_UNIQUENESS being + // > 1. That is the way it is to work. + + Socket = 0; + break; + } + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + + return(Socket); +} + + + + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxAddrFile->saf_GlobalNext = SpxGlobalAddrList; + SpxGlobalAddrList = pSpxAddrFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_ADDR_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalAddrList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxAddrFile) + { + DBGPRINT(SEND, INFO, + ("SpxAddrRemoveFromGlobal: %lx\n", pSpxAddrFile)); + + // Remove from list + *ppC = pC->saf_GlobalNext; + break; + } + + ppC = &pC->saf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_ADDRESS; + + return(status); +} + + + + diff --git a/private/ntos/tdi/isn/spx/spxbind.c b/private/ntos/tdi/isn/spx/spxbind.c new file mode 100644 index 000000000..ba46eb7a9 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxbind.c @@ -0,0 +1,602 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.c + +Abstract: + + This module contains the code to bind to the IPX transport, as well as the + indication routines for the IPX transport not including the send/recv ones. + +Author: + + Stefan Solomon (stefans) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXBIND + +VOID +SpxStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry); + +VOID +SpxLineDown ( + IN USHORT NicId, + IN ULONG FwdAdapterContext); + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// globals and externs +// +extern CTELock spxTimerLock; +extern LARGE_INTEGER spxTimerTick; +extern KTIMER spxTimer; +extern KDPC spxTimerDpc; +extern BOOLEAN spxTimerStopped; +#endif _PNP_POWER + +NTSTATUS +SpxInitBindToIpx( + VOID + ) + +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objectAttr; + PIPX_INTERNAL_BIND_INPUT pBindInput; + PIPX_INTERNAL_BIND_OUTPUT pBindOutput; + + InitializeObjectAttributes( + &objectAttr, + &IpxDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = NtCreateFile( + &IpxHandle, + SYNCHRONIZE | GENERIC_READ, + &objectAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(status)) { + return status; + } + + if ((pBindInput = CTEAllocMem(sizeof(IPX_INTERNAL_BIND_INPUT))) == NULL) { + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Fill in our bind data +#if defined(_PNP_POWER) + pBindInput->Version = ISN_VERSION; +#else + pBindInput->Version = 1; +#endif _PNP_POWER + pBindInput->Identifier = IDENTIFIER_SPX; + pBindInput->BroadcastEnable = FALSE; + pBindInput->LookaheadRequired = IPX_HDRSIZE; + pBindInput->ProtocolOptions = 0; + pBindInput->ReceiveHandler = SpxReceive; + pBindInput->ReceiveCompleteHandler = SpxReceiveComplete; + pBindInput->StatusHandler = SpxStatus; + pBindInput->SendCompleteHandler = SpxSendComplete; + pBindInput->TransferDataCompleteHandler = SpxTransferDataComplete; + pBindInput->FindRouteCompleteHandler = SpxFindRouteComplete; + pBindInput->LineUpHandler = SpxLineUp; + pBindInput->LineDownHandler = SpxLineDown; + pBindInput->ScheduleRouteHandler = SpxScheduleRoute; +#if defined(_PNP_POWER) + pBindInput->PnPHandler = SpxPnPNotification; +#endif _PNP_POWER + + + // First get the length for the output buffer. + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + NULL, // Output Buffer + 0); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status != STATUS_BUFFER_TOO_SMALL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INVALID_PARAMETER); + } + + if ((pBindOutput = CTEAllocMem(ioStatusBlock.Information)) == NULL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + pBindOutput, // Output Buffer + ioStatusBlock.Information); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status == STATUS_SUCCESS) { + + // Get all the info from the bind output buffer and save in + // appropriate places. + IpxLineInfo = pBindOutput->LineInfo; + IpxMacHdrNeeded = pBindOutput->MacHeaderNeeded; + IpxInclHdrOffset = pBindOutput->IncludedHeaderOffset; + + IpxSendPacket = pBindOutput->SendHandler; + IpxFindRoute = pBindOutput->FindRouteHandler; + IpxQuery = pBindOutput->QueryHandler; + IpxTransferData = pBindOutput->TransferDataHandler; + +#if !defined(_PNP_POWER) + // Copy over the network node info. + RtlCopyMemory( + SpxDevice->dev_Network, + pBindOutput->Network, + IPX_NET_LEN); + + RtlCopyMemory( + SpxDevice->dev_Node, + pBindOutput->Node, + IPX_NODE_LEN); + + + DBGPRINT(TDI, INFO, + ("SpxInitBindToIpx: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // Find out how many adapters IPX has, if this fails + // just assume one. + // + + if ((*IpxQuery)( + IPX_QUERY_MAXIMUM_NIC_ID, + 0, + &SpxDevice->dev_Adapters, + sizeof(USHORT), + NULL) != STATUS_SUCCESS) { + + SpxDevice->dev_Adapters = 1; + + } +#endif !_PNP_POWER + } else { + + NtClose(IpxHandle); + status = STATUS_INVALID_PARAMETER; + } + CTEFreeMem(pBindInput); + CTEFreeMem(pBindOutput); + + return status; +} + + + + +VOID +SpxUnbindFromIpx( + VOID + ) + +{ + NtClose(IpxHandle); + return; +} + + + + +VOID +SpxStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxStatus: CALLED WITH %lx\n", + GeneralStatus)); + + return; +} + + + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +{ + CTELockHandle lockHandle; + PSPX_FIND_ROUTE_REQUEST pSpxFrReq = (PSPX_FIND_ROUTE_REQUEST)FindRouteRequest; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)pSpxFrReq->fr_Ctx; + + // This will be on a connection. Grab the lock, check the state and go from + // there. + if (pSpxConnFile == NULL) + { + // Should this ever happen? + KeBugCheck(0); + return; + } + + // Check the state. The called routines release the lock, remove the reference. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + // We are doing an active connect! + SpxConnConnectFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + else // For all others call active + { + SpxConnActiveFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + + // Free the find route request. + SpxFreeMemory(pSpxFrReq); + + return; +} + + + + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + +{ + // With PnP, our local address is changed when we get PnP + // notification. +#if !defined(_PNP_POWER) + + // + // If we get a line up for NicId 0, it means our local + // network number has changed, re-query from IPX. + // + + if (NicId == 0) { + + TDI_ADDRESS_IPX IpxAddress; + + if ((*IpxQuery)( + IPX_QUERY_IPX_ADDRESS, + 0, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + RtlCopyMemory( + SpxDevice->dev_Network, + &IpxAddress.NetworkAddress, + IPX_NET_LEN); + + DBGPRINT(TDI, INFO, + ("SpxLineUp: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // The node shouldn't change! + // + + if (!RtlEqualMemory( + SpxDevice->dev_Node, + IpxAddress.NodeAddress, + IPX_NODE_LEN)) { + + DBGPRINT(TDI, ERR, + ("SpxLineUp: Node address has changed\n")); + } + } + + } else { + + DBGPRINT(RECEIVE, ERR, + ("SpxLineUp: CALLED WITH %lx\n", + NicId)); + } + + return; +#endif !_PNP_POWER + +} + + + + +VOID +SpxLineDown ( + IN USHORT NicId, + IN ULONG FwdAdapterContext + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxLineDown: CALLED WITH %lx\n", + NicId)); + + return; +} + + + + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxScheduleRoute: CALLED WITH %lx\n", + RouteEntry)); + + return; +} + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + NTSTATUS Status; + PDEVICE Device = SpxDevice; + UNICODE_STRING UnicodeDeviceName; + + DBGPRINT(DEVICE, DBG,("Received a pnp notification, opcode %d\n",OpCode)); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + CTELockHandle TimerLockHandle; + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->dev_State != DEVICE_STATE_OPEN ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + // + // Start the timer. It is possible that the timer + // was still running or we are still in the timer dpc + // from the previous ADD_DEVICE - DELETE_DEVICE execution + // cycle. But it is ok simply restart this, because + // KeSetTimer implicitly cancels the previous Dpc. + // + + CTEGetLock(&spxTimerLock, &TimerLockHandle); + spxTimerStopped = FALSE; + CTEFreeLock(&spxTimerLock, TimerLockHandle); + KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + + Device->dev_State = DEVICE_STATE_OPEN; + + + CTEAssert( !Device->dev_Adapters ); + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + IpxLineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumPacketSize; + // set the provider info + SpxDevice->dev_ProviderInfo.MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + }else { + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + } + + Device->dev_Adapters++; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->dev_DeviceName; + UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen; + UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to register Spx Device with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + CTEAssert( Device->dev_Adapters ); + Device->dev_Adapters--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->dev_State = DEVICE_STATE_LOADED; + Device->dev_Adapters = 0; + } + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + if ( PnPInfo->FirstORLastDevice ) { + SpxTimerFlushAndStop(); + // + // inform tdi clients about the device deletion + // + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to Deregister Spx Device with TDI\n")); + } + } + // + // TBD: call ExNotifyCallback + // + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEAssert( PnPInfo->NewReservedAddress ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isn/spx/spxconn.c b/private/ntos/tdi/isn/spx/spxconn.c new file mode 100644 index 000000000..0c139fbc7 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxconn.c @@ -0,0 +1,3851 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, SpxConnOpen) +#pragma alloc_text(PAGE, SpxConnCleanup) +#pragma alloc_text(PAGE, SpxConnClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXCONN + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT ConnCtx, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine is used to create a connection object and associate the + passed ConnectionContext with it. + +Arguments: + + pConnCtx - The TDI ConnectionContext to be associated with object + +Return Value: + + STATUS_SUCCESS if connection was successfully opened + Error otherwise. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PSPX_CONN_FILE pSpxConnFile; + +#ifdef ISN_NT + PIRP Irp = (PIRP)pRequest; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // Allocate memory for a connection object + if ((pSpxConnFile = SpxAllocateZeroedMemory(sizeof(SPX_CONN_FILE))) == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Initialize values + pSpxConnFile->scf_Flags = 0; + pSpxConnFile->scf_Type = SPX_CONNFILE_SIGNATURE; + pSpxConnFile->scf_Size = sizeof (SPX_CONN_FILE); + + CTEInitLock (&pSpxConnFile->scf_Lock); + + pSpxConnFile->scf_ConnCtx = ConnCtx; + pSpxConnFile->scf_Device = pDevice; + + // Initialize list for requests. + InitializeListHead(&pSpxConnFile->scf_ReqLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_ReqDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_DiscLinkage); + +#ifdef ISN_NT + // easy backlink to file object. + pSpxConnFile->scf_FileObject = IrpSp->FileObject; +#endif + + // For connections we go from 0->0 with flags indicating if a close + // happened. + pSpxConnFile->scf_RefCount = 0; + + // Insert into a global connection list. + spxConnInsertIntoGlobalList(pSpxConnFile); + +#if DBG + + // Initialize this to 0xFFFF so we dont hit assert on first packet. + pSpxConnFile->scf_PktSeqNum = 0xFFFF; + +#endif + + // Set values in the request. + REQUEST_OPEN_CONTEXT(pRequest) = (PVOID)pSpxConnFile; + REQUEST_OPEN_TYPE(pRequest) = (PVOID)TDI_CONNECTION_FILE; + + DBGPRINT(CREATE, INFO, + ("SpxConnOpen: Opened %lx\n", pSpxConnFile)); + + ASSERT(status == STATUS_SUCCESS); + return(status); +} + + + + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileCleanup: %lx.%lx when %lx\n", + pSpxConnFile, Request, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CleanupReq = Request; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // We have a reference, so it wont go to zero until stop returns. Therefore + // deref can expect flag to be set. + SpxConnStop(pSpxConnFile); + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + + // + // If this is a connection which is waiting for a local disconnect, + // deref it since we dont expect a disconnect after a cleanup. + // + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + KdPrint(("Deref for DISCWAIT on connfile: %lx\n", pSpxConnFile)); + + SpxConnFileDereference (pSpxConnFile, CFREF_DISCWAITSPX); + } else { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + + return STATUS_PENDING; +} + + + + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileClose: %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CloseReq = Request; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + return STATUS_PENDING; +} + + + + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!!Connection must have a reference when this is called!!! + +Arguments: + + +Return Value: + + +--*/ +{ + CTELockHandle lockHandle; + + DBGPRINT(CREATE, INFO, + ("SpxConnFileStop: %lx when %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags)); + + // Call disconnect and disassociate + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + } + else + { + // Disassociate if we are associated. + spxConnDisAssoc(pSpxConnFile, lockHandle); + } + + // Lock released at this point. + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + return; +} + + + + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine moves the connection from the device list to the inactive + connection list in the address of the address file specified. The address + file is pointed to by the connection and is referenced for the associate. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE pSpxAddrFile; + CTELockHandle lockHandle1, lockHandle2; + + BOOLEAN derefAddr = FALSE, derefConn = FALSE; + PFILE_OBJECT pFileObj = NULL; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + HANDLE AddrObjHandle = + ((PTDI_REQUEST_KERNEL_ASSOCIATE)(REQUEST_PARAMETERS(pRequest)))->AddressHandle; + + do + { + // Get the handle to the address object from the irp and map it to + // the corres. file object. + status = ObReferenceObjectByHandle( + AddrObjHandle, + 0, + 0, + KernelMode, + (PVOID *)&pFileObj, + NULL); + + if (!NT_SUCCESS(status)) + break; + + pSpxAddrFile = pFileObj->FsContext; + ASSERT(pFileObj->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + // Verify address file/connection file + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + break; + + derefAddr = TRUE; + + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + break; + + derefConn = TRUE; + + // Grab the addres file lock, then the connection lock for associate. + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + if (!SPX_CONN_FLAG(pSpxConnFile, (SPX_CONNFILE_CLOSING | + SPX_CONNFILE_STOPPING | + SPX_CONNFILE_ASSOC)) + && + !(pSpxAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING)) + { + derefAddr = FALSE; + SpxAddrFileTransferReference( + pSpxAddrFile, AFREF_VERIFY, AFREF_CONN_ASSOC); + + // Queue in the inactive list in the address + pSpxConnFile->scf_Next = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxAddrFile->saf_Addr->sa_InactiveConnList = pSpxConnFile; + + // Queue in the assoc list in the address file + pSpxConnFile->scf_AssocNext = pSpxAddrFile->saf_AssocConnList; + pSpxAddrFile->saf_AssocConnList = pSpxConnFile; + + // Remember the addrfile in the connection + pSpxConnFile->scf_AddrFile = pSpxAddrFile; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + status = STATUS_SUCCESS; + + DBGPRINT(CREATE, INFO, + ("SpxConnAssociate: %lx with address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandle1); + + // Dereference the file object corres. to the address object + ObDereferenceObject(pFileObj); + + } while (FALSE); + + if (derefAddr) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + if (derefConn) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return (status); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile) + || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + SpxConnStop(pSpxConnFile); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + CTELockHandle lockHandleAddr; + PSPX_ADDR_FILE pSpxAddrFile; + + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Check again as we had released the lock + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxConnFile->scf_AddrFile = NULL; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + + DBGPRINT(CREATE, INFO, + ("SpxConnDisAssociate: %lx from address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + if (NT_SUCCESS(status)) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + } + + return(status); +} + + + + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + BUGBUG: + We need to have another timer that will be started on the connection + if the tdi client indicated a timeout value. 0 -> we do not start such + a timer, -1 implies, we let our connection timeout values do their thing. + Any other value will forcibly shutdown the connect process, when the timer + fires. + +Return Value: + + +--*/ + +{ + PTDI_REQUEST_KERNEL_CONNECT pParam; + TDI_ADDRESS_IPX UNALIGNED * pTdiAddr; + PNDIS_PACKET pCrPkt; + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_ADDR pSpxAddr; + BOOLEAN locksHeld = TRUE; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Unpack the connect parameters + pParam = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(pRequest); + pTdiAddr= SpxParseTdiAddress( + pParam->RequestConnectionInformation->RemoteAddress); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: Remote SOCKET %lx on %lx.%lx\n", + pTdiAddr->Socket, + pSpxConnFile, + pRequest)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + do + { + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SPX2 requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SOCK_STREAM requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + } while (FALSE); + + if (!NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + if (pFindRouteReq) + { + SpxFreeMemory(pFindRouteReq); + } + + return(status); + } + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile) && + ((pSpxConnFile->scf_LocalConnId = spxConnGetId()) != 0)) + { + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_CONNECTING); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + + // Move connection from inactive list to non-inactive list. + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // This should never happen! + KeBugCheck(0); + } + + // Put connection in the non-inactive list. Connection id must be set. + SPX_INSERT_ADDR_ACTIVE( + pSpxAddr, + pSpxConnFile); + + // Insert in the global connection tree on device + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + // Store the remote address in the connection. + // !!NOTE!! We get both the network/socket in network form. + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) = + *((UNALIGNED ULONG *)(&pTdiAddr->NetworkAddress)); + + RtlCopyMemory( + pSpxConnFile->scf_RemAddr+4, + pTdiAddr->NodeAddress, + 6); + + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)) = + *((UNALIGNED USHORT *)(&pTdiAddr->Socket)); + + // Ok, we are all set, build connect packet, queue it into connection + // with the connect request. Ndis buffer already describes this memory + // Build IPX header. + + pCrPkt = NULL; // so it knows to allocate one. + + SpxPktBuildCr( + pSpxConnFile, + pSpxAddr, + &pCrPkt, + SPX_SENDPKT_IDLE, + SPX2_CONN(pSpxConnFile)); + + if (pCrPkt != NULL) + { + // Remember the request in the connection + // + // Dont queue for the failure case since we complete it in SpxInternalDispatch. + // + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + SpxConnQueueSendPktTail(pSpxConnFile, pCrPkt); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pIpxSpxHdr->hdr_DestNode, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNode); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pIpxSpxHdr->hdr_DestNode+4)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NETWORK %lx\n", + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet))); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NODE %02x-%02x-%02x-%02x-%02x-%02x\n", + pFindRouteReq->fr_FindRouteReq.Node[0], pFindRouteReq->fr_FindRouteReq.Node[1], + pFindRouteReq->fr_FindRouteReq.Node[2], pFindRouteReq->fr_FindRouteReq.Node[3], + pFindRouteReq->fr_FindRouteReq.Node[4], pFindRouteReq->fr_FindRouteReq.Node[5])); + + pFindRouteReq->fr_FindRouteReq.Identifier = IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // We wont force a rip for every connection. Only if its not + // in the IPX database. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // Reference for the find route. So that abort connect wont + // free up the connection until we return from here. + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + status = STATUS_PENDING; + } + else + { + // Abort connect attempt. + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + CTEAssert(pSpxConnFile->scf_ConnectReq == NULL); + + locksHeld = FALSE; + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (NT_SUCCESS(status)) + { + // Start off the find route request, We send the packet in completion. + // The verify reference is kept until the connect request completes. + // If connecting to network 0 we don't do this, proceed to find + // route completion which will send the request on very card. + + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + SpxFindRouteComplete( + &pFindRouteReq->fr_FindRouteReq, + TRUE); + + } else { + + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + } + else + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + SpxFreeMemory(pFindRouteReq); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + We assume the connection passed in is already associated with an address. + If it is not, we will die! Is that ok? + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle1, lockHandle2; + PSPX_ADDR pSpxAddr; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + + if (NT_SUCCESS(status)) + { + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + + // Move connection from inactive list to listening list. + if (NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // Put connection in the listening list. + SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile); + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + else + { + // This should never happen! + KeBugCheck(0); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle1); + } + + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_ADDR pSpxAddr; + NTSTATUS status; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: %lx\n", pSpxConnFile)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return (status); + } + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (NT_SUCCESS(status)) + { + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + status = STATUS_INVALID_CONNECTION; + if ((SPX_CONN_LISTENING(pSpxConnFile)) && + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_RECDREQ)) + { + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Call acceptcr now. + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: Accepted\n")); + + status = STATUS_PENDING; + } + else + { + // Free all locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + } + + // Remove reference. Note: Listen reference will exist if ok. And that will + // be transferred to the fact that the connection is active when accepted. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + If active, we do the following. + If informative disconnect, just remember the request in the connection. + We do not ref for request. Assume it will always be checked for when + changing from disconnect to idle. + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + SPX_SENDREQ_TYPE reqType; + int numDerefs = 0; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Deref unless the disc request gets queued in as a send request. + numDerefs++; + + DBGPRINT(CONNECT, DBG, + ("spxConnDisconnect: %lx On %lx when %lx.%lx %lx Params %lx\n", + pRequest, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile), + SPX_DISC_STATE(pSpxConnFile), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC), + pParam->RequestFlags)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnDisconnect: %lx\n", pSpxConnFile)); + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + switch (pParam->RequestFlags) + { + case TDI_DISCONNECT_WAIT: + + // If informative disconnect, just remember in the connection. + status = STATUS_INVALID_CONNECTION; + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + + break; + + case TDI_DISCONNECT_ABORT: + case TDI_DISCONNECT_RELEASE: + + // NOTE! We don't honor the async disconnect symantics of tdi + // but map them to an abortive disconnect. + // NOTE! If our send list is not empty but our client tries to + // do a orderly release, we just queue the ord rel as a send + // data request. In process ack, we check for the next packet + // to not be a ord rel before giving up on window closure. + // NOTE! For spx1 connection, map TDI_DISCONNECT_RELEASE to + // TDI_DISCONNECT_ABORT (Informed disconnect) + + if (!SPX2_CONN(pSpxConnFile)) + { + pParam->RequestFlags = TDI_DISCONNECT_ABORT; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // Since we are not a timer disconnect, then we need to keep + // retrying the disconnect packet. Change state to DISCONN if this + // is not an orderly release or we previously received an orderly + // release and are now confirming it. + // Retry timer will now keep sending out the disconnect packet. + + reqType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_ORDREL); + reqType = SPX_REQ_ORDREL; + } + else + { + // Abortive disconnect + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_IDISC); + numDerefs++; + + spxConnAbortSends( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Abort all receives if we are informed disconnect. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Since we released the lock, a remote IDISC could have come + // in in which case we really don't want to queue in the disc + // request. Instead, we set it as the disc request in the + // connection if one is not already there. + if (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDisconnect: DISC not POST! %lx.%lx\n", + pSpxConnFile, SPX_DISC_STATE(pSpxConnFile))); + + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + break; + } + } + + // !NOTE + // AbortSends might leave send requests around as packets might + // have been with ipx at the time. That is why SendComplete should + // never call AbortSends but must call AbortPkt else it may complete + // the following disconnect request prematurely. + + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = reqType; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Do not deref the connection, it is taken by the pending request + numDerefs--; + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandleConn); + + lockHeld = FALSE; + } + + status = STATUS_PENDING; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + status = STATUS_SUCCESS; + break; + + case SPX_CONNFILE_DISCONN: + + // When we queue in a disconnect as a send request, we expect + // to be able to set it into the scf_DiscReq when it is done. + // So we don't use scf_DiscReq here. This will be a problem if + // the client has a InformDiscReq pending, and a remote disconnect + // comes in, *and* the client then does a disc. We will be completing + // the request with STATUS_INVALID_CONNECTION. + status = STATUS_INVALID_CONNECTION; + if (pParam->RequestFlags != TDI_DISCONNECT_RELEASE) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + + // + // If this is a disconnect for a connection which was already + // disconnected (but AFD's disconnect handler was not called + // because the connfile could not be placed in the inactive list), + // set this flag so that the disconnect is not called from + // ConnInactivate now that the disconnect has occured here. + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // If this was an SPXI connection where we indicated TDI_DISCONNECT_RELEASE + // to AFD, the ref count was bumped up to indicate a wait for local disconnect + // from AFD. Now that we have this disconnect, deref the connection file. Now + // we are ready to truly inactivate this connection file. + // + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + lockHeld = FALSE; + + SpxConnFileDereference(pSpxConnFile, CFREF_DISCWAITSPX); + } + } + + break; + + default: + + // Should never happen! + status = STATUS_INVALID_CONNECTION; + } + + break; + + default: + + status = STATUS_INVALID_PARAMETER; + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + DBGPRINT(CONNECT, INFO, + ("SpxConnDisconnect: returning for %lx.%lx\n", pSpxConnFile, status)); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_SEND pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(SEND, DBG, + ("SpxConnSend: %lx.%lx.%lx.%lx\n", + pSpxConnFile, pRequest, pParam->SendLength, pParam->SendFlags)); + + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + DBGPRINT(SEND, INFO, + ("Send: %lx.%lx.%lx\n", + pParam->SendLength, pParam->SendFlags, pRequest)); + + status = STATUS_PENDING; + do + { + if (SPX_CONN_ACTIVE(pSpxConnFile) && + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED))) + { + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + DBGPRINT(SEND, INFO, + ("%lx\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + } + else + { + // + // [SA] Bug #14655 + // Return the correct error message in case a send fails due to remote disconnect + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + status = STATUS_REMOTE_DISCONNECT ; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + break; + } + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize(pSpxConnFile, TRUE, lockHandleConn); + lockHeld = FALSE; + } + + } while (FALSE); + + + if (lockHeld) + { + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + CTELockHandle lockHandle; + BOOLEAN fLockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnReceive: %lx.%lx\n", pSpxConnFile, pRequest)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_ACTIVE(pSpxConnFile) && + !(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))) + { + status = STATUS_PENDING; + + // This routine adds its own reference. + SpxConnQueueRecv(pSpxConnFile, pRequest); + + // If recv pkt queue is non-empty then we have buffered data. Call + // process pkts/receives. + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) + { + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS Status; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + CTELockHandle lockHandle; + PIPX_SPXCONNSTATUS_DATA pGetStats; + PSPX_CONN_FILE pSpxConnFile = NULL; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(pRequest); + if (NdisBuffer == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer( + REQUEST_NDIS_BUFFER(pRequest), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + return STATUS_NOT_SUPPORTED; + } + + // Make sure we have enough room for just the header not + // including the data. + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, buffer too small\n")); + + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + // Make sure that the correct file object is being used. + switch (NwlinkAction->OptionType) + { + case NWLINK_OPTION_CONNECTION: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_CONNECTION_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not connection file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return(Status); + + break; + + case NWLINK_OPTION_ADDRESS: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not address file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(Status); + + break; + + default: + + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, option type %d\n", + NwlinkAction->OptionType)); + + return STATUS_INVALID_HANDLE; + } + + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + + Status = STATUS_SUCCESS; + + DBGPRINT(ACTION, INFO, + ("SpxConnAction: Option %x\n", NwlinkAction->Option)); + + switch (NwlinkAction->Option) + { + + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MSPX_SETDATASTREAM: + + if (pSpxConnFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + if (DataLength >= 1) + { + DBGPRINT(ACTION, INFO, + ("%lx: MIPX_SETSENDPTYPE %x\n", + pSpxConnFile, NwlinkAction->Data[0])); + + pSpxConnFile->scf_DataType = NwlinkAction->Data[0]; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MSPX_SENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_SENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break ; + + case MSPX_NOSENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_NOSENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_GETSTATS: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_GETSTATS\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + USHORT TempRetryCount; + + // + // Status fields are returned in network order. + // + + pGetStats = (PIPX_SPXCONNSTATUS_DATA)&NwlinkAction->Data[0]; + + switch (SPX_MAIN_STATE(pSpxConnFile)) { + case SPX_CONNFILE_LISTENING: pGetStats->ConnectionState = 1; break; + case SPX_CONNFILE_CONNECTING: pGetStats->ConnectionState = 2; break; + case SPX_CONNFILE_ACTIVE: pGetStats->ConnectionState = 3; break; + case SPX_CONNFILE_DISCONN: pGetStats->ConnectionState = 4; break; + default: pGetStats->ConnectionState = 0; + } + pGetStats->WatchDogActive = 1; // Always 1 + GETSHORT2SHORT( // scf_LocalConnId is in host order + &pGetStats->LocalConnectionId, + &pSpxConnFile->scf_LocalConnId); + pGetStats->RemoteConnectionId = pSpxConnFile->scf_RemConnId; + + GETSHORT2SHORT(&pGetStats->LocalSequenceNumber, &pSpxConnFile->scf_SendSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAckNumber, &pSpxConnFile->scf_RecvSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAllocNumber, &pSpxConnFile->scf_SentAllocNum); + GETSHORT2SHORT(&pGetStats->RemoteAckNumber, &pSpxConnFile->scf_RecdAckNum); + GETSHORT2SHORT(&pGetStats->RemoteAllocNumber, &pSpxConnFile->scf_RecdAllocNum); + + pGetStats->LocalSocket = pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket; + + RtlZeroMemory(pGetStats->ImmediateAddress, 6); + + // Remote network returned in net order. + *((ULONG UNALIGNED *)pGetStats->RemoteNetwork) = + *((ULONG UNALIGNED *)pSpxConnFile->scf_RemAddr); + + RtlCopyMemory( + pGetStats->RemoteNode, + &pSpxConnFile->scf_RemAddr[4], + 6); + + pGetStats->RemoteSocket = *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)); + + TempRetryCount = (USHORT)pSpxConnFile->scf_WRetryCount; + GETSHORT2SHORT(&pGetStats->RetransmissionCount, &TempRetryCount); + GETSHORT2SHORT(&pGetStats->EstimatedRoundTripDelay, &pSpxConnFile->scf_BaseT1); + pGetStats->RetransmittedPackets = 0; + pGetStats->SuppressedPacket = 0; + + DBGPRINT(ACTION, INFO, + ("SSeq %lx RSeq %lx RecdAck %lx RemAllocNum %lx\n", + pGetStats->LocalSequenceNumber, + pGetStats->LocalAckNumber, + pGetStats->RemoteAckNumber, + pGetStats->RemoteAllocNumber)); + + DBGPRINT(ACTION, INFO, + ("LocalSkt %lx RemSkt %lx LocConnId %lx RemConnId %lx\n", + pGetStats->LocalSocket, + pGetStats->RemoteSocket, + pGetStats->LocalConnectionId, + pGetStats->RemoteConnectionId)); + } + else + { + Status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + break; + + case MSPX_NOACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_NOACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_ACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_ACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + // The Option was not supported, so fail. + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (Status != STATUS_SUCCESS) { + DBGPRINT(ACTION, ERR, + ("Nwlink action %lx failed, status %lx\n", + NwlinkAction->Option, Status)); + } + +#endif + + if (pSpxConnFile) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + if (pSpxAddrFile) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + return Status; +} + + + + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + ULONG Timeout; + NTSTATUS status = STATUS_BAD_NETWORK_PATH; + + pSendResd = pSpxConnFile->scf_SendListHead; + pCrPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: %lx.%d\n", + pSpxConnFile, FoundRoute)); + +#if defined(_PNP_POWER) + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + // Here we are going to send on every NIC ID. We adjust the + // timeout down so that a full run through all the NIC IDs will + // take one normal timeout. We don't adjust the timer below + // 100 ms however. + + Timeout = (PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR) / SpxDevice->dev_Adapters; + if (Timeout < (HALFSEC_TO_MS_FACTOR/5)) { + Timeout = HALFSEC_TO_MS_FACTOR / 5; + } + + } else { + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; + } +#endif + + + // Timeout value is in half-seconds + if ((FoundRoute) && + ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + Timeout, + pSpxConnFile)) != 0)) + { + // Add a reference for the connect timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + +#if 0 + { + int i; + char *address = pFrReq->fr_FindRouteReq.LocalTarget.MacAddress; + + DbgPrint("FIND ROUTE LOCALTARGET.MAC:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + + address = pSpxConnFile->scf_RemAddr+4; + DbgPrint("SPX DESTINATION ADDRESS:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + } + + DbgPrint("NIC Id %lx\n", pFrReq->fr_FindRouteReq.LocalTarget.NicId); +#endif + + // If the mac address in local target is all zeros, fill it with our + // destination address. Also if this is a connect to network 0 fill + // it in with the destination address, and further down we will loop + // through all possible NIC IDs. + if (((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + || + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + // We are all set to go ahead with the connect. + // Timer is started on connection + status = STATUS_SUCCESS; + +#if defined(_PNP_POWER) + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT) * SpxDevice->dev_Adapters; + } else { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } +#endif _PNP_POWER + + SPX_CONN_SETFLAG(pSpxConnFile, + (SPX_CONNFILE_C_TIMER | SPX_CONNECT_SENTREQ)); + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + pSpxConnFile->scf_AckLocalTarget= pFrReq->fr_FindRouteReq.LocalTarget; + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#if defined(_PNP_POWER) + pSpxConnFile->scf_LocalTarget.NicHandle.NicId = (USHORT)ITERATIVE_NIC_ID; + pSpxConnFile->scf_AckLocalTarget.NicHandle.NicId = (USHORT)ITERATIVE_NIC_ID; +#else + pSpxConnFile->scf_LocalTarget.NicId = 1; + pSpxConnFile->scf_AckLocalTarget.NicId = 1; +#endif _PNP_POWER + } + + // We will be giving the packet to ipx. + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pCrPkt, pSendResd); + } + + if (!NT_SUCCESS(status)) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + DBGPRINT(CONNECT, ERR, + ("SpxConnConnectFindRouteComplete: FAILED on %lx.%d\n", + pSpxConnFile, FoundRoute)); + + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + } + + // Remove the reference for the call. + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + BOOLEAN fDisconnect = TRUE; + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + + DBGPRINT(CONNECT, DBG, + ("SpxConnActiveFindRouteComplete: %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // If we are disconnecting, just remove the reference and exit. + if (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE) + { + fDisconnect = FALSE; + + // We are here if either the wdog or the retry timer did a find + // route. We need to save the info from the find route if it was + // successful and just restart the timers. + if (FoundRoute) + { + // If the mac address in local target is all zeros, fill it with our + // destination address. + if ((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnActiveFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + } + + // Depending on state restart the wdog or retry timer. Add reference + // for it. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRY: + + // Set state to SPX_SEND_RETRYWD + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRYWD); + + // Start retry timer. + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Start watchdog timer. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_IDLE: + case SPX_SEND_PACKETIZE: + + // Do nothing, remove reference and leave. + break; + + default: + + KeBugCheck(0); + } + } + + if (fDisconnect) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnActiveFindRouteComplete: DISCONNECT %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // Abortive disc will reset the funky state if necessary. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + LockHandle, + FALSE); // [SA] Bug #15249 + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + We enter this routine during the connection attempt. We could be at any + stage of sending either the CR or the SN packet. If we have reached the end of + the retry count, we need to know the substate at that point. For a CR, we give + up trying to connect, and for a SN we try the next lower packet size or if we + have reached the minimum packet size, we give up the connect. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN fAbort = FALSE, locksHeld = FALSE, sendPkt = FALSE; + PREQUEST pRequest = NULL; + + // Get all locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnConnectTimer: Entered\n")); + + do + { + if ((!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) || + (!SPX_CONN_CONNECTING(pSpxConnFile) && + !SPX_CONN_LISTENING(pSpxConnFile))) + { + TimerShuttingDown = TRUE; + } + + if (TimerShuttingDown) + { + break; + } + + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // There should be only one packet in list, the cr. + CTEAssert(pSpxConnFile->scf_SendListHead == + pSpxConnFile->scf_SendListTail); + + pSendResd = pSpxConnFile->scf_SendListHead; + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, + NDIS_PACKET, + ProtocolReserved); + + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // No luck, we need to complete connect request with failure + ++SpxDevice->dev_Stat.NotFoundFailures; + fAbort = TRUE; + break; + } + + // We need to resend the packet + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + break; + } + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_NEG: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_W_SETUP: + default: + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: state is W_Setup %lx\n", + pSpxConnFile)); + + KeBugCheck(0); + } + } + else + { + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_SETUP: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. Have an abort + // kind of routine. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + default: + + KeBugCheck(0); + + } + } + + } while (FALSE); + + if (fAbort) + { + CTEAssert(!sendPkt); + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: Expired for %lx\n", pSpxConnFile)); + + spxConnAbortConnect( + pSpxConnFile, + STATUS_BAD_NETWORK_PATH, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (sendPkt) + { + CTEAssert(!fAbort); + +#if !defined(_PNP_POWER) + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) { + + // we are sending to all NICs because this is the initial + // connect frame and the remote network is 0. + + pSpxConnFile->scf_LocalTarget.NicId = (USHORT) + ((pSpxConnFile->scf_LocalTarget.NicId % SpxDevice->dev_Adapters) + 1); + + // we pass this a valid packet in pPkt, so it knows to + // just refresh the header and not update the protocol + // reserved variables. + + SpxPktBuildCr( + pSpxConnFile, + pSpxConnFile->scf_AddrFile->saf_Addr, + &pPkt, + 0, // state will not be updated + SPX2_CONN(pSpxConnFile)); + + } +#endif !_PNP_POWER + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + if (TimerShuttingDown || fAbort) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); + } + + return(TIMER_REQUEUE_CUR_VALUE); +} + + + + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + This is started on a connection right after the CR or the CR ack is received. + During the connection establishment phase, it does nothing other than decrement + the retry count and upon reaching 0, it aborts the connection. When it goes off + and finds the connection is active, it sends a probe. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + PNDIS_PACKET pProbe = NULL; + BOOLEAN lockHeld, fSpx2 = SPX2_CONN(pSpxConnFile), + fDisconnect = FALSE, fFindRoute = FALSE, fSendProbe = FALSE; + + DBGPRINT(CONNECT, INFO, + ("spxConnWatchdogTimer: Entered\n")); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + do + { + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // If the retry timer is active on this connection, and the watchdog + // timer happens to fire, just requeue ourselves for spx2. For spx1, + // we go ahead with sending a probe. Retry timer does the same things + // watchdog does for spx2. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // Squash the race condition where a disconnect request is never + // packetized, because the send state was not IDLE. + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: POST IDISC %lx\n", + pSpxConnFile)); + + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: PKT POST IDISC %lx\n", + pSpxConnFile)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle); + + lockHeld = FALSE; + break; + } + } + + if (!fSpx2) + { + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + fSendProbe = TRUE; + } + else + { + fDisconnect = TRUE; + } + + break; + } + + // SPX2 connection. Watchdog algorithm needs to do lots of goody + // stuff. If retry is active, just requeue ourselves. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + break; + + // There is a race between watchdog and retry if its started. Who + // ever changes the state first gets to go do its thing. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Enter WD state only if we fired for the second time witout + // an ack. This prevents PACKETIZE from blocking due to being + // in a non-idle state. + CTEAssert(pSpxConnFile->scf_WRetryCount != 0); + if ((pSpxConnFile->scf_WRetryCount)-- != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)) + { + // We enter the WD state. Build and send a probe. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_WD); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + + fSendProbe = TRUE; + break; + + case SPX_SEND_PACKETIZE: + + // Do nothing. + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRYWD: + case SPX_SEND_RENEG: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + // Do nothing. Send timer got in first. + DBGPRINT(CONNECT, DBG1, + ("SpxConnWDogTimer: When retry fired %lx\n", + pSpxConnFile)); + + break; + + case SPX_SEND_WD: + + // Decrement count. If not zero, send a probe. If half the + // count is reached, stop timer and call find route. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + if (pSpxConnFile->scf_WRetryCount != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)/2) + { + fSendProbe = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network) = + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6); + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnWDogTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + } + else + { + fDisconnect = TRUE; + } + + break; + + default: + + KeBugCheck(0); + } + + break; + + case SPX_CONNFILE_CONNECTING: + + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) || + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_NEG)) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_CONNFILE_LISTENING: + + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + default: + + // Should never happen! + KeBugCheck(0); + } + + } while (FALSE); + + if (fSendProbe) + { + CTEAssert(lockHeld); + CTEAssert(!fDisconnect); + + DBGPRINT(CONNECT, DBG1, + ("spxConnWatchdogTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + fSpx2); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + } + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + CTEAssert(!fSendProbe); + + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + + // If spx2, check if we need to do anything special. + // AbortiveDisc will reset funky state if needed. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fFindRoute) + { + CTEAssert(!fSendProbe); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + + if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return((TimerShuttingDown ? TIMER_DONT_REQUEUE : TIMER_REQUEUE_CUR_VALUE)); +} + + + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn; + PIPXSPX_HDR pSendHdr; + PNDIS_PACKET pPkt; + PNDIS_PACKET pProbe = NULL; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + USHORT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; + BOOLEAN lockHeld, fResendPkt = FALSE, fDisconnect = FALSE, + fFindRoute = FALSE, fBackoffTimer = FALSE; + PREQUEST pRequest = NULL; + + DBGPRINT(CONNECT, INFO, + ("spxConnRetryTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + do + { + // If timer is not up, no send pkts, just return. + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL)) + { +#if DBG + if ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL) + { + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // In all other cases, reenqueue with potentially modified reenqueue + // time. + reenqueueTime = pSpxConnFile->scf_BaseT1; + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: BaseT1 %lx on %lx\n", + pSpxConnFile->scf_BaseT1, pSpxConnFile)); + + // If an ack for a packet was processed while we were out, reset + // retry count and return. Or if we are packetizing, return. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) + { + break; + } + else if ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_RetrySeqNum != pSendResd->sr_SeqNum)) + { + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + break; + } + + // If packet is still with IPX, requeue for next time. + if (pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) + { + break; + } + + CTEAssert(pSendResd != NULL); + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // Do we backoff the timer? + fBackoffTimer = + (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_REXMIT) != 0); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + ++SpxDevice->dev_Stat.ResponseTimerExpirations; + + CTEAssert((ULONG)pSpxConnFile->scf_RRetryCount <= + PARAM(CONFIG_REXMIT_COUNT)); + + DBGPRINT(SEND, DBG1, + ("spxConnRetryTimer: Retry Count %lx on %lx\n", + pSpxConnFile->scf_RRetryCount, pSpxConnFile)); + + fResendPkt = TRUE; + if (pSpxConnFile->scf_RRetryCount-- != 0) + { + // We dont treat the IDISC packet as a data packet, so none + // of the fancy spx2 retry stuff if we are retrying the idisc. + if (SPX2_CONN(pSpxConnFile) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_IDISC)) + { + // We enter the RETRY state. Reference conn for this + // "funky" state. + CTEAssert(SPX2_CONN(pSpxConnFile)); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + fResendPkt = FALSE; + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + } + + break; + + case SPX_SEND_RETRY: + + // When we have reached retry_count/2 limit, start locate route. Do + // not queue ourselves. Handle restarting timer in find route + // completion. If timer starts successfully in find route comp, then + // it will change our state to RETRYWD. + + // Decrement count. If half the count is reached, stop timer and call + // find route. + if (pSpxConnFile->scf_RRetryCount-- != + (LONG)PARAM(CONFIG_REXMIT_COUNT)/2) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Alloc Mem %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4)) = + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnRetryTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + break; + + case SPX_SEND_RETRYWD: + + // Retry a watchdog packet WCount times (initialize to RETRY_COUNT). + // If process ack receives an ack (i.e. actual ack packet) while in + // this state, it will transition the state to RENEG. + // + // If the pending data gets acked while in this state, we go back + // to idle. + DBGPRINT(CONNECT, DBG1, + ("spxConnRetryTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Use watchdog count here. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + TRUE); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + break; + } + } + + // Just set state to retry data packet retry_count/2 times. + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY2); + break; + + case SPX_SEND_RENEG: + + // Renegotiate size. If we give up, goto RETRY3. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + break; + } + + // Send reneg packet, if we get the rr ack, then we resend data + // on queue. Note that each time we goto a new negotiate size, + // we rebuild the data packets. + if (pSpxConnFile->scf_RRetryCount-- == 0) + { + // Reset count. + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnRetryTimer: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + + // Are we at the lowest possible reneg pkt size? If not, try + // next lower. When we do this, we free all pending send + // packets and reset the packetize queue to the first packet. + // Process ack will just do packetize and will not do anything + // more other than resetting state to proper value. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG: %lx - CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // We tried lowest size and failed to receive ack. Just + // retry data packet, and disc if no ack. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(min), RETRY3: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + break; + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(!min): %lx - ATTEMPT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: %lx.%lx.%lx RENEG SEQNUM %lx ACKNUM %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RRetryCount, + pSpxConnFile->scf_MaxPktSize, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + pSpxConnFile->scf_SentAllocNum)); + + // Use first unused data packet sequence number. + SpxPktBuildRr( + pSpxConnFile, + &pPkt, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY)); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + fResendPkt = TRUE; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + } + + break; + + case SPX_SEND_RETRY2: + + // Retry the data packet for remaining amount of RRetryCount. If not + // acked goto cleanup. If ack received while in this state, goto idle. + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 2nd try Resend on %lx\n", + pSpxConnFile)); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_RETRY3: + + // Send data packet for RETRY_COUNT times initialized in RRetryCount + // before state changed to this state. If ok, process ack moves us + // back to PKT/IDLE. If not, we disconnect. + // We are going to resend this packet + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 3rd try Resend on %lx\n", + pSpxConnFile)); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Do nothing. Watchdog timer has fired, just requeue. + break; + + default: + + KeBugCheck(0); + } + + if (fBackoffTimer) + { + // Increase retransmit timeout by 50% upto maximum indicated by + // initial retransmission value. + + reenqueueTime += reenqueueTime/2; + if (reenqueueTime > MAX_RETRY_DELAY) + reenqueueTime = MAX_RETRY_DELAY; + + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = reenqueueTime; + pSpxConnFile->scf_DevT1 = 0; + + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Backed retry on %lx.%lx %lx\n", + pSpxConnFile, pSendResd->sr_SeqNum, reenqueueTime)); + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + + // Do not requeue this timer. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + + // Disconnect the connection. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + else if (fFindRoute) + { + CTEAssert(!fResendPkt); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: Find route on %lx\n", + pSpxConnFile)); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + else if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + reenqueueTime = TIMER_DONT_REQUEUE; + } + + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: Reenqueue time : %lx on %lx\n", + reenqueueTime, pSpxConnFile)); + + return(reenqueueTime); +} + + + + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandleConn; + + DBGPRINT(SEND, INFO, + ("spxConnAckTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (!TimerShuttingDown && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // We didnt have any back traffic, until we do a send from this + // end, send acks immediately. Dont try to piggyback. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + ++SpxDevice->dev_Stat.PiggybackAckTimeouts; + + DBGPRINT(SEND, DBG, + ("spxConnAckTimer: Send ack on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + SpxConnSendAck(pSpxConnFile, lockHandleConn); + } + else + { + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); +} + + + +// +// DISCONNECT ROUTINES +// + + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN IDiscFlag // [SA] Bug #15249 + ) +/*++ + +Routine Description: + + This is called when: + We time out or have insufficient resources - + STATUS_LINK_TIMEOUT/STATUS_INSUFFICIENT_RESOURCES + - We abort everything. Could be from watchdog or retry. Stop both. + + We receive a informed disconnect packet - + STATUS_REMOTE_DISCONNECT + - We abort everything. Ack must be sent by caller as an orphan pkt. + + We receive a informed disconnect ack pkt + STATUS_SUCCESS + - We abort everything + - Abort is done with status success (this completes our disc req in + the send queue) + + NOTE: CALLED UNDER THE CONNECTION LOCK. + +Arguments: +[SA] Bug #15249: Added IDiscFlag to indicate if this is an Informed Disconnect. If so, indicate + TDI_DISCONNECT_RELEASE to AFD so it allows a receive of buffered pkts. This flag is TRUE + only if this routine is called from SpxConnProcessIDisc for SPX connections. + +Return Value: + + +--*/ +{ + int numDerefs = 0; + PVOID pDiscHandlerCtx=NULL; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + BOOLEAN lockHeld = TRUE; + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: %lx - On %lx when %lx\n", + Status, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + switch (Status) { + case STATUS_LINK_TIMEOUT: ++SpxDevice->dev_Stat.LinkFailures; break; + case STATUS_INSUFFICIENT_RESOURCES: ++SpxDevice->dev_Stat.LocalResourceFailures; break; + case STATUS_REMOTE_DISCONNECT: ++SpxDevice->dev_Stat.RemoteDisconnects; break; + case STATUS_SUCCESS: + case STATUS_LOCAL_DISCONNECT: ++SpxDevice->dev_Stat.LocalDisconnects; break; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // For transition from active to disconn. + numDerefs++; + + case SPX_CONNFILE_DISCONN: + + // If we are in any state other than idle/packetize, + // remove the reference for the funky state, and reset the send state to be + // idle. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) + { +#if DBG + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnAbortiveDisc: When DISC STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(CONNECT, DBG1, + ("spxConnAbortiveDisc: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // This can be called when a idisc is received, or if a timer + // disconnect is happening, or if we sent a idisc/ordrel, but the retries + // timed out and we are aborting the connection. + // So if we are already aborting, never mind. + + // + // [SA] Bug #15249 + // SPX_DISC_INACTIVATED indicates a DISC_ABORT'ing connection that has been + // inactivated (connfile removed from active conn. list) + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + break; + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ABORT); + + // Stop all timers. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_RTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } +#if 0 + // + // [SA] We need to call AFD after aborting sends since this connection + // becomes a candidate for re-use as soon as the disconnect handler is + // called. + // We call the disconnect handler when the refcount falls to 0 and the + // connection transitions to the inactive list. + // + + // NOTE! We indicate disconnect to afd *before* aborting sends to avoid + // afd from calling us again with a disconnect. + // Get disconnect handler if we have one. And we have not indicated + // abortive disconnect on this connection to afd. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } +#endif + // + // [SA] Save the IDiscFlag in the Connection. + // + (IDiscFlag) ? + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC) : + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(CONNECT, INFO, + ("spxConnAbortiveDisc: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + + if (!IDiscFlag) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_ABORT); + } + else + { + // + // [SA] bug #15249 + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + spxConnAbortSends( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + lockHeld = FALSE; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + lockHeld = FALSE; + + { + CTELockHandle lockHandleAddr, lockHandleDev; + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Ensure we are still in connecting/listening, else call abortive + // again. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortConnect( + pSpxConnFile, + Status, + lockHandleDev, + lockHandleAddr, + LockHandleConn); + + break; + + case SPX_CONNFILE_ACTIVE: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CHG ACT Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortiveDisc( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn, + FALSE); // [SA] Bug #15249 + + break; + + default: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + break; + } + } + + default: + + // Already disconnected. + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isn/spx/spxcpkt.c b/private/ntos/tdi/isn/spx/spxcpkt.c new file mode 100644 index 000000000..0c606ab0a --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxcpkt.c @@ -0,0 +1,4131 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcpkt.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXCPKT + +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + BOOLEAN fNeg, fSpx2; + TA_IPX_ADDRESS srcIpxAddr; + PTDI_IND_CONNECT connHandler; + USHORT srcConnId, destConnId, destSkt, + pktLen, seqNum, ackNum, allocNum; + PVOID connHandlerCtx; + PREQUEST pListenReq; + PSPX_SEND_RESD pSendResd; + NTSTATUS status; + CTELockHandle lockHandle, lockHandleDev, lockHandleConn; + CONNECTION_CONTEXT connCtx; + PIRP acceptIrp; + PSPX_ADDR pSpxAddr; + PSPX_ADDR_FILE pSpxAddrFile, pSpxRefFile; + PSPX_CONN_FILE pSpxConnFile; + PNDIS_PACKET pCrAckPkt; + BOOLEAN connectAccepted = FALSE, delayAccept = FALSE, + addrLock = FALSE, tdiListen = FALSE; + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // Verify Connect Request + if (((pIpxSpxHdr->hdr_ConnCtrl & (SPX_CC_ACK | SPX_CC_SYS)) != + (SPX_CC_ACK | SPX_CC_SYS)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (seqNum != 0) || + (ackNum != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId != 0xFFFF)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifyCR Failed %lx.%lx\n", + srcConnId, destConnId)); + return; + } + + // Get the destination socket from the header + destSkt = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_DestSkt; + + SpxBuildTdiAddress( + &srcIpxAddr, + sizeof(srcIpxAddr), + (PBYTE)pIpxSpxHdr->hdr_SrcNet, + pIpxSpxHdr->hdr_SrcNode, + pIpxSpxHdr->hdr_SrcSkt); + + // Ok, get the address object this is destined for. + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + pSpxAddr = SpxAddrLookup(SpxDevice, destSkt); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + if (pSpxAddr == NULL) + { + DBGPRINT(RECEIVE, DBG, + ("SpxReceive: No addr for %lx\n", destSkt)); + + return; + } + + fSpx2 = ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2)); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Received connect req! %d.%d\n", + fSpx2, fNeg)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + + // We use a bit setting in the flag to prevent reentering + // per address file. + // + // We first search the list of non-inactive connections on the address + // this packet came in on to see if it is a duplicate. If it is, we just + // resend ack. Note we dont need to scan the global connection list. + status = SpxAddrConnByRemoteIdAddrLock( + pSpxAddr, srcConnId, pIpxSpxHdr->hdr_SrcNet, &pSpxConnFile); + + if (NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Received duplicate connect req! %lx\n", + pSpxConnFile)); + + if (SPX_CONN_ACTIVE(pSpxConnFile) || + (SPX_CONN_LISTENING(pSpxConnFile) && + ((SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) || + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Sending Duplicate CR - ACK! %lx\n", + pSpxConnFile)); + + // Build and send an ack + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)); + + if (pCrAckPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + // Send the CR Ack packet! + if (pCrAckPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + } + } + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // We should return in this if, else addrLock should be set to + // FALSE. + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; + } + + do + { + // New connection request: + // Assume we will be able to accept it and allocate a packet for the ack. + // Walk list of listening connections if any. + + pSpxRefFile = NULL; + if ((pSpxConnFile = pSpxAddr->sa_ListenConnList) != NULL) + { + PTDI_REQUEST_KERNEL_LISTEN pParam; + + DBGPRINT(RECEIVE, INFO, + ("SpxConnIndicate: Listen available!\n")); + + // dequeue connection + pSpxAddr->sa_ListenConnList = pSpxConnFile->scf_Next; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + pListenReq = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + pParam = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(pListenReq); + + // if autoaccept, acceptIrp = listenIrp, get connection id and + // process as we do for an indication. As the connection has a + // listen posted on it, it must have a reference for it. + // + // if !autoaccept, we need to complete the listen irp. + delayAccept = (BOOLEAN)((pParam->RequestFlags & TDI_QUERY_ACCEPT) != 0); + if (delayAccept) + { + // Remove the listen irp and prepare for completion. + // NOTE!! Here we do not remove the listen reference. This will + // be removed if disconnect happens, or if accept + // happens, it is transferred to being ref for connection + // being active. + RemoveEntryList(REQUEST_LINKAGE(pListenReq)); + REQUEST_STATUS(pListenReq) = STATUS_SUCCESS; + REQUEST_INFORMATION(pListenReq) = 0; + } + + // Are we ok with spx2? + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)) || + !fSpx2) + { + // We better use spx only. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + fSpx2 = fNeg = FALSE; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + connectAccepted = TRUE; + tdiListen = TRUE; + } + else + { + // No listens available. Check for connect handlers. + // Walk list of address files indicating to each until accepted. + for (pSpxAddrFile = pSpxAddr->sa_AddrFileList; + pSpxAddrFile != NULL; + pSpxAddrFile = pSpxAddrFile->saf_Next) + { + if ((pSpxAddrFile->saf_Flags & (SPX_ADDRFILE_CLOSING | + SPX_ADDRFILE_CONNIND)) || + ((connHandler = pSpxAddrFile->saf_ConnHandler) == NULL)) + { + continue; + } + + // Connect indication in progress, drop all subsequent. + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_CONNIND; + + connHandlerCtx = pSpxAddrFile->saf_ConnHandlerCtx; + SpxAddrFileLockReference(pSpxAddrFile, AFREF_INDICATION); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + // Make the indication. We are always returned an accept irp on + // indication. Else we fail to accept the connection. + status = (*connHandler)( + connHandlerCtx, + sizeof(srcIpxAddr), + (PVOID)&srcIpxAddr, + 0, // User data length + NULL, // User data + 0, // Option length + NULL, // Options + &connCtx, + &acceptIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConn: indicate status %lx.%lx\n", + status, acceptIrp)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_CONNIND; + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEAssert(acceptIrp != NULL); + + // Find the connection and accept the connection using that + // connection object. + SpxConnFileReferenceByCtxLock( + pSpxAddrFile, + connCtx, + &pSpxConnFile, + &status); + + if (!NT_SUCCESS(status)) + { + // The connection object is closing, or is not found + // in our list. The accept irp must have had the same + // connection object. AFD isnt behaving well. + KeBugCheck(0); + } + + // Only for debugging. + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_BYCTX, + CFREF_VERIFY); + + pListenReq = SpxAllocateRequest( + SpxDevice, + acceptIrp); + + IF_NOT_ALLOCATED(pListenReq) + { + acceptIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + break; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pListenReq)); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + connectAccepted = TRUE; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((pSpxAddrFile->saf_Flags & SPX_ADDRFILE_SPX2) && fSpx2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + else + { + fSpx2 = fNeg = FALSE; + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + + break; + } + else + { + // We are not going to accept the connection on this address. + // Try next one. + pSpxRefFile = pSpxAddrFile; + continue; + } + } + } + + } while (FALSE); + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // No need for flag from this point on. + // addrLock = FALSE; + } + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + if (connectAccepted) + { + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_LocalConnId = spxConnGetId(); + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RetrySeqNum = 0; + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + pSpxConnFile->scf_RecdAllocNum = allocNum; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleConnReq: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Set max packet size in connection + SPX_MAX_PKT_SIZE(pSpxConnFile, (fSpx2 && fNeg), fSpx2, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Accept connect req on %lx.%lx..%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + // Aborts must now deal with the lists. Need this as Accept has to + // deal with it. + // Put in non-inactive list. All processing now is equivalent to + // that when a listen is completed on a connection. + if ((!tdiListen) && (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile)))) + { + // Should never happen! + KeBugCheck(0); + } + + SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile); + + // Insert in the global connection tree on device. + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + SPX_CONN_SETFLAG(pSpxConnFile, + ((fNeg ? SPX_CONNFILE_NEG : 0) | + (fSpx2 ? SPX_CONNFILE_SPX2: 0))); + + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } +#if 0 + // + // Make sure that this connection got a local disconnect if it was an SPXI + // connection earlier, in response to a TDI_DISCONNECT_RELEASE. + // + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX] == 0); +#endif + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + SPX_LISTEN_SETSTATE(pSpxConnFile, (delayAccept ? SPX_LISTEN_RECDREQ : 0)); + + if (!delayAccept) + { + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandle, + lockHandleConn); + } + else + { + // Release the locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + // Complete the listen irp. Note reference is not removed. Done when + // accept is posted. + SpxCompleteRequest(pListenReq); + } + } else { + ++SpxDevice->dev_Stat.NoListenFailures; + } + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; +} + + + + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the client side of the connection. + Handles: + Session Negotiate + Sends Session Setup, when recd, handles SS Ack + + STATE MACHINE: + + RR + / \ + / \ ReceivedAck(SPX1Connection) + / \ + / \--------> ACTIVE + / ^ + Send / | + ACK / | + / | + / RecvNeg/NoNeg | + / SendSS | + SA--------->SS---------------+ + ^ | SSAckRecv + | | + +-----+ + RecvNeg + + RR - Received Connect Request + SA - Sent CR Ack + SS - Sent Session Setup + + We move from SA to SS when connection is not negotiatiable and we + immediately send the SS, or when we receive negotiate packet and send the neg + ack and the session setup. + + Note we could receive a negotiate packet when in SS, as our ack to the + negotiate could have been dropped. We deal with this. + +Arguments: + + +Return Value: + + +--*/ + +{ + PNDIS_PACKET pSnAckPkt, pSsPkt = NULL; + PSPX_SEND_RESD pSendResd, pSsSendResd; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN locksHeld = FALSE; + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromClient: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_RECDREQ: + + // Do nothing. + break; + + case SPX_LISTEN_SETUP: + + // Is this a setup ack? If so, yippee. Our ack to a negotiate packet + // could have been dropped, and so we could also get a negotiate packet + // in that case. If that happens, fall through. + // Verify Ss Ack + if (!SPX2_CONN(pSpxConnFile) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: VerifySSACK Failed Checking SN %lx.%lx\n", + srcConnId, destConnId)); + + // Fall through to see if this is a neg packet + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG))) + { + break; + } + } + else + { + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Recd SSACK %lx\n", + pSpxConnFile)); + + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + case SPX_LISTEN_SENTACK: + + // We expect a negotiate packet. + // We should have asked for SPX2/NEG to begin with. + // Verify Sn + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySN Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + CTEAssert(negSize > 0); + + // Build sn ack, abort if we fail + SpxPktBuildSnAck( + pSpxConnFile, + &pSnAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY); + + if (pSnAckPkt == NULL) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SNACK %lx\n", + pSpxConnFile)); + + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pSnAckPkt); + + // The session packet should already be on queue. + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pSsPkt)) + { + KeBugCheck(0); + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SS %lx\n", + pSpxConnFile)); + + pSsSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + + // We need to resend the packet + if ((pSsSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + pSsPkt = NULL; + } + else + { + // Set the size to the neg size indicated in connection. + // This could be lower than the size the packet was build + // with originally. But will never be higher. + pSsSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + spxConnSetNegSize( + pSsPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + } + + // If we are actually LISTEN_SETUP, then send the ss packet also. + // We need to start the connect timer to resend the ss pkt. + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + SPX_LISTEN_SETSTATE(pSpxConnFile, SPX_LISTEN_SETUP); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSnAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnAckPkt, pSendResd); + + // If we have to send the session setup packet, send that too. + if (pSsPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + return; +} + + + + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the server side of the connection. This will both + release the lock and dereference the connection as it sees fit. + + STATE MACHINE: + + SR--CTimerExpires-->IDLE + /| \ + / | \ ReceivedAck(SPX1Connection) + / | \ + / | \--------> ACTIVE + (Neg) / | ^ + Send / |RecvAck | + SN / |NoNeg | + / | | + / | | + / v | + SN--------->WS---------------+ + RecvSNAck RecvSS + + SR - Sent Connect request + SN - Sent Session Negotiate + WS - Waiting for session setup packet + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + BOOLEAN fNeg, fSpx2; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN cTimerCancelled = FALSE, fAbort = FALSE, locksHeld = FALSE; + PNDIS_PACKET pSsAckPkt, pSnPkt, pPkt = NULL; + + // We should get a CR Ack, or if our substate is sent session neg + // we should get a session neg ack, or if we are waiting for session + // setup, we should get one of those. + + fSpx2 = (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // Check if this qualifies as the ack. + // Verify CR Ack + if ((pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (seqNum != 0) || + (ackNum != 0) || + ((pktLen != MIN_IPXSPX_HDRSIZE) && + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen != MIN_IPXSPX2_HDRSIZE))) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleSessPktFromSrv: CRAck Invalid %lx %lx.%lx.%lx\n", + pSpxConnFile, + pktLen, negSize, pIpxSpxHdr->hdr_ConnCtrl)); + + break; + } + + // !!!BUGBUG!!! + // From current spx code base: + // Do we need to send an ack to this ack? In case of SPX only? + // What if this ack is dropped? We need to send an ack, if in future + // we get CONNECT REQ Acks, until we reach active? + // * If they want an ack schedule it. The normal case is for this not + // * to happen, but some Novell mainframe front ends insist on having + // * this. And technically, it is OK for them to do this. + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: Recd CRACK %lx\n", pSpxConnFile)); + + // Grab the remote alloc num/conn id (in net format) + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_RecdAllocNum = allocNum; + + // If we have been looking for network 0, which means the + // packets were sent on all NIC IDs, update our local + // target now that we have received a response. + +#if defined(_PNP_POWER) + if (pSpxConnFile->scf_LocalTarget.NicHandle.NicId == (USHORT)ITERATIVE_NIC_ID) { +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#endif _PNP_POWER + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + } + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + if (!fSpx2 || !fNeg) + { + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + fAbort = TRUE; + break; + } + + // Reference transferred to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // Set max packet size, assume not spx2 or !neg, so pass in FALSE + SPX_MAX_PKT_SIZE(pSpxConnFile, FALSE, FALSE, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPSrv: Accept connect req on %lx.%lx.%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + if (!fSpx2) + { + // Reset spx2 flags. + SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + + // Complete connect request, this free the lock. + // Cancels tdi timer too. Sets all necessary flags. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + if (!fNeg) + { + // Goto W_SETUP + // Reset all connect related flags, also spx2/neg flags. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_NEG); + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + break; + } + + // Reset max packet size. SPX2 and NEG. + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, pIpxSpxHdr->hdr_SrcNet); + + CTEAssert(negSize > 0); + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + pSpxConnFile->scf_MaxPktSize = + MIN(negSize, pSpxConnFile->scf_MaxPktSize); + + pSpxConnFile->scf_MaxPktSize = (USHORT) + MIN(pSpxConnFile->scf_MaxPktSize, PARAM(CONFIG_MAX_PACKET_SIZE)); + + // For SPX2 with negotiation, we set up sneg packet and move to + // SPX_CONNECT_NEG. + SpxPktBuildSn( + pSpxConnFile, + &pSnPkt, + SPX_SENDPKT_IPXOWNS); + + if (pSnPkt == NULL) + { + fAbort = TRUE; + break; + } + + // Queue in packet + SpxConnQueueSendPktTail(pSpxConnFile, pSnPkt); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SN %lx\n", + pSpxConnFile)); + + // Reset retry count for connect timer + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Change state. + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_NEG); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send the packet + pSendResd = (PSPX_SEND_RESD)(pSnPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnPkt, pSendResd); + break; + + case SPX_CONNECT_NEG: + + // We expect a session neg ack. + // We should have asked for SPX2/NEG to begin with. + // Verify SN Ack + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySNACK Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SNACK %lx %lx.%lx\n", + pSpxConnFile, negSize, pSpxConnFile->scf_MaxPktSize)); + + if (negSize > pSpxConnFile->scf_MaxPktSize) + negSize = pSpxConnFile->scf_MaxPktSize; + + // Get the size to use + if (negSize <= pSpxConnFile->scf_MaxPktSize) + { + pSpxConnFile->scf_MaxPktSize = negSize; + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + else + { + // Free the negotiate packet + SpxPktSendRelease(pPkt); + } + + CTEAssert(pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER); + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + // Start the watchdog timer, if fail, we abort. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + // Complete cr with error. + fAbort = TRUE; + break; + } + + // Reference goes to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + // We move to the W_SETUP state. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + } + + break; + + case SPX_CONNECT_W_SETUP: + + // Does this qualify as a session setup packet? + // Verify SS + if (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySS Failed %lx.%lx, %lx %lx.%lx\n", + srcConnId, destConnId, negSize, + pIpxSpxHdr->hdr_ConnCtrl, + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2))); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SS %lx\n", pSpxConnFile)); + + // Copy remote address over into connection (socket could change) + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + + // Build ss ack, abort if we fail + SpxPktBuildSsAck( + pSpxConnFile, + &pSsAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT); + + if (pSsAckPkt == NULL) + { + fAbort = TRUE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SSACK %lx\n", + pSpxConnFile)); + + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + + // We dont queue in the pkt as its already marked as abort. + // Queue in the packet. + // SpxConnQueueSendPktTail(pSpxConnFile, pSsAckPkt); + + // Complete connect, this releases lock. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSsAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsAckPkt, pSendResd); + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (fAbort) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (cTimerCancelled) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine abort a connection (both client and server side) in the middle + of a connection establishment. + + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + PREQUEST pRequest = NULL; + int numDerefs = 0; + + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + KeBugCheck(0); + } +#endif + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { // others should be counted elsewhere + ++SpxDevice->dev_Stat.LocalResourceFailures; + } + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel all timers + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + + // We could be called from disconnect for an accept in which case there + // will be no queued request. But we need to remove the reference if there + // is no request (an accept/listen irp) and listen state is on. + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + // Save req in conn for deref to complete. + pSpxConnFile->scf_ConnectReq = pRequest; + + numDerefs++; + } + else if (SPX_CONN_LISTENING(pSpxConnFile)) + { + numDerefs++; + } + + // Bug #20999 + // Race condition was an abort came in from timer, but the connect state + // was left unchanged. Due to an extra ref on the connection from the + // aborted cr, the state remained so, and then the cr ack came in, and + // a session neg was built and queued on the connection. Although it should + // not have been. And we hit the assert in deref where the connection is + // being reinitialized. Since this can be called for both listening and + // connecting connections, do the below. + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine completes a connection (both client and server side) + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + DBGPRINT(CONNECT, INFO, + ("spxConnCompleteConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + DBGBRK(FATAL); + } +#endif + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel tdi connect timer if we are connecting. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (pSpxConnFile->scf_CRetryCount == (LONG)(PARAM(CONFIG_CONNECTION_COUNT))) { + ++SpxDevice->dev_Stat.ConnectionsAfterNoRetry; + } else { + ++SpxDevice->dev_Stat.ConnectionsAfterRetry; + } + + // Reset all connect related flags + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + break; + + case SPX_CONNFILE_LISTENING: + + if (pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + break; + + default: + + KeBugCheck(0); + + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_ACTIVE); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_IDLE); + + ++SpxDevice->dev_Stat.OpenConnections; + + // Initialize timer values + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = PARAM(CONFIG_INITIAL_RETRANSMIT_TIMEOUT); + pSpxConnFile->scf_DevT1 = 0; + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest) = 0; + + // When we complete the request, we essentially transfer the reference + // to the fact that the connection is active. This will be taken away + // when a Disconnect happens on the connection and we transition from + // ACTIVE to DISCONN. + // numDerefs++; + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + // Complete request + SpxCompleteRequest(pRequest); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +{ + PNDIS_PACKET pSsPkt, pCrAckPkt; + PSPX_SEND_RESD pSendResd; + + BOOLEAN fNeg = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG); + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + DBGPRINT(CONNECT, DBG, + ("spxConnAcceptCr: %lx.%d.%d\n", + pSpxConnFile, fSpx2, fNeg)); + + // Build and queue in packet. + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + fNeg, + fSpx2); + + if ((pCrAckPkt != NULL) && + (pSpxConnFile->scf_LocalConnId != 0)) + { + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + else + { + goto AbortConnect; + } + + + // Start the timer + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + else + { + goto AbortConnect; + } + + + // We start the connect timer for retrying ss which we send out now + // if we are not negotiating. + if (fSpx2) + { + // Build the session setup packet also for spx2. + SpxPktBuildSs( + pSpxConnFile, + &pSsPkt, + (USHORT)(fNeg ? 0 : SPX_SENDPKT_IPXOWNS)); + + if (pSsPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pSsPkt); + } + else + { + goto AbortConnect; + } + + if (!fNeg) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + goto AbortConnect; + } + } + } + + CTEAssert((fNeg && fSpx2) || (!fSpx2 && !fNeg)); + + // For a SPX connection, we immediately become active. This happens + // in the completeConnect routine. !!Dont change it here!! + if (!fSpx2) + { + spxConnCompleteConnect( + pSpxConnFile, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + } + else + { + SPX_LISTEN_SETSTATE( + pSpxConnFile, (fNeg ? SPX_LISTEN_SENTACK : SPX_LISTEN_SETUP)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + } + + // Send the CR Ack packet! + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + + if (fSpx2 && !fNeg) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + return(TRUE); + + +AbortConnect: + + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + + return (FALSE); +} + + + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + The caller needs to set the state to packetize before calling this + routine. This can be called when SEND state is RENEG also. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + + fNormalState - If true, it will assume it can release lock to send, + else, it just builds pkts without releasing lock and + releases lock at end. Used after reneg changes size. + +Return Value: + + +--*/ +{ + PLIST_ENTRY p; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT windowSize; + ULONG dataLen; + USHORT sendFlags; + int numDerefs = 0; + BOOLEAN fFirstPass = TRUE, fSuccess = TRUE; + PREQUEST pRequest; + +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + fNormalState) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + // Build all of the packets. The firsttime flag is used so + // that if we get a 0 byte send, we will send it. The firsttime + // flag will be set and we will build the packet and send it. + // + // FOR SPX1, we cannot trust the remote window size. So we only send + // stuff if window size is greater than 0 *AND* we do not have any pending + // sends. Dont get in here if we are ABORT. Dont want to be handling any + // more requests. + while((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ABORT) && + ((pRequest = pSpxConnFile->scf_ReqPkt) != NULL) && + ((pSpxConnFile->scf_ReqPktSize > 0) || fFirstPass)) + { + fFirstPass = FALSE; + windowSize = pSpxConnFile->scf_RecdAllocNum - + pSpxConnFile->scf_SendSeqNum + 1; + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: WINDOW %lx for %lx\n", + windowSize, pSpxConnFile)); + + + DBGPRINT(SEND, DBG, + ("REMALLOC %lx SENDSEQ %lx\n", + pSpxConnFile->scf_RecdAllocNum, + pSpxConnFile->scf_SendSeqNum)); + + + CTEAssert(windowSize >= 0); + + // Disconnect/Orderly release is not subject to window closure. + if ((pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) && + (((windowSize == 0) && SPX2_CONN(pSpxConnFile)) || + (!SPX2_CONN(pSpxConnFile) && + (pSpxConnFile->scf_SendSeqListHead != NULL)))) + { + break; + } + + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) + { + CTEAssert(pRequest == pSpxConnFile->scf_ReqPkt); + + // Get data length + dataLen = (ULONG)MIN(pSpxConnFile->scf_ReqPktSize, + (pSpxConnFile->scf_MaxPktSize - + ((SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)))); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: %lx Sending %lx Size %lx Req %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + dataLen, + pSpxConnFile->scf_ReqPkt, + pSpxConnFile->scf_ReqPktSize)); + + // Build data packet. Handles 0-length for data. Puts in seq num in + // send resd section of packet also. + sendFlags = + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | + SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | + ((!SPX2_CONN(pSpxConnFile) || (windowSize == 1)) ? + SPX_SENDPKT_ACKREQ : 0)); + + if (dataLen == pSpxConnFile->scf_ReqPktSize) + { + // Last packet of send, ask for a ack. + sendFlags |= (SPX_SENDPKT_LASTPKT | SPX_SENDPKT_ACKREQ); + if ((pSpxConnFile->scf_ReqPktFlags & TDI_SEND_PARTIAL) == 0) + sendFlags |= SPX_SENDPKT_EOM; + } + + SpxPktBuildData( + pSpxConnFile, + &pPkt, + sendFlags, + (USHORT)dataLen); + } + else + { + dataLen = 0; + + DBGPRINT(SEND, DBG, + ("Building DISC packet on %lx ReqPktSize %lx\n", + pSpxConnFile, pSpxConnFile->scf_ReqPktSize)); + + // Build informed disc/orderly rel packet, associate with request + SpxPktBuildDisc( + pSpxConnFile, + pRequest, + &pPkt, + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | SPX_SENDPKT_LASTPKT), + (UCHAR)((pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) ? + SPX2_DT_ORDREL : SPX2_DT_IDISC)); + } + + if (pPkt != NULL) + { + // If we were waiting to send an ack, we don't have to as we are + // piggybacking it now. Cancel ack timer, get out. + if (fNormalState && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Piggyback happening for %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // We are sending data, allow piggybacks to happen. + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + if (SpxTimerCancelEvent(pSpxConnFile->scf_ATimerId, FALSE)) + { + numDerefs++; + } + } + + if (pSpxConnFile->scf_ReqPktType != SPX_REQ_DATA) + { + // For a disconnect set the state + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + numDerefs++; + } + else if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_ORDREL) + { + CTEAssert((SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_ACTIVE) || + (SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_DISCONN)); + + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + } + else + { + CTEAssert( + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)); + } + } + else + { + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + CTEAssert(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC); + + // Note we have send the idisc here. + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_IDISC); + } + } + } + else + { + fSuccess = FALSE; + break; + } + + + // Queue in packet, reference request for the packet + SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt); + REQUEST_INFORMATION(pRequest)++; + + pSpxConnFile->scf_ReqPktSize -= dataLen; + pSpxConnFile->scf_ReqPktOffset += dataLen; + + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx Size after pkt %lx.%lx\n", + pSpxConnFile->scf_ReqPkt, pSpxConnFile->scf_ReqPktSize, + dataLen)); + + // Even if window size if zero, setup next request is current one + // is done. We are here only after we have packetized this send req. + if (pSpxConnFile->scf_ReqPktSize == 0) + { + // This request has been fully packetized. Either go on to + // next request or we are done packetizing. + p = REQUEST_LINKAGE(pRequest); + if (p->Flink == &pSpxConnFile->scf_ReqLinkage) + { + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx done, no more\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktOffset = 0; + pRequest = NULL; + } + else + { + pRequest = LIST_ENTRY_TO_REQUEST(p->Flink); + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Req done, setting next %lx.%lx\n", + pRequest, pParam->SendLength)); + + DBGPRINT(SEND, INFO, + ("-%lx-\n", + pRequest)); + + // Set parameters in connection for another go. + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if ((pSpxConnFile->scf_ReqPktSize = pParam->SendLength) == 0) + { + // Another zero length send. + fFirstPass = TRUE; + } + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + fFirstPass = TRUE; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + } + } + + if (fNormalState) + { + // Send the packet if we are not at the reneg state + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + ++SpxDevice->dev_Stat.DataFramesSent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesSent, + dataLen); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if retry timer needs to be started. + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER))) + { + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(SEND, ERR, + ("SpxConnPacketize: Failed to start retry timer\n")); + + fSuccess = FALSE; + break; + } + } + } + + // Dont overwrite an error state. + if (((fNormalState) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE)) || + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) && + (pSpxConnFile->scf_SendSeqListHead == NULL))) + { + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, ERR, + ("COULD NOT PACKETIZE AFTER RENEG %lx\n", pSpxConnFile)); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(fSuccess); +} + + + + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_RECEIVE pParam; + NTSTATUS status = STATUS_PENDING; + + if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + pParam = (PTDI_REQUEST_KERNEL_RECEIVE)REQUEST_PARAMETERS(pRequest); + pSpxConnFile->scf_CurRecvReq = pRequest; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = pParam->ReceiveLength; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnQueueRecv: %lx.%lx\n", pRequest, pParam->ReceiveLength)); + + // Reference connection for this recv. + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + InsertTailList( + &pSpxConnFile->scf_RecvLinkage, + REQUEST_LINKAGE(pRequest)); + + // RECV irps have no creation references. + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + + // State to receive_posted if we are idle. + if (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_POSTED); + } + + return; +} + + + + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile + ) +{ + CTELockHandle lockHandleInter; + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + + InitializeListHead(&ReqList); + + DBGPRINT(RECEIVE, DBG, + ("spxConnCompletePended: PENDING RECV REQUESTS IN DONE LIST! %lx\n", + pSpxConnFile)); + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + p = pSpxConnFile->scf_RecvDoneLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + +#if DBG + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_RECEIVE) + { + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } + } +#endif + + SpxCompleteRequest(pRequest); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + // If we are not already in ack queue, queue ourselves in starting + // ack timer. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // First start ack timer. + if ((pSpxConnFile->scf_ATimerId = + SpxTimerScheduleEvent( + spxConnAckTimer, + 100, + pSpxConnFile)) != 0) + { + // Reference connection for timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + ++SpxDevice->dev_Stat.PiggybackAckQueued; + } + } + + return; +} + + + + + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendAck: ACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an ack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + FALSE, + 0); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendNack: NACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an nack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + TRUE, + NumToSend); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + CTELockHandle interLockHandle; + USHORT seqNum = 0, ackNum; + int numDerefs = 0; + BOOLEAN fLastPkt, lockHeld = TRUE, fAbort = FALSE, + fResetRetryTimer, fResendPkt = FALSE, fResetSendQueue = FALSE; + + if (pIpxSpxHdr != NULL) + { + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + // Ack numbers should already be set in connection! + if (SPX2_CONN(pSpxConnFile)) + { + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRYWD: + + // Did we receive an ack for pending data? If so, we goto + // idle and process the ack. + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RETRYWD %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + else + { + // Ok, we received an ack for our probe retry, goto + // renegotiate packet size. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + } + + break; + + case SPX_SEND_RENEG: + + // We better have a data packet in the list. + CTEAssert(pSpxConnFile->scf_SendSeqListHead); + +#if DBG + if ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx.%lx.%lx RENEGACK SEQNUM %lx ACKNUM %lx EXPSEQ %lx\n", + pSpxConnFile, + pIpxSpxHdr->hdr_ConnCtrl, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT), + seqNum, + ackNum, + (pSpxConnFile->scf_SendSeqListHead->sr_SeqNum + 1))); + } +#endif + + // Verify we received an RR ack. If so, we set state to + // SEND_RETRY3. First repacketize if we need to. + if ((SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT)) && + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2))) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG! NEW %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + // Dont allow anymore reneg packet acks to be looked at. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Also set the new send sequence number. + pSpxConnFile->scf_SendSeqNum = + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1); + + // Get the max packet size we will really use. Retry timer + // could have sent other sizes by now, so we can't depend + // on whats set. + // Remember max packet size in connection. + GETSHORT2SHORT( + &pSpxConnFile->scf_MaxPktSize, &pIpxSpxHdr->hdr_NegSize); + + // Basic sanity checking on the max packet size. + if (pSpxConnFile->scf_MaxPktSize < SPX_NEG_MIN) + pSpxConnFile->scf_MaxPktSize = SPX_NEG_MIN; + + // Get ready to reset the send queue. + fResetSendQueue = TRUE; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG DONE : RETRY3 %lx.%lx MP %lx!\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + else + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: DUPLICATE RENEG ACK %lx!\n", + pSpxConnFile)); + } + + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Data acked %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + +#if DBG + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: CONN RESTORED %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + } +#endif + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + break; + + case SPX_SEND_WD: + + // Ok, we received an ack for our watchdog. Done. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + break; + + default: + + break; + } + +#if DBG + if (seqNum != 0) + { + // We have a nack, which contains an implicit ack. + // Instead of nack processing, what we do is we resend a + // packet left unacked after ack processing. ONLY if we + // either enter the loop below (fResetRetryTimer is FALSE) + // or if seqNum is non-zero (SPX2 only NACK) + } +#endif + } + } + + // Once our numbers are updated, we check to see if any of our packets + // have been acked. + fResetRetryTimer = TRUE; + while (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) || + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) || + fResetSendQueue) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + // Reset retry timer + if (fResetRetryTimer) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_RTimerId, + TRUE); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + } + + fResetRetryTimer = FALSE; + } + + // Update the retry seq num. + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pRequest = pSendResd->sr_Request; + +#if DBG + if (fResetSendQueue) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RENEG %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(SEND, DBG, + ("%lx Acked\n", pSendResd->sr_SeqNum)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: %lx Seq %lx Acked Sr %lx Req %lx %lx.%lx\n", + pSpxConnFile, + pSendResd->sr_SeqNum, + pSendResd, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // If this packet is the last one comprising this request, remove request + // from queue. Calculate retry time. + fLastPkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_LASTPKT) != 0); + if ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) && + ((pSendResd->sr_State & SPX_SENDPKT_REXMIT) == 0) && + ((pSendResd->sr_SeqNum + 1) == pSpxConnFile->scf_RecdAckNum)) + { + LARGE_INTEGER li, ntTime; + int value; + + // This is the packet which is being acked. Adjust round trip + // timer. + li = pSendResd->sr_SentTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new time + SpxCalculateNewT1(pSpxConnFile, value); + } + } + + if (fLastPkt) + { + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Remove creation reference + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: LASTSEQ # %lx for Req %lx with %lx.%lx\n", + pSendResd->sr_SeqNum, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + } + + // Dequeue the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0); + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Dereference request for the dequeing of the packet + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Packet owned by IPX. What do we do now? Set acked pkt so request + // gets dereferenced in send completion. Note that the packet is already + // off the queue and is floating at this point. + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: IPXOWNS Pkt %lx with %lx.%lx\n", + pPkt, pRequest, REQUEST_STATUS(pRequest))); + + pSendResd->sr_State |= SPX_SENDPKT_ACKEDPKT; + } + + if (SPX2_CONN(pSpxConnFile) && + (REQUEST_MINOR_FUNCTION(pRequest) == TDI_DISCONNECT) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ORDREL_ACKED); + + // If we had received an ordrel in the meantime, we need + // to disconnect. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + fAbort = TRUE; + } + } + + // All packets comprising a request have been acked! + if (REQUEST_INFORMATION(pRequest) == 0) + { + CTELockHandle lockHandleInter; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + + } + } +#if DBG + else if (fLastPkt) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: ReqFloating %lx.%lx\n", + pSpxConnFile, pRequest)); + } +#endif + } + + // See if we reset the send queue and repacketize. + if (fResetSendQueue) + { + // Reset send queue and repacketize only if pkts left unacked. + if (pSpxConnFile->scf_SendSeqListHead) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resetting send queue %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + spxConnResetSendQueue(pSpxConnFile); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Repacketizing %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SpxConnPacketize(pSpxConnFile, FALSE, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + else + { + // We just go back to idle state now. + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: All packets acked reneg ack! %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + } + } + + // See if we resend a packet. + if ((seqNum != 0) && + !fAbort && + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)) + { + PIPXSPX_HDR pSendHdr; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + } + + // Push into packetize only if we received an ack. And if there arent any + // packets already waiting. Probably retransmit happening. + if (!fAbort && + SPX_CONN_ACTIVE(pSpxConnFile) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_ReqPkt != NULL) && + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_PKTQ)) && + ((pSpxConnFile->scf_SendSeqListHead) == NULL) && + (!SPX2_CONN(pSpxConnFile) || + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL)))) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: Recd ack pktizng\n", pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + SpxConnFileLockReference(pSpxConnFile, CFREF_PKTIZE); + + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + else if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(TRUE); +} + + + + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + USHORT seqNum, ackNum, allocNum, maxPktSize; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + // The remote sent us a renegotiate request. We need to send an ack back + // ONLY if we have not acked a data packet with that same sequence number. + // This is guaranteed by the fact that we will not accept the reneg request + // if we have already acked a data packet with the same seq num, as our + // receive seq number would be incremented already. + // + // Note that if we have pending send packets we may end up doing a reneg + // also. + + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&maxPktSize, &pIpxSpxHdr->hdr_PktLen); + + // If the received seq num is less than the expected receive sequence number + // we ignore this request. + if (!UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + (seqNum != pSpxConnFile->scf_RecvSeqNum)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx ERROR RENSEQ %lx RECVSEQ %lx %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + return; + } + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RENSEQ %lx RECVSEQ %lx MAXPKT %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, maxPktSize)); + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + // Set RenegAckAckNum before calling buildrrack. If a previous reneg + // request was received with a greater maxpktsize, send an ack with + // that maxpktsize. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD)) + { + pSpxConnFile->scf_RenegAckAckNum = pSpxConnFile->scf_RecvSeqNum; + pSpxConnFile->scf_RenegMaxPktSize= maxPktSize; + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + + // Adjust sentallocnum now that recvseqnum might have moved up. + pSpxConnFile->scf_SentAllocNum += + (seqNum - pSpxConnFile->scf_RenegAckAckNum); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM ADJUSTED %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + } + + // The recvseqnum for the reneg is always >= the renegackacknum. + pSpxConnFile->scf_RecvSeqNum = seqNum; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RESET RECVSEQ %lx SavedACKACK %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RenegAckAckNum)); + + // Build and send an ack. + SpxPktBuildRrAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + pSpxConnFile->scf_RenegMaxPktSize); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } +#if DBG + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendRenegReqAck: Could not allocate!\n")); + } +#endif + + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + int numDerefs = 0; + PNDIS_PACKET pPkt = NULL; + BOOLEAN lockHeld = TRUE, fAbort = FALSE; + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ORDREL_ACKED) + { + fAbort = TRUE; + } + + // Send an ack if one was asked for. And we are done with this pkt + // Update seq numbers and stuff. + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for this. Ordinary spx2 ack. + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // Get disconnect handler if we have one. And have not indicated + // abortive disconnect on this connection to afd. + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler =pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx=pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + // We abort any receives here if !fAbort else we abort conn. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + else + { + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle); + + lockHeld = FALSE; + } + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for the idisc. Need to modify data type + // and reset sys bit on ack. + // BUG #12344 - Fixing this led to the problem where we queue in + // the pkt below, but AbortSends could already have been called + // => this packet stays on queue without a ref, conn gets freed + // underneath, and in the sendcomplete we crash when this send + // completes. + // + // Fix is to setup this pkt as a aborted pkt to start with. + + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + PIPXSPX_HDR pSendHdr; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + pSendHdr->hdr_ConnCtrl &= ~SPX_CC_SYS; + pSendHdr->hdr_DataType = SPX2_DT_IDISC_ACK; + + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // We better not have any received pkts, we ignore disconnect + // pkts when that happens. + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + +#if DBG + if (pSpxConnFile->scf_SendSeqListHead != NULL) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA/DISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendListHead, + pSpxConnFile->scf_SendSeqListHead)); + } +#endif + + // Call abortive disconnect on connection. + + // + // [SA] bug #15249 + // This is an informed disconnect, hence pass DISCONNECT_RELEASE to AFD (TRUE in last param) + // + // + // We pass true only in the case of an SPX connection. SPX2 connections follow the + // exact semantics of Informed Disconnect. + // + if (!SPX2_CONN(pSpxConnFile)) { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + TRUE); + } else { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + CTEAssert(pSendResd != NULL); + + pRequest = pSendResd->sr_Request; + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = pSendResd->sr_Request; + pSpxConnFile->scf_ReqPktOffset = pSendResd->sr_Offset; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset SEND Req to %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest, pParam->SendLength)); + + // Set parameters in connection for another go. Size parameter is + // original size - offset at this point. + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength - + pSpxConnFile->scf_ReqPktOffset; + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset DISC Req to %lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest)); + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: Seq Num for %lx is now %lx\n", + pSpxConnFile, pSpxConnFile->scf_SendSeqNum)); + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + do + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + KeBugCheck(0); + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + } while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL); + + return; +} + + + + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Called to abort either a sequenced or a non-sequenced packet ONLY from + send completion. + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + if ((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) + { + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + // BUG #11626 - request is already removed from list. + // RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Free the packet + SpxPktSendRelease(pPkt); + SpxConnFileDereference(pSpxConnFile, CFREF_ABORTPKT); + + if (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + // First go through the non-seq pkt queue.Just set abort flag if owned by ipx + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) == 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt if its not already. + // We only do this check for the non-sequenced packets. + // BUG #12344 (see SpxRecvDiscPacket()) + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) == 0) + { + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + } + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + // If retry timer state is on, then we need to reset and deref. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + DBGPRINT(SEND, DBG1, + ("spxConnAbortSends: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // Remove creation references on all sends. + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + p = pSpxConnFile->scf_ReqLinkage.Flink; + while (p != &pSpxConnFile->scf_ReqLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on. Its complete or abort list for it. + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Aborting Send Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG1, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PBYTE pData; + ULONG dataLen; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + // Reset the current receive request values + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + // If we have any buffered data, abort it. + // Buffered data that is 0 bytes long (only eom) may not have a ndis + // buffer associated with it. + while ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) + { + if ((pSpxConnFile->scf_RecvListHead = pRecvResd->rr_Next) == NULL) + { + pSpxConnFile->scf_RecvListTail = NULL; + } + + pNdisPkt = (PNDIS_PACKET) + CONTAINING_RECORD(pRecvResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(RECEIVE, DBG1, + ("spxConnAbortRecvs: %lx in bufferlist on %lx\n", + pSpxConnFile, pNdisPkt)); + + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + SpxFreeMemory(pData); + NdisFreeBuffer(pNdisBuffer); + } + + // Packet consumed. Free it up. + numDerefs++; + + // Free the ndis packet + SpxPktRecvRelease(pNdisPkt); + } + + // If packets are on this queue, they are waiting for transfer data to + // complete. Can't do much about that, just go and remove creation refs + // on the receives. + if (!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + p = pSpxConnFile->scf_RecvLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(RECEIVE, DBG1, + ("SpxRecvAbort: Aborting Recv Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxRecvAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + numDerefs++; + + SpxCompleteRequest(pRequest); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +#if 0 + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT startSeqNum; + BOOLEAN fLockHeld = TRUE, fDone = FALSE; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + if (pSendResd) + { + startSeqNum = pSendResd->sr_SeqNum; + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: StartSeqNum %lx for resend on %lx\n", + startSeqNum, pSpxConnFile)); + + while (spxConnGetPktBySeqNum(pSpxConnFile, startSeqNum++, &pPkt)) + { + CTEAssert(pPkt != NULL); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if (!(pSendResd->sr_State & SPX_SENDPKT_IPXOWNS)) + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx resent on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + + // We are going to send this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_REXMIT); + } + else + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx owned by ipx on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + break; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + fLockHeld = FALSE; + + // If pkt has the ack bit set, we break. + fDone = ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) != 0); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + if (fDone) + { + break; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fLockHeld = TRUE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return; +} +#endif diff --git a/private/ntos/tdi/isn/spx/spxcutil.c b/private/ntos/tdi/isn/spx/spxcutil.c new file mode 100644 index 000000000..fa2105e42 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxcutil.c @@ -0,0 +1,1736 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcutil.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXCUTIL + +// +// Minor utility routines +// + + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + int i; + + // We go thru table and see if this is the minimum size or if it + // can go down further. Return true if it is not the minimum size. + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[0])); + + if ((ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE) <= SpxMaxPktSize[0]) + return(FALSE); + + for (i = SpxMaxPktSizeIndex-1; i > 0; i--) + { + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[i])); + + if (SpxMaxPktSize[i] < (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE)) + break; + } + + *pNegSize = (USHORT)(SpxMaxPktSize[i] + MIN_IPXSPX2_HDRSIZE); + + DBGPRINT(CONNECT, ERR, + ("spxConnCheckNegSize: Trying Size %lx Min size possible %lx\n", + *pNegSize, SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)); + + return(TRUE); +} + + + + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + UINT bufCount; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + + CTEAssert(Size > 0); + NdisQueryPacket(pPkt, NULL, &bufCount, &pNdisBuffer, NULL); + CTEAssert (bufCount == 3); + + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisAdjustBufferLength(pNdisBuffer, Size); + + // Change it in send reserved + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Len = (Size + MIN_IPXSPX2_HDRSIZE); + + // Change in ipx header + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + PUTSHORT2SHORT((PUSHORT)&pIpxSpxHdr->hdr_PktLen, (Size + MIN_IPXSPX2_HDRSIZE)); + + // Change in the neg packet field of the header. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + (Size + MIN_IPXSPX2_HDRSIZE)); + + DBGPRINT(CONNECT, DBG, + ("spxConnSetNegSize: Setting size to %lx Hdr %lx\n", + Size, (Size + MIN_IPXSPX2_HDRSIZE))); + + return; +} + + + + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, pListHead, pListTail; + PSPX_SEND_RESD pSendResd; + BOOLEAN removed = TRUE; + + // If we are sequenced or not decides which list we choose. + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pListHead = pSpxConnFile->scf_SendSeqListHead; + pListTail = pSpxConnFile->scf_SendSeqListTail; + } + else + { + pListHead = pSpxConnFile->scf_SendListHead; + pListTail = pSpxConnFile->scf_SendListTail; + } + + // Most often, we will be at the head of the list. + if (pListHead == pSendResd) + { + if ((pListHead = pSendResd->sr_Next) == NULL) + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pSendResd)); + + pListTail = NULL; + } + } + else + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pSendResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->sr_Next == pSendResd) + { + if ((pSr->sr_Next = pSendResd->sr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->sr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pSpxConnFile->scf_SendSeqListHead = pListHead; + pSpxConnFile->scf_SendSeqListTail = pListTail; + } + else + { + pSpxConnFile->scf_SendListHead = pListHead; + pSpxConnFile->scf_SendListTail = pListTail; + } + } + + return(removed); +} + + + + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_RECV_RESD pSr, pListHead, pListTail; + PSPX_RECV_RESD pRecvResd; + BOOLEAN removed = TRUE; + + pRecvResd = (PSPX_RECV_RESD)(pPkt->ProtocolReserved); + pListHead = pSpxConnFile->scf_RecvListHead; + pListTail = pSpxConnFile->scf_RecvListTail; + + // Most often, we will be at the head of the list. + if (pListHead == pRecvResd) + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pRecvResd)); + + if ((pListHead = pRecvResd->rr_Next) == NULL) + { + pListTail = NULL; + } + } + else + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pRecvResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->rr_Next == pRecvResd) + { + if ((pSr->rr_Next = pRecvResd->rr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->rr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + pSpxConnFile->scf_RecvListHead = pListHead; + pSpxConnFile->scf_RecvListTail = pListTail; + } + + return(removed); +} + + + + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = (fSeqList ? + &pSpxConnFile->scf_SendSeqListHead : + &pSpxConnFile->scf_SendListHead); + + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_Type == PktType) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, INFO, + ("SpxConnFindByType: %lx.%lx.%d\n", pSr,*ppPkt, fSeqList)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = &pSpxConnFile->scf_SendSeqListHead; + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_SeqNum == SeqNum) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, DBG, + ("SpxConnFindBySeq: %lx.%lx.%d\n", pSr,*ppPkt, SeqNum)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +USHORT +spxConnGetId( + VOID + ) +/*++ + +Routine Description: + + This must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + BOOLEAN wrapped = FALSE; + USHORT startConnId, retConnId; + + startConnId = SpxDevice->dev_NextConnId; + + // Search the global active list. + do + { + if ((SpxDevice->dev_NextConnId >= startConnId) && wrapped) + { + retConnId = 0; + break; + } + + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + continue; + } + + // BUGBUG: Later this be a tree. + for (pSpxConnFile = SpxDevice->dev_GlobalActiveConnList[ + SpxDevice->dev_NextConnId & NUM_SPXCONN_HASH_MASK]; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_GlobalActiveNext) + { + if (pSpxConnFile->scf_LocalConnId == SpxDevice->dev_NextConnId) + { + break; + } + } + + // Increment for next time. + retConnId = SpxDevice->dev_NextConnId++; + + // Ensure we are still legal. We could return if connfile is null. + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + } + + if (pSpxConnFile != NULL) + { + continue; + } + + break; + + } while (TRUE); + + return(retConnId); +} + + + + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_Next; + break; + } + + ppRemConn = &pRemConn->scf_Next; + } + + if (pRemConn == NULL) + { + DBGBRK(FATAL); + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_AssocNext; + break; + } + + ppRemConn = &pRemConn->scf_AssocNext; + } + + if (pRemConn == NULL) + { + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + + // For now, its just a linear list. + pSpxConnFile->scf_GlobalActiveNext = + SpxDevice->dev_GlobalActiveConnList[index]; + + SpxDevice->dev_GlobalActiveConnList[index] = + pSpxConnFile; + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + PSPX_CONN_FILE pC, *ppC; + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + NTSTATUS status = STATUS_SUCCESS; + + // For now, its just a linear list. + for (ppC = &SpxDevice->dev_GlobalActiveConnList[index]; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalActiveNext; + break; + } + + ppC = &pC->scf_GlobalActiveNext; + } + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_GlobalNext = SpxGlobalConnList; + SpxGlobalConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_CONN_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalConnList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalNext; + break; + } + + ppC = &pC->scf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + + + +#if 0 + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_PktNext = SpxPktConnList; + SpxPktConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxPktConnList) != NULL) + { + SpxPktConnList = SpxPktConnList->scf_PktNext; + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + + + + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_ProcessRecvNext = SpxRecvConnList; + SpxRecvConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxRecvConnList) != NULL) + { + SpxRecvConnList = SpxRecvConnList->scf_ProcessRecvNext; + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromRecv: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + +#endif + + +// +// Reference/Dereference routines +// + + +#if DBG + +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFile + + + + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE CONNECTION LOCK HELD. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFileLock + +#endif + + + + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) + +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE DEVICE LOCK HELD!!! + + All active connections should be on the device active list. Later, + this data structure will be a tree, caching the last accessed + connection. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = + SpxDevice->dev_GlobalActiveConnList[ConnId & NUM_SPXCONN_HASH_MASK]; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_GlobalActiveNext) + { + if (pSpxChkConn->scf_LocalConnId == ConnId) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYID); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; + +} + + + + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!! + + Returns a referenced connection file with the associated context and + address file desired. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_Next) + { + if ((pSpxChkConn->scf_ConnCtx == Ctx) && + (pSpxChkConn->scf_AddrFile == pSpxAddrFile)) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYCTX); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; +} + + + + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + + try + { + if ((pConnFile->scf_Size == sizeof (SPX_CONN_FILE)) && + (pConnFile->scf_Type == SPX_CONNFILE_SIGNATURE)) + { + CTEGetLock (&pConnFile->scf_Lock, &LockHandle); + if (!SPX_CONN_FLAG(pConnFile, SPX_CONNFILE_CLOSING)) + { + SpxConnFileLockReference(pConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyConnFile: A %lx closing\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock (&pConnFile->scf_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxVerifyConnFile: AF %lx exception\n", pConnFile)); + + return GetExceptionCode(); + } + + return status; + +} // SpxVerifyConnFile + + + + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyConnectionFile to remove it from the system. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + BOOLEAN fDiscNotIndicated = FALSE; + BOOLEAN fIDiscFlag = FALSE; + BOOLEAN fSpx2; + + CTEAssert(pSpxConnFile->scf_RefCount > 0); + oldvalue = SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + (ULONG)-1, + &pSpxConnFile->scf_Lock); + + CTEAssert (oldvalue > 0); + if (oldvalue == 1) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + LIST_ENTRY discReqList, *p; + PREQUEST pDiscReq; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + PREQUEST pCloseReq = NULL, + pCleanupReq = NULL, + pConnectReq = NULL; + BOOLEAN fDisassoc = FALSE; + + InitializeListHead(&discReqList); + + // We may not be associated at this point. Note: When we are active we + // always have a reference. So its not like we execute this code very often. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (pSpxAddrFile) + { + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + //if (pSpxConnFile->scf_RefCount == 0) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&pSpxConnFile->scf_RefCount, 0, &pSpxConnFile->scf_Lock) == 0) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn is 0 %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // All pending requests on this connection are done. See if we + // need to complete the disconnect phase etc. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_DISCONN: + + // Disconnect is done. Move connection out of all the lists + // it is on, reset states etc. + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn being inactivated %lx\n", + pSpxConnFile)); + + // Time to complete disc requests if present. + // There could be multiple of them. + p = pSpxConnFile->scf_DiscLinkage.Flink; + while (p != &pSpxConnFile->scf_DiscLinkage) + { + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Disc on %lx.%lx\n", + pSpxConnFile, pDiscReq)); + + RemoveEntryList(REQUEST_LINKAGE(pDiscReq)); + + if (REQUEST_STATUS(pDiscReq) == STATUS_PENDING) + { + REQUEST_STATUS(pDiscReq) = STATUS_SUCCESS; + } + + InsertTailList( + &discReqList, + REQUEST_LINKAGE(pDiscReq)); + } + + // + // Note the state here, and check after the conn has been inactivated. + // + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) { + fDiscNotIndicated = TRUE; + } + + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC)) { + fIDiscFlag = TRUE; + } + + fSpx2 = (SPX2_CONN(pSpxConnFile)) ? TRUE : FALSE; + + // + // [SA] Bug #14655 + // Do not try to inactivate an already inactivated connection + // + + if (!(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + spxConnInactivate(pSpxConnFile); + } else { + // + // This is an SPXI connection which has got the local disconnect. + // Reset the flags now. + // + CTEAssert(!fDiscNotIndicated); + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_DISC_SETSTATE(pSpxConnFile, 0); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // [SA] If we were waiting for sends to be aborted and did not indicate this + // disconnect to AFD; and AFD did not call a disconnect on this connection, + // then call the disonnect handler now. + // + if (fDiscNotIndicated) { + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + ULONG discCode = 0; + + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) { + + // + // If this was an SPXI connection, the disconnect state is still + // DISCONN, so if this routine is re-entered, we need to prevent + // a re-indicate to AFD. + // Also, we need to wait for a local disconnect from AFD since + // we indicated a TDI_DISCONNECT_RELEASE. We bump up the ref count + // in this case. + // + if (!fSpx2) { + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) ); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + + if (fIDiscFlag) { + SpxConnFileLockReference(pSpxConnFile, CFREF_DISCWAITSPX); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + } + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + DBGPRINT(CONNECT, INFO, + ("spxDerefConnectionFile: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + if (fIDiscFlag) { + // + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + discCode = TDI_DISCONNECT_RELEASE; + } else { + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + discCode = TDI_DISCONNECT_ABORT; + } + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + discCode); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + } + } + + --SpxDevice->dev_Stat.OpenConnections; + + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + // Get connect/accept request if present. + pConnectReq = pSpxConnFile->scf_ConnectReq; + pSpxConnFile->scf_ConnectReq = NULL; + + spxConnInactivate(pSpxConnFile); + break; + + case SPX_CONNFILE_ACTIVE: + + KeBugCheck(0); + + default: + + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == 0); + break; + } + + // If stopping, disassociate from the address file. Complete + // cleanup request. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn stopping %lx\n", + pSpxConnFile)); + + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + SPX_CONN_RESETFLAG(pSpxConnFile,SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must + // be in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + + DBGPRINT(CREATE, INFO, + ("SpxConnDerefDisAssociate: %lx from addr file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + fDisassoc = TRUE; + } + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + } + + if (fDisassoc) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + + if (pConnectReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Connect on %lx req %lx\n", + pSpxConnFile, pConnectReq)); + + // Status will already be set in here. We should be here only if + // connect is being aborted. + SpxCompleteRequest(pConnectReq); + } + + while (!IsListEmpty(&discReqList)) + { + p = RemoveHeadList(&discReqList); + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(CONNECT, DBG, + ("SpxConnFileDeref: DISC REQ %lx.%lx Completing\n", + pSpxConnFile, pDiscReq)); + + SpxCompleteRequest(pDiscReq); + } + + if (pCleanupReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Cleanup complete %lx req %lx\n", + pSpxConnFile, pCleanupReq)); + + REQUEST_INFORMATION(pCleanupReq) = 0; + REQUEST_STATUS(pCleanupReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCleanupReq); + } + + if (pCloseReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Freed %lx close req %lx\n", + pSpxConnFile, pCloseReq)); + + CTEAssert(pSpxConnFile->scf_RefCount == 0); + + // Remove from the global list + if (!NT_SUCCESS(spxConnRemoveFromGlobalList(pSpxConnFile))) + { + KeBugCheck(0); + } + + // Free it up. + SpxFreeMemory (pSpxConnFile); + + REQUEST_INFORMATION(pCloseReq) = 0; + REQUEST_STATUS(pCloseReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCloseReq); + } + } + + return; + +} // SpxDerefConnectionFile + + + + +VOID +spxConnReInit( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + // Reinit all variables. + pSpxConnFile->scf_Flags2 = 0; + + pSpxConnFile->scf_GlobalActiveNext = NULL; + pSpxConnFile->scf_PktNext = NULL; + pSpxConnFile->scf_CRetryCount = 0; + pSpxConnFile->scf_WRetryCount = 0; + pSpxConnFile->scf_RRetryCount = 0; + pSpxConnFile->scf_RRetrySeqNum = 0; + + pSpxConnFile->scf_CTimerId = + pSpxConnFile->scf_RTimerId = + pSpxConnFile->scf_WTimerId = + pSpxConnFile->scf_TTimerId = + pSpxConnFile->scf_ATimerId = 0; + + pSpxConnFile->scf_LocalConnId = + pSpxConnFile->scf_SendSeqNum = + pSpxConnFile->scf_SentAllocNum = + pSpxConnFile->scf_RecvSeqNum = + pSpxConnFile->scf_RetrySeqNum = + pSpxConnFile->scf_RecdAckNum = + pSpxConnFile->scf_RemConnId = + pSpxConnFile->scf_RecdAllocNum = 0; + +#if DBG + // Initialize so we dont hit breakpoint on seq 0 + pSpxConnFile->scf_PktSeqNum = 0xFFFF; +#endif + + pSpxConnFile->scf_DataType = 0; + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListTail == NULL); + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + pSpxConnFile->scf_ReqPkt = NULL; + + return; +} + + + + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!! Called with dev/addr/connection lock held !!! + +Arguments: + + This gets us back to associate SAVING the state of the STOPPING and + CLOSING flags so that dereference can go ahead and finish those. + +Return Value: + + +--*/ +{ + BOOLEAN fStopping, fClosing, fAborting; + + fStopping = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + fClosing = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + + // + // [SA] Bug #14655 + // Save the disconnect states so that a proper error can be given in the case of + // a send after a remote disconnection. + // + + // + // Bug #17729 + // Dont retain these flags if a local disconnect has already occured. + // + + fAborting = (!SPX2_CONN(pSpxConnFile) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)); + +#if DBG + pSpxConnFile->scf_GhostFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_GhostFlags2 = pSpxConnFile->scf_Flags2; + pSpxConnFile->scf_GhostRefCount = pSpxConnFile->scf_RefCount; +#endif + + // Clear all flags, go back to the assoc state. Restore stop/close + pSpxConnFile->scf_Flags = SPX_CONNFILE_ASSOC; + SPX_CONN_SETFLAG(pSpxConnFile, + ((fStopping ? SPX_CONNFILE_STOPPING : 0) | + (fClosing ? SPX_CONNFILE_CLOSING : 0))); + + // + // [SA] bug #14655 + // In order to avoid a re-entry, mark connection as SPX_DISC_INACTIVATED + // + if (fAborting) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_INACTIVATED); + } + + // Remove connection from global list on device + if (!NT_SUCCESS(spxConnRemoveFromGlobalActiveList( + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Remove connection from active list on address + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxConnFile->scf_AddrFile->saf_Addr->sa_ActiveConnList, + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Put connection in inactive list on address + SPX_INSERT_ADDR_INACTIVE( + pSpxConnFile->scf_AddrFile->saf_Addr, + pSpxConnFile); + + spxConnReInit(pSpxConnFile); + return; +} diff --git a/private/ntos/tdi/isn/spx/spxdev.c b/private/ntos/tdi/isn/spx/spxdev.c new file mode 100644 index 000000000..431498686 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxdev.c @@ -0,0 +1,242 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDEV + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitCreateDevice) +#pragma alloc_text(PAGE, SpxDestroyDevice) +#endif + + + + +VOID +SpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->dev_RefCount); + + CTEAssert (result >= 0); + + if (result == 0) + { + // Close binding to IPX + SpxUnbindFromIpx(); + + // Set unload event. + KeSetEvent(&SpxUnloadEvent, IO_NETWORK_INCREMENT, FALSE); + } + +} // SpxDerefDevice + + + + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE * DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG DeviceNameOffset; + + + DBGPRINT(DEVICE, INFO, + ("SpxInitCreateDevice - Create device %ws\n", DeviceName->Buffer)); + + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + DBGPRINT(DEVICE, ERR, ("IoCreateDevice failed\n")); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + Device = (PDEVICE)deviceObject; + + DBGPRINT(DEVICE, INFO, ("IoCreateDevice succeeded %lx\n", Device)); + + // Initialize our part of the device context. + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + DeviceNameOffset = sizeof(DEVICE); + + // Copy over the device name. + Device->dev_DeviceNameLen = DeviceName->Length + sizeof(WCHAR); + Device->dev_DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + + RtlCopyMemory( + Device->dev_DeviceName, + DeviceName->Buffer, + DeviceName->Length); + + Device->dev_DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // Initialize the reference count. + Device->dev_RefCount = 1; + +#if DBG + Device->dev_RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->dev_Signature1, "IDC1", 4); + RtlCopyMemory(Device->dev_Signature2, "IDC2", 4); +#endif + + // Set next conn id to be used. + Device->dev_NextConnId = (USHORT)SpxRandomNumber(); + if (Device->dev_NextConnId == 0xFFFF) + { + Device->dev_NextConnId = 1; + } + + DBGPRINT(DEVICE, ERR, + ("SpxInitCreateDevice: Start Conn Id %lx\n", Device->dev_NextConnId)); + + // Initialize the resource that guards address ACLs. + ExInitializeResource (&Device->dev_AddrResource); + + // initialize the various fields in the device context + CTEInitLock (&Device->dev_Interlock); + CTEInitLock (&Device->dev_Lock); + KeInitializeSpinLock (&Device->dev_StatInterlock); + KeInitializeSpinLock (&Device->dev_StatSpinLock); + + Device->dev_State = DEVICE_STATE_CLOSED; + Device->dev_Type = SPX_DEVICE_SIGNATURE; + Device->dev_Size = sizeof (DEVICE); + + Device->dev_Stat.Version = 0x100; + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} // SpxCreateDevice + + + + +VOID +SpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&Device->dev_AddrResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} // SpxDestroyDevice diff --git a/private/ntos/tdi/isn/spx/spxdrvr.c b/private/ntos/tdi/isn/spx/spxdrvr.c new file mode 100644 index 000000000..0e9935d1a --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxdrvr.c @@ -0,0 +1,1008 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdrvr.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the SPX/SPXII module of the ISN transport. + +Author: + + Adam Barr (adamba) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDRVR + +// Forward declaration of various routines used in this module. + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath); + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject); + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SpxUnload) +#pragma alloc_text(PAGE, SpxDispatchOpenClose) +#pragma alloc_text(PAGE, SpxDispatch) +#pragma alloc_text(PAGE, SpxDeviceControl) +#pragma alloc_text(PAGE, SpxUnload) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the SPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + UNICODE_STRING deviceName; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN BoundToIpx = FALSE; + + // DBGBRK(FATAL); + + // Initialize the Common Transport Environment. + if (CTEInitialize() == 0) { + return (STATUS_UNSUCCESSFUL); + } + + // We have this #define'd. Ugh, but CONTAINING_RECORD has problem owise. + CTEAssert(NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); + + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + RtlInitUnicodeString(&deviceName, SPX_DEVICE_NAME); + status = SpxInitCreateDevice( + DriverObject, + &deviceName, + &SpxDevice); + + if (!NT_SUCCESS(status)) + { + return(status); + } + + do + { + CTEInitLock (&SpxGlobalInterlock); + CTEInitLock (&SpxGlobalQInterlock); + + // Initialize the unload event + KeInitializeEvent( + &SpxUnloadEvent, + NotificationEvent, + FALSE); + + // !!!The device is created at this point!!! + // Get information from the registry. + status = SpxInitGetConfiguration( + RegistryPath, + &SpxDevice->dev_ConfigInfo); + + if (!NT_SUCCESS(status)) + { + break; + } + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif _PNP_POWER + + // Bind to the IPX transport. + if (!NT_SUCCESS(status = SpxInitBindToIpx())) + { + // BUGBUG: Have ipx name here as second string + LOG_ERROR( + EVENT_TRANSPORT_BINDING_FAILED, + status, + NULL, + NULL, + 0); + + break; + } + + BoundToIpx = TRUE; + +#if !defined(_PNP_POWER) + // Initialize the timer system + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif !_PNP_POWER + + // Initialize the block manager + if (!NT_SUCCESS(status = SpxInitMemorySystem(SpxDevice))) + { + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + break; + } + + // Initialize the driver object with this driver's entry points. + DriverObject->MajorFunction [IRP_MJ_CREATE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] + = SpxDispatch; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] + = SpxDispatchInternal; + DriverObject->DriverUnload = SpxUnload; + + // Initialize the provider info + SpxQueryInitProviderInfo(&SpxDevice->dev_ProviderInfo); + SpxDevice->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + +#if !defined(_PNP_POWER) + // We are open now. + SpxDevice->dev_State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == SpxDevice->dev_State ) { + SpxDevice->dev_State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + } while (FALSE); + + if (!NT_SUCCESS(status) ) + { + // Delete the device and any associated resources created. + if( BoundToIpx ) { + SpxDerefDevice(SpxDevice); + } + SpxDestroyDevice(SpxDevice); + } + + return (status); +} + + + + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. The I/O system will not + call us until nobody above has ST open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + UNREFERENCED_PARAMETER (DriverObject); + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + + // Remove creation reference count on the IPX device object. + SpxDerefDevice(SpxDevice); + + // Wait on the unload event. + KeWaitForSingleObject( + &SpxUnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL); + + // Release the block memory stuff + SpxDeInitMemorySystem(SpxDevice); + SpxDestroyDevice(SpxDevice); + return; +} + + + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // Make sure status information is consistent every time. + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + + Status = SpxDeviceControl (DeviceObject, Irp); + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + // + // Complete the Irp here instead of below. + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } // major function switch + + /* Commented out and re-located to the default case above. + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + */ + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatch + +VOID +SpxAssignControlChannelId( + IN PDEVICE Device, + IN PIRP Request + ) +/*++ + +Routine Description: + + This routine is required to ensure that the Device lock (to protect the ControlChannelId in the Device) + is not taken in a pageable routine (SpxDispatchOpenClose). + + NOTE: SPX returns the ControlChannelId in the Request, but never uses it later when it comes down in a + close/cleanup. The CCID is a ULONG; in future, if we start using this field (as in IPX which uses these Ids + to determine lineup Irps to complete), then we may run out of numbers (since we monotonically increase the CCID); + though there is a low chance of that since we will probably run out of memory before that! Anyhow, if that + happens, one solution (used in IPX) is to make the CCID into a Large Integer and pack the values into the + REQUEST_OPEN_TYPE(Irp) too. + + +Arguments: + + Device - Pointer to the device object for this driver. + + Request - Pointer to the request packet representing the I/O request. + +Return Value: + + None. + +--*/ +{ + CTELockHandle LockHandle; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->dev_CcId); + ++Device->dev_CcId; + if (Device->dev_CcId == 0) { + Device->dev_CcId = 1; + } + + CTEFreeLock (&Device->dev_Lock, LockHandle); +} + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + BOOLEAN found; + PREQUEST Request; + UINT i; + PFILE_FULL_EA_INFORMATION openType; + CONNECTION_CONTEXT connCtx; + + +#if !defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = SpxAddrOpen (Device, Request); + break; + } + + // Connection? + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + if (openType->EaValueLength < sizeof(CONNECTION_CONTEXT)) + { + + DBGPRINT(CREATE, ERR, + ("Create: Context size %d\n", openType->EaValueLength)); + + Status = STATUS_EA_LIST_INCONSISTENT; + break; + } + + connCtx = + *((CONNECTION_CONTEXT UNALIGNED *) + &openType->EaName[openType->EaNameLength+1]); + + Status = SpxConnOpen( + Device, + connCtx, + Request); + + break; + } + + } else { + + // + // Takes a lock in a Pageable routine - call another (non-paged) function to do that. + // + SpxAssignControlChannelId(Device, Request); + + REQUEST_OPEN_TYPE(Request) = (PVOID)SPX_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileClose(Device, Request); + break; + + case TDI_CONNECTION_FILE: + Status = SpxConnClose(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileCleanup(Device, Request); + break; + + case TDI_CONNECTION_FILE: + + Status = SpxConnCleanup(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } // major function switch + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchOpenClose + + + + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // Convert the user call to the proper internal device call. + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + if (Status == STATUS_SUCCESS) { + + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + Status = SpxDispatchInternal (DeviceObject, Irp); + + // + // Return the proper error code here. If SpxDispatchInternal returns an error, + // then we used to map it to pending below; this is wrong since the client above + // us could wait for ever since the IO subsystem does not set the event if an + // error is returned and the Irp is not marked pending. + // + + // Status = STATUS_PENDING; + } else { + + DBGPRINT(TDI, DBG, + ("Unknown Tdi code in Irp: %lx\n", Irp)); + + // + // Complete the Irp.... + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + return Status; + +} // SpxDeviceControl + + + + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PREQUEST Request; + KIRQL oldIrql; + NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; + PDEVICE Device = (PDEVICE)DeviceObject; + + + if (Device->dev_State != DEVICE_STATE_OPEN) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Cancel irp + IoAcquireCancelSpinLock(&oldIrql); + if (!Irp->Cancel) + { + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)SpxTdiCancel); + } + IoReleaseCancelSpinLock(oldIrql); + + if (Irp->Cancel) + return STATUS_CANCELLED; + + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + switch (REQUEST_MINOR_FUNCTION(Request)) + { + case TDI_ACCEPT: + + Status = SpxConnAccept( + Device, + Request); + + break; + + case TDI_SET_EVENT_HANDLER: + + Status = SpxAddrSetEventHandler( + Device, + Request); + + break; + + case TDI_RECEIVE: + + Status = SpxConnRecv( + Device, + Request); + break; + + + case TDI_SEND: + + Status = SpxConnSend( + Device, + Request); + break; + + case TDI_ACTION: + + Status = SpxConnAction( + Device, + Request); + break; + + case TDI_ASSOCIATE_ADDRESS: + + Status = SpxConnAssociate( + Device, + Request); + + break; + + case TDI_DISASSOCIATE_ADDRESS: + + Status = SpxConnDisAssociate( + Device, + Request); + + break; + + case TDI_CONNECT: + + Status = SpxConnConnect( + Device, + Request); + + break; + + case TDI_DISCONNECT: + + Status = SpxConnDisconnect( + Device, + Request); + break; + + case TDI_LISTEN: + + Status = SpxConnListen( + Device, + Request); + break; + + case TDI_QUERY_INFORMATION: + + Status = SpxTdiQueryInformation( + Device, + Request); + + break; + + case TDI_SET_INFORMATION: + + Status = SpxTdiSetInformation( + Device, + Request); + + break; + + // Something we don't know about was submitted. + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (Status != STATUS_PENDING) + { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IoAcquireCancelSpinLock(&oldIrql); + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(oldIrql); + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchInternal + + + + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cancellation of IO requests + +Arguments: + + +Return Value: +--*/ +{ + PREQUEST Request; + PSPX_ADDR_FILE pSpxAddrFile; + PSPX_ADDR pSpxAddr; + PDEVICE Device = (PDEVICE)DeviceObject; + CTELockHandle connectIrql; + CTELockHandle TempIrql; + PSPX_CONN_FILE pSpxConnFile; + + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + return; + } + + DBGPRINT(TDI, ERR, + ("SpxTdiCancel: Cancel irp called %lx.%lx\n", + Irp, REQUEST_OPEN_CONTEXT(Request))); + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) + { + case TDI_CONNECTION_FILE: + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + CTEGetLock(&pSpxConnFile->scf_Lock, &connectIrql); + + // + // Swap the irql + // + TempIrql = connectIrql; + connectIrql = Irp->CancelIrql; + Irp->CancelIrql = TempIrql; + + IoReleaseCancelSpinLock (Irp->CancelIrql); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + // + // This releases the lock + // + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + connectIrql, + FALSE); // [SA] bug #15249 + } + } + +// SpxConnStop((PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request)); + break; + + case TDI_TRANSPORT_ADDRESS_FILE: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + pSpxAddr = pSpxAddrFile->saf_Addr; + SpxAddrFileStop(pSpxAddrFile, pSpxAddr); + break; + + default: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + break; + + } + +} diff --git a/private/ntos/tdi/isn/spx/spxerror.c b/private/ntos/tdi/isn/spx/spxerror.c new file mode 100644 index 000000000..7d2cc7444 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxerror.c @@ -0,0 +1,316 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxerror.c + +Abstract: + + This module contains code which provides error logging support. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXERROR + +LONG SpxLastRawDataLen = 0; +NTSTATUS SpxLastUniqueErrorCode = STATUS_SUCCESS; +NTSTATUS SpxLastNtStatusCode = STATUS_SUCCESS; +ULONG SpxLastErrorCount = 0; +LONG SpxLastErrorTime = 0; +BYTE SpxLastRawData[PORT_MAXIMUM_MESSAGE_LENGTH - \ + sizeof(IO_ERROR_LOG_PACKET)] = {0}; + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + + int insertionStringLength = 0; + + // Filter out events such that the same event recurring close together does not + // cause errorlog clogging. The scheme is - if the event is same as the last event + // and the elapsed time is > THRESHOLD and ERROR_CONSEQ_FREQ simulataneous errors + // have happened, then log it else skip + if ((UniqueErrorCode == SpxLastUniqueErrorCode) && + (NtStatusCode == SpxLastNtStatusCode)) + { + SpxLastErrorCount++; + if ((SpxLastRawDataLen == RawDataLen) && + (RtlEqualMemory(SpxLastRawData, RawDataBuf, RawDataLen)) && + ((SpxLastErrorCount % ERROR_CONSEQ_FREQ) != 0) && + ((SpxGetCurrentTime() - SpxLastErrorTime) < ERROR_CONSEQ_TIME)) + { + return(FALSE); + } + } + + SpxLastUniqueErrorCode = UniqueErrorCode; + SpxLastNtStatusCode = NtStatusCode; + SpxLastErrorCount = 0; + SpxLastErrorTime = SpxGetCurrentTime(); + if (RawDataLen != 0) + { + SpxLastRawDataLen = RawDataLen; + RtlCopyMemory( + SpxLastRawData, + RawDataBuf, + RawDataLen); + } + + return(TRUE); +} + + + + +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + if (!SpxFilterErrorLogEntry( + EVENT_TRANSPORT_RESOURCE_POOL, + STATUS_INSUFFICIENT_RESOURCES, + (PVOID)&BytesNeeded, + sizeof(BytesNeeded))) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->dev_DeviceNameLen + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + // Convert the error value into a buffer. + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) + { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + RtlCopyMemory( + StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + } +} + + + + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + RawDataBuf - The number of ULONGs of dump data. + + RawDataLen - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[4] = L"Spx"; + + if (!SpxFilterErrorLogEntry( + ErrorCode, + FinalStatus, + RawDataBuf, + RawDataLen)) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + EntrySize += (UCHAR)Device->dev_DeviceNameLen; + } + else + { + EntrySize += sizeof(DriverName); + } + + if (SecondString) + { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)RawDataLen; + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (RawDataLen != 0) + { + RtlCopyMemory(errorLogEntry->DumpData, RawDataBuf, RawDataLen); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + } + else + { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + + if (SecondString) + { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + } + + return; +} diff --git a/private/ntos/tdi/isn/spx/spxmem.c b/private/ntos/tdi/isn/spx/spxmem.c new file mode 100644 index 000000000..9cd400e5b --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxmem.c @@ -0,0 +1,897 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.c + +Abstract: + + This module contains code which implements the memory allocation wrappers. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + Jameel Hyder (jameelh) Initial Version + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, SpxInitMemorySystem) +#pragma alloc_text( PAGE, SpxDeInitMemorySystem) +#endif + +// Define module number for event logging entries +#define FILENUM SPXMEM + +// Globals for this module +// Some block sizes (like NDISSEND/NDISRECV are filled in after binding with IPX) +USHORT spxBlkSize[NUM_BLKIDS] = // Size of each block + { + sizeof(BLK_HDR)+sizeof(TIMERLIST), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +USHORT spxChunkSize[NUM_BLKIDS] = // Size of each Chunk + { + 512-BC_OVERHEAD, // BLKID_TIMERLIST + 512-BC_OVERHEAD, // BLKID_NDISSEND + 512-BC_OVERHEAD // BLKID_NDISRECV + }; + + +// Filled in after binding with IPX +// Reference for below. +// ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ +// (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST +USHORT spxNumBlks[NUM_BLKIDS] = // Number of blocks per chunk + { + ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ + (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +CTELock spxBPLock[NUM_BLKIDS] = { 0 }; +PBLK_CHUNK spxBPHead[NUM_BLKIDS] = { 0 }; + + + + +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + !!! MUST BE CALLED AFTER BINDING TO IPX!!! + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i; + NDIS_STATUS ndisStatus; + + // Try to allocate the ndis buffer pool. + NdisAllocateBufferPool( + &ndisStatus, + &pSpxDevice->dev_NdisBufferPoolHandle, + 20); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + return(STATUS_INSUFFICIENT_RESOURCES); + + for (i = 0; i < NUM_BLKIDS; i++) + CTEInitLock (&spxBPLock[i]); + + // Set the sizes in the block id info arrays. + for (i = 0; i < NUM_BLKIDS; i++) + { + // BUGBUG: Do it. + switch (i) + { + case BLKID_NDISSEND: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_SEND_RESD) + + NDIS_PACKET_SIZE + + IpxMacHdrNeeded + + MIN_IPXSPX2_HDRSIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + case BLKID_NDISRECV: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_RECV_RESD) + + NDIS_PACKET_SIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + default: + + break; + } + + } + + SpxTimerScheduleEvent((TIMER_ROUTINE)spxBPAgePool, + BLOCK_POOL_TIMER, + NULL); +} + + + + +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i, j, NumBlksPerChunk; + PBLK_CHUNK pChunk, pFree; + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + for (pChunk = spxBPHead[i]; + pChunk != NULL; ) + { + DBGPRINT(RESOURCES, ERR, + ("SpxInitMemorySystem: Freeing %lx\n", pChunk)); + + CTEAssert (pChunk->bc_NumFrees == NumBlksPerChunk); + + if ((pChunk->bc_BlkId == BLKID_NDISSEND) || + (pChunk->bc_BlkId == BLKID_NDISRECV)) + { + PBLK_HDR pBlkHdr; + + // We need to free the Ndis stuff for these guys + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + +#ifdef SPX_OWN_PACKETS + // Only need to free the ndis buffer. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + + // + // Free the second MDL also + // + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } +#else + // Need to free both the packet and the buffer. + ppNdisPkt = (PNDIS_PACKET *)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + + NdisUnchainBufferAtFront(*ppNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } + NdisFreePacket(*ppNdisPkt); +#endif + } + } + pFree = pChunk; + pChunk = pChunk->bc_Next; + +#ifndef SPX_OWN_PACKETS + // Free the ndis packet pool in chunk + NdisFreePacketPool((NDIS_HANDLE)pFree->bc_ChunkCtx); +#endif + SpxFreeMemory(pFree); + } + } + + // Free up the ndis buffer pool + NdisFreeBufferPool( + pSpxDevice->dev_NdisBufferPoolHandle); + + return; +} + + + + +PVOID +SpxAllocMem( +#ifdef TRACK_MEMORY_USAGE + IN ULONG Size, + IN ULONG FileLine +#else + IN ULONG Size +#endif // TRACK_MEMORY_USAGE + ) +/*++ + +Routine Description: + + Allocate a block of non-paged memory. This is just a wrapper over ExAllocPool. + Allocation failures are error-logged. We always allocate a ULONG more than + the specified size to accomodate the size. This is used by SpxFreeMemory + to update the statistics. + +Arguments: + + +Return Value: + + +--*/ +{ + PBYTE pBuf; + BOOLEAN zeroed; + + // round up the size so that we can put a signature at the end + // that is on a LARGE_INTEGER boundary + zeroed = ((Size & ZEROED_MEMORY_TAG) == ZEROED_MEMORY_TAG); + + Size = QWORDSIZEBLOCK(Size & ~ZEROED_MEMORY_TAG); + + // Do the actual memory allocation. Allocate eight extra bytes so + // that we can store the size of the allocation for the free routine + // and still keep the buffer quadword aligned. + + if ((pBuf = ExAllocatePoolWithTag(NonPagedPool, Size + sizeof(LARGE_INTEGER) +#if DBG + + sizeof(ULONG) +#endif + ,SPX_TAG)) == NULL) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxAllocMemory: failed - size %lx\n", Size)); + + TMPLOGERR(); + return NULL; + } + + // Save the size of this block in the four extra bytes we allocated. + *((PULONG)pBuf) = (Size + sizeof(LARGE_INTEGER)); + + // Return a pointer to the memory after the size longword. + pBuf += sizeof(LARGE_INTEGER); + +#if DBG + *((PULONG)(pBuf+Size)) = SPX_MEMORY_SIGNATURE; + DBGPRINT(RESOURCES, INFO, + ("SpxAllocMemory: %lx Allocated %lx bytes @%lx\n", + *(PULONG)((PBYTE)(&Size) - sizeof(Size)), Size, pBuf)); +#endif + + SpxTrackMemoryUsage((PVOID)(pBuf - sizeof(LARGE_INTEGER)), TRUE, FileLine); + + if (zeroed) + RtlZeroMemory(pBuf, Size); + + return (pBuf); +} + + + + +VOID +SpxFreeMemory( + IN PVOID pBuf + ) +/*++ + +Routine Description: + + Free the block of memory allocated via SpxAllocMemory. This is + a wrapper around ExFreePool. + +Arguments: + + +Return Value: + + +--*/ +{ + PULONG pRealBuffer; + + // Get a pointer to the block allocated by ExAllocatePool -- + // we allocate a LARGE_INTEGER at the front. + pRealBuffer = ((PULONG)pBuf - 2); + + SpxTrackMemoryUsage(pRealBuffer, FALSE, 0); + +#if DBG + // Check the signature at the end + if (*(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) + != SPX_MEMORY_SIGNATURE) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxFreeMemory: Memory overrun on block %lx\n", pRealBuffer)); + + DBGBRK(FATAL); + } + + *(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) = 0; +#endif + +#if DBG + *pRealBuffer = 0; +#endif + + // Free the pool and return. + ExFreePool(pRealBuffer); +} + + + + +#ifdef TRACK_MEMORY_USAGE + +#define MAX_PTR_COUNT 4*1024 +#define MAX_MEM_USERS 512 +CTELock spxMemTrackLock = {0}; +CTELockHandle lockHandle = {0}; +struct +{ + PVOID mem_Ptr; + ULONG mem_FileLine; +} spxMemPtrs[MAX_PTR_COUNT] = {0}; + +struct +{ + ULONG mem_FL; + ULONG mem_Count; +} spxMemUsage[MAX_MEM_USERS] = {0}; + +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine + ) +/*++ + +Routine Description: + + Keep track of memory usage by storing and clearing away pointers as and + when they are allocated or freed. This helps in keeping track of memory + leaks. + +Arguments: + + +Return Value: + + +--*/ +{ + static int i = 0; + int j, k; + + CTEGetLock (&spxMemTrackLock, &lockHandle); + + if (Alloc) + { + for (j = 0; j < MAX_PTR_COUNT; i++, j++) + { + i = i & (MAX_PTR_COUNT-1); + if (spxMemPtrs[i].mem_Ptr == NULL) + { + spxMemPtrs[i].mem_Ptr = pMem; + spxMemPtrs[i++].mem_FileLine = FileLine; + break; + } + } + + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == FileLine) + { + spxMemUsage[k].mem_Count ++; + break; + } + } + if (k == MAX_MEM_USERS) + { + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == 0) + { + spxMemUsage[k].mem_FL = FileLine; + spxMemUsage[k].mem_Count = 1; + break; + } + } + } + if (k == MAX_MEM_USERS) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: Out of space on spxMemUsage !!!\n")); + + DBGBRK(FATAL); + } + } + else + { + for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--) + { + k = k & (MAX_PTR_COUNT-1); + if (spxMemPtrs[k].mem_Ptr == pMem) + { + spxMemPtrs[k].mem_Ptr = 0; + spxMemPtrs[k].mem_FileLine = 0; + break; + } + } + } + + CTEFreeLock (&spxMemTrackLock, lockHandle); + + if (j == MAX_PTR_COUNT) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: %s\n", Alloc ? "Table Full" : "Can't find")); + + DBGBRK(FATAL); + } +} + +#endif // TRACK_MEMORY_USAGE + + + + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Alloc a block of memory from the block pool package. This is written to speed up + operations where a lot of small fixed size allocations/frees happen. Going to + ExAllocPool() in these cases is expensive. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_HDR pBlk = NULL; + PBLK_CHUNK pChunk, *ppChunkHead; + USHORT BlkSize; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PNDIS_BUFFER pNdisIpxSpxBuffer; + + + CTEAssert (BlockId < NUM_BLKIDS); + + if (BlockId < NUM_BLKIDS) + { + BlkSize = spxBlkSize[BlockId]; + ppChunkHead = &spxBPHead[BlockId]; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = *ppChunkHead; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pChunk->bc_NumFrees > 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Found space in Chunk %lx\n", pChunk)); +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPHits); +#endif + break; + } + } + + if (pChunk == NULL) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Allocating a new chunk for Id %d\n", BlockId)); + +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPMisses); +#endif + pChunk = SpxAllocateMemory(spxChunkSize[BlockId]); + if (pChunk != NULL) + { + LONG i, j; + PBLK_HDR pBlkHdr; + USHORT NumBlksPerChunk; + + NumBlksPerChunk = spxNumBlks[BlockId]; + pChunk->bc_NumFrees = NumBlksPerChunk; + pChunk->bc_BlkId = BlockId; + pChunk->bc_FreeHead = (PBLK_HDR)((PBYTE)pChunk + sizeof(BLK_CHUNK)); + + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Initializing chunk %lx\n", pChunk)); + + // Initialize the blocks in the chunk + for (i = 0, pBlkHdr = pChunk->bc_FreeHead; + i < NumBlksPerChunk; + i++, pBlkHdr = pBlkHdr->bh_Next) + { + NDIS_STATUS ndisStatus; + + pBlkHdr->bh_Next = (PBLK_HDR)((PBYTE)pBlkHdr + BlkSize); + if (BlockId == BLKID_NDISSEND) + { + PBYTE pHdrMem; + +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + // Allocate a ndis buffer descriptor describing hdr memory + // and queue it in. + pHdrMem = (PBYTE)pNdisPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD); + + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem, + IpxMacHdrNeeded); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + + + NdisAllocateBuffer( + &ndisStatus, + &pNdisIpxSpxBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem + IpxMacHdrNeeded, + MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisIpxSpxBuffer); + + + + pSendResd = (PSPX_SEND_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + + // Initialize elements of the protocol reserved structure. + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = SPX_SENDPKT_IDLE; + } + else if (BlockId == BLKID_NDISRECV) + { +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + pRecvResd = (PSPX_RECV_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + // Initialize elements of the protocol reserved structure. + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = SPX_RECVPKT_IDLE; + } + } + + if (i != NumBlksPerChunk) + { + // This has to be a failure from Ndis for send blocks!!! + // Undo a bunch of stuff + CTEAssert (BlockId == BLKID_NDISSEND); + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < i; j++, pBlkHdr = pBlkHdr->bh_Next) + { + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisBuffer); + + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisIpxSpxBuffer); + + if (pNdisIpxSpxBuffer) + { + NdisFreeBuffer(pNdisIpxSpxBuffer); + } + } + + SpxFreeMemory(pChunk); + pChunk = NULL; + } + else + { + // Successfully initialized the chunk, link it in + pChunk->bc_Next = *ppChunkHead; + *ppChunkHead = pChunk; + } + } + } + + if (pChunk != NULL) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + DBGPRINT(RESOURCES, INFO, + ("SpxBPAllocBlock: Allocating a block out of chunk %lx(%d) for Id %d\n", + pChunk, pChunk->bc_NumFrees, BlockId)); + + pChunk->bc_NumFrees --; + pChunk->bc_Age = 0; // Reset age + pBlk = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlk->bh_Next; + pBlk->bh_pChunk = pChunk; + + // Skip the block header! + pBlk++; + } + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + } + + return pBlk; +} + + + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Return a block to its owning chunk. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk; + PBLK_HDR pBlkHdr = (PBLK_HDR)((PCHAR)pBlock - sizeof(BLK_HDR)); + CTELockHandle lockHandle; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = spxBPHead[BlockId]; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pBlkHdr->bh_pChunk == pChunk) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPFreeBlock: Returning Block %lx to chunk %lx for Id %d\n", + pBlkHdr, pChunk, BlockId)); + + CTEAssert (pChunk->bc_NumFrees < spxNumBlks[BlockId]); + pChunk->bc_NumFrees ++; + pBlkHdr->bh_Next = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlkHdr; + break; + } + } + CTEAssert ((pChunk != NULL) && (pChunk->bc_FreeHead == pBlkHdr)); + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + return; +} + + + + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + Age out the block pool of unused blocks + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk, *ppChunk, pFree = NULL; + LONG i, j, NumBlksPerChunk; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + + if (TimerShuttingDown) + { + return TIMER_DONT_REQUEUE; + } + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + CTEGetLock(&spxBPLock[i], &lockHandle); + + for (ppChunk = &spxBPHead[i]; + (pChunk = *ppChunk) != NULL; ) + { + if ((pChunk->bc_NumFrees == NumBlksPerChunk) && + (++(pChunk->bc_Age) >= MAX_BLOCK_POOL_AGE)) + { + DBGPRINT(SYSTEM, INFO, + ("spxBPAgePool: freeing Chunk %lx, Id %d\n", + pChunk, pChunk->bc_BlkId)); + + *ppChunk = pChunk->bc_Next; +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPAge); +#endif + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + PBLK_HDR pBlkHdr; + + // We need to free Ndis stuff for these guys + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + } + } + + SpxFreeMemory(pChunk); + } + else + { + ppChunk = &pChunk->bc_Next; + } + } + CTEFreeLock(&spxBPLock[i], lockHandle); + } + + return TIMER_REQUEUE_CUR_VALUE; +} diff --git a/private/ntos/tdi/isn/spx/spxpkt.c b/private/ntos/tdi/isn/spx/spxpkt.c new file mode 100644 index 000000000..46b234020 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxpkt.c @@ -0,0 +1,1594 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.c + +Abstract: + + This module contains code that builds various spx packets. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXPKT + +VOID +SpxPktBuildCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + NOTE: If *ppPkt is NULL, we allocate a packet. If not, we just + recreate the data and don't update the packet's state. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + if (*ppPkt == NULL) { + + SpxAllocSendPacket(SpxDevice, &pCrPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + } else { + + pCrPkt = *ppPkt; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + NdisQueryPacket(pCrPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? (SPX_CC_SPX2 | SPX_CC_NEG) : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = 0xFFFF; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + // Initialize + + if (*ppPkt == NULL) { + + pSendResd = (PSPX_SEND_RESD)(pCrPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX_HDRSIZE; + + *ppPkt = pCrPkt; + } + + return; +} + + + + +VOID +SpxPktBuildCrAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrAckPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pCrAckPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrAckPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pCrAckPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | + (fSpx2 ? SPX_CC_SPX2 : 0) | + (fNeg ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnBuildCrAck: Spx2 packet size %d.%lx\n", + pSpxConnFile->scf_MaxPktSize)); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pCrAckPkt; + return; +} + + + +VOID +SpxPktBuildSn( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSn: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SN; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSnAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SNACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSs( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSs: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SS; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + } while (FALSE); + + return; +} + + + +VOID +SpxPktBuildSsAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SSACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRr( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + // For a renegotiate request, we use the sequence number of + // the first waiting data packet. Passed in. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + SeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_SeqNum = SeqNum; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRrAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + // For the RrAck, ack number will be the appropriate number + // for the last data packet received. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RenegAckAckNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + MaxPktSize); + + DBGPRINT(SEND, DBG3, + ("SpxPktBuildRrAck: SEQ %lx ACKNUM %lx ALLOCNUM %lx MAXPKT %lx\n", + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_RenegAckAckNum, + pSpxConnFile->scf_SentAllocNum, + MaxPktSize)); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDiscPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDiscPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDiscPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pDiscPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_ACK | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0) | + ((DataType == SPX2_DT_IDISC) ? 0 : SPX_CC_EOM)); + + pIpxSpxHdr->hdr_DataType = DataType; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDiscPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = + ((DataType == SPX2_DT_IDISC) ? SPX_TYPE_IDISC : SPX_TYPE_ORDREL); + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pRequest; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Offset = 0; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pDiscPkt; + } + + return; +} + + + + +VOID +SpxPktBuildProbe( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pProbe; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pProbe, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pProbe + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pProbe, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? SPX_CC_SPX2 : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_PROBE; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = NULL; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE + : MIN_IPXSPX_HDRSIZE); + + *ppPkt = pProbe; + } + + return; +} + + + + +VOID +SpxPktBuildData( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length + ) +/*++ + +Routine Description: + + Handles zero length sends. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDataPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDataPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Make a ndis buffer descriptor for the data if present. + if (Length > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_ReqPkt), + pSpxConnFile->scf_ReqPktOffset, + Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the send packet + SpxPktSendRelease(pDataPkt); + return; + } + + // Chain this in the packet + NdisChainBufferAtBack(pDataPkt, pNdisBuffer); + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDataPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + Length += hdrLen; + + NdisQueryPacket(pDataPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + Length, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (((State & SPX_SENDPKT_ACKREQ) ? SPX_CC_ACK : 0) | + ((State & SPX_SENDPKT_EOM) ? SPX_CC_EOM : 0) | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = pSpxConnFile->scf_DataType; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDataPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = SPX_TYPE_DATA; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pSpxConnFile->scf_ReqPkt; + pSendResd->sr_Offset = pSpxConnFile->scf_ReqPktOffset; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = Length; + pSendResd->sr_HdrLen = hdrLen; + + if (State & SPX_SENDPKT_ACKREQ) + { + KeQuerySystemTime((PLARGE_INTEGER)&pSendResd->sr_SentTime); + } + + CTEAssert(pSendResd->sr_Len <= pSpxConnFile->scf_MaxPktSize); + *ppPkt = pDataPkt; + + // Ok, allocation succeeded. Increment send seq. + pSpxConnFile->scf_SendSeqNum++; + } + + return; +} + + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ) +/*++ + +Routine Description: + + Creates a TargetBufferChain from the SourceBufferChain. The copy begins at + the 'Offset' location in the source chain. It copies 'Length' bytes. It also + handles Length = 0. If we run out of source chain before copying length amount + of bytes or run out of memory to create any more buffers for the target chain, + we clean up the partial chain created so far. + +Arguments: + + Status - Status of the request. + TargetChain - Pointer to the allocated buffer descriptor. + PoolHandle - Handle that is used to specify the pool. + SourceChain - Pointer to the descriptor of the source memory. + Offset - The Offset in the sources memory from which the copy is to + begin + Length - Number of Bytes to copy. + +Return Value: + + None. + +--*/ +{ + UINT BytesBeforeCurBuffer = 0; + PNDIS_BUFFER CurBuffer = SourceChain; + UINT BytesLeft; + UINT AvailableBytes; + PNDIS_BUFFER NewNdisBuffer, StartTargetChain; + + CTEAssert( SourceChain ); + + // First of all find the source buffer that contains data that starts at + // Offset. + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( BytesBeforeCurBuffer + AvailableBytes <= Offset ) { + BytesBeforeCurBuffer += AvailableBytes; + CurBuffer = CurBuffer->Next; + if ( CurBuffer ) { + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } else { + break; + } + } + + if ( ! CurBuffer ) { + *Status = STATUS_UNSUCCESSFUL; + return; + } + + // + // Copy the first buffer. This takes care of Length = 0. + // + BytesLeft = Length; + + // + // ( Offset - BytesBeforeCurBuffer ) gives us the offset within this buffer. + // + + AvailableBytes -= ( Offset - BytesBeforeCurBuffer ); + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &NewNdisBuffer, + PoolHandle, + CurBuffer, + Offset - BytesBeforeCurBuffer, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + return; + } + + StartTargetChain = NewNdisBuffer; + BytesLeft -= AvailableBytes; + + // + // Did the first buffer have enough data. If so, we r done. + // + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + // + // Now follow the Mdl chain and copy more buffers. + // + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( CurBuffer ) { + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + PoolHandle, + CurBuffer, + 0, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + return; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + BytesLeft -= AvailableBytes; + + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } + + // + // Ran out of source chain. This should not happen. + // + + CTEAssert( FALSE ); + + // For Retail build we clean up anyways. + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + *Status = STATUS_UNSUCCESSFUL; + return; +} + + +VOID +SpxPktBuildAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, ERR, + ("SpxPktBuildAck: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + // Send where data came from + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | (fSpx2 ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + if (fBuildNack) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + NumToResend); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + // Put current send seq number in packet for spx1 + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = (fBuildNack ? SPX_TYPE_DATANACK : SPX_TYPE_DATAACK); + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pPkt; + return; +} + + + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt + ) +{ + ((PSPX_RECV_RESD)(pPkt->ProtocolReserved))->rr_State = SPX_RECVPKT_IDLE; + SpxFreeRecvPacket(SpxDevice, pPkt); + return; +} + + + + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt + ) +{ + PNDIS_BUFFER pBuf, pIpxSpxBuf, pFreeBuf; + UINT bufCount; + + CTEAssert((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_IPXOWNS) == 0); + + NdisQueryPacket(pPkt, NULL, &bufCount, &pBuf, NULL); + + // BufCount == 1 for only the header. That's ok, we just reset the length + // and free the packet to the buffer pools. Else we need to free user buffers + // before that. + + NdisUnchainBufferAtFront( + pPkt, + &pBuf); + + NdisUnchainBufferAtFront( + pPkt, + &pIpxSpxBuf); + + // + // Set the header length to the max. that can be needed. + // + NdisAdjustBufferLength(pIpxSpxBuf, MIN_IPXSPX2_HDRSIZE); + + while (bufCount-- > 2) + { + PBYTE pData; + ULONG dataLen; + + NdisUnchainBufferAtBack( + pPkt, + &pFreeBuf); + + // See if we free data associated with the buffer + if ((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_FREEDATA) != 0) + { + NdisQueryBuffer(pFreeBuf, &pData, &dataLen); + CTEAssert(pData != NULL); + SpxFreeMemory(pData); + } + + CTEAssert(pFreeBuf != NULL); + NdisFreeBuffer(pFreeBuf); + } + + NdisReinitializePacket(pPkt); + + // Initialize elements of the protocol reserved structure. + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Id = IDENTIFIER_SPX; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State = SPX_SENDPKT_IDLE; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved1= NULL; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved2= NULL; + + NdisChainBufferAtFront( + pPkt, + pBuf); + + NdisChainBufferAtBack( + pPkt, + pIpxSpxBuf); + + SpxFreeSendPacket(SpxDevice, pPkt); + return; +} diff --git a/private/ntos/tdi/isn/spx/spxquery.c b/private/ntos/tdi/isn/spx/spxquery.c new file mode 100644 index 000000000..047ecabe8 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxquery.c @@ -0,0 +1,259 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Author: + + Adam Barr (adamba) Initial Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxQueryInitProviderInfo) +#endif + +// Define module number for event logging entries +#define FILENUM SPXQUERY + +// Useful macro to obtain the total length of an MDL chain. +#define SpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo + ) +{ + // Initialize to defaults first + RtlZeroMemory((PVOID)ProviderInfo, sizeof(TDI_PROVIDER_INFO)); + + ProviderInfo->Version = SPX_TDI_PROVIDERINFO_VERSION; + KeQuerySystemTime (&ProviderInfo->StartTime); + ProviderInfo->MinimumLookaheadData = SPX_PINFOMINMAXLOOKAHEAD; + ProviderInfo->MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + ProviderInfo->MaxSendSize = SPX_PINFOSENDSIZE; + ProviderInfo->ServiceFlags = SPX_PINFOSERVICEFLAGS; + return; +} + + + + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE AddressFile; + PSPX_CONN_FILE ConnectionFile; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS SpxAddress; + } AddressInfo; + + + + // what type of status do we want? + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) + { + case TDI_QUERY_CONNECTION_INFO: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADDRESS_INFO: + + // The caller wants the exact address value. + + ConnectionFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + status = SpxConnFileVerify(ConnectionFile); + + if (status == STATUS_SUCCESS) { + AddressFile = ConnectionFile->scf_AddrFile; + SpxConnFileDereference(ConnectionFile, CFREF_VERIFY); + } else { + AddressFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + } + + status = SpxAddrFileVerify(AddressFile); + + if (status == STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, INFO, + ("SpxTdiQuery: Net.Socket %lx.%lx\n", + *(PULONG)Device->dev_Network, + AddressFile->saf_Addr->sa_Socket)); + + AddressInfo.ActivityCount = 0; + (VOID)SpxBuildTdiAddress( + &AddressInfo.SpxAddress, + sizeof(TA_IPX_ADDRESS), + Device->dev_Network, + Device->dev_Node, + AddressFile->saf_Addr->sa_Socket); + + status = TdiCopyBufferToMdl( + &AddressInfo, + 0, + sizeof(AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + SpxAddrFileDereference(AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: { + BYTE socketType; + TDI_PROVIDER_INFO providerInfo = Device->dev_ProviderInfo; + + // + // The device name extension comes down in the Irp + // + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) { + DBGPRINT(RECEIVE, ERR, ("TDI_QUERY_PROVIDER_INFO: SpxUtilGetSocketType failed: %lx\n", status)); + return(status); + } + + // + // The Catapult folks had a problem where AFD was discarding buffered sends on the NT box when it got a + // local disconnect on SPX1. This was because the Orderly release flag was always set in the provider + // info. AFD queries this once per device type. We detect the device above and OR in the orderly release + // flag if this query came down on an SPX2 endpoint. + // This is to make sure that AFD follows the correct disconnect semantics for SPX1 and SPX2 (SPX1 does + // only abortive; SPX2 does both abortive and orderly). + // + // BUGBUG: this will still not solve the problem completely since a connection that starts off as an SPX2 + // one can still be negotiated to SPX1 if the remote supports only SPX1. + // + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) { + + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX2 socket\n")); + providerInfo.ServiceFlags |= TDI_SERVICE_ORDERLY_RELEASE; + } else { + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX1 socket\n")); + } + + status = TdiCopyBufferToMdl ( + &providerInfo, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + } + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->dev_Stat, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} // SpxTdiQueryInformation + + + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} // SpxTdiSetInformation + diff --git a/private/ntos/tdi/isn/spx/spxrecv.c b/private/ntos/tdi/isn/spx/spxrecv.c new file mode 100644 index 000000000..9dc8b6fe3 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxrecv.c @@ -0,0 +1,2839 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXRECV + +BOOLEAN +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN ULONG FwdAdapterCtx, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN PMDL pMdl + ) + +{ + PIPXSPX_HDR pHdr; + + // We have a separate routine to process SYS packets. DATA packets are + // processed within this routine. + if (LookaheadBufferSize < MIN_IPXSPX_HDRSIZE) + { + DBGPRINT(RECEIVE, ERR, + ("SpxReceive: Invalid length %lx\n", LookaheadBufferSize)); + + return FALSE; + } + + ++SpxDevice->dev_Stat.PacketsReceived; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + if ((pHdr->hdr_ConnCtrl & SPX_CC_SYS) == 0) + { + // Check for data packets + if ((pHdr->hdr_DataType != SPX2_DT_ORDREL) && + (pHdr->hdr_DataType != SPX2_DT_IDISC) && + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK)) + { + // HANDLE DATA PACKET + SpxRecvDataPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + else + { + // The whole packet better be in the lookahead, else we ignore. + if (LookaheadBufferSize == PacketSize) + { + SpxRecvDiscPacket( + LookaheadBuffer, + RemoteAddress, + LookaheadBufferSize); + } + } + } + else + { + SpxRecvSysPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + + return FALSE; +} + + + + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + CTELockHandle lockHandle; + NTSTATUS status; + BOOLEAN fAck, fEom, fBuffered, fImmedAck, fLockHeld; + PNDIS_BUFFER pNdisBuffer; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: For %lx with status %lx\n", pNdisPkt, NdisStatus)); + + pRecvResd = RECV_RESD(pNdisPkt); + pSpxConnFile = pRecvResd->rr_ConnFile; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + + fEom = ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + fImmedAck = ((pRecvResd->rr_State & SPX_RECVPKT_IMMEDACK) != 0); + fBuffered = ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + fAck = ((pRecvResd->rr_State & SPX_RECVPKT_SENDACK) != 0); + + // Check if receive is done. If we remove the reference for this + // packet and it goes to zero, that means the receive was aborted. + // Move to the completion queue. + // If receive is filled up, then remove the creation reference + // i.e. just complete the receive at this point. + // There can be only one packet per receive, we dont support + // out of order reception. + + if (!fBuffered) + { + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert((pNdisBuffer != NULL) || (BytesTransferred == 0)); + + // BUG #11772 + // On MP-machines scf_CurRecvReq could be set to NULL. Get the req + // from the recv packet. + // pRequest = pSpxConnFile->scf_CurRecvReq; + // CTEAssert(pRequest == pRecvResd->rr_Request); + pRequest = pRecvResd->rr_Request; + + // Remove reference for this packet. + --(REQUEST_INFORMATION(pRequest)); + + if (NdisStatus == NDIS_STATUS_SUCCESS) + { + pSpxConnFile->scf_CurRecvOffset += BytesTransferred; + pSpxConnFile->scf_CurRecvSize -= BytesTransferred; + +#if DBG + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (BytesTransferred != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= BytesTransferred; + } + } +#endif + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom || + ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL))) + { + CTELockHandle lockHandleInter; + + // We are done with this receive. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + + status = STATUS_SUCCESS; + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + status = STATUS_RECEIVE_PARTIAL; + } + + if ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL)) + { + status = REQUEST_STATUS(pRequest); + } + + REQUEST_STATUS(pRequest) = status; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + } + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + else + { + // Buffered receive, queue it in if successful. + // BUG #18363 + // IF WE DISCONNECTED in the meantime, we need to just dump this + // packet. + if (SPX_CONN_ACTIVE(pSpxConnFile) && + (NdisStatus == NDIS_STATUS_SUCCESS)) + { + // Queue packet in connection. Reference connection for this. + SpxConnQueueRecvPktTail(pSpxConnFile, pNdisPkt); + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: Buffering: %lx Pkt %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, BytesTransferred, pRecvResd->rr_State)); + + // There could either be queued receives. (This could happen in + // a partial receive case. Or if a receive got queued in while we + // were processing this packet (Possible on MP)), or a packet was + // buffered while we were completing some receives + + CTEAssert(pSpxConnFile->scf_RecvListHead); + + if ((pSpxConnFile->scf_CurRecvReq != NULL) || + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)) + { + CTELockHandle interLockHandle; + + // Push this connection into a ProcessRecv queue which will be + // dealt with in receive completion. + + DBGPRINT(RECEIVE, DBG, + ("spxRecvTransferData: Queueing for recvp %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // Get the global q lock, push into recv list. + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + } + else + { + PBYTE pData; + ULONG dataLen; + + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + // Free the data, ndis buffer. + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + SpxFreeMemory(pData); + } + + // Dont send ack, set status to be failure so we free packet/buffer. + fAck = FALSE; + NdisStatus = NDIS_STATUS_FAILURE; + } + } + + END_PROCESS_PACKET( + pSpxConnFile, fBuffered, (NdisStatus == NDIS_STATUS_SUCCESS)); + + if (fAck) + { + // Rem ack addr should have been copied in receive. + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (!fBuffered || (NdisStatus != STATUS_SUCCESS)) + { + // Free the ndis packet/buffer + SpxPktRecvRelease(pNdisPkt); + } + + return; +} + + + + +VOID +SpxReceiveComplete( + IN USHORT NicId + ) + +{ + CTELockHandle lockHandleInter, lockHandle; + PREQUEST pRequest; + BOOLEAN fConnLockHeld, fInterlockHeld; + PSPX_CONN_FILE pSpxConnFile; + int numDerefs = 0; + + // See if any connections need recv processing. This will also take + // care of any acks opening up window so our sends go to the max. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + fInterlockHeld = TRUE; + + while ((pSpxConnFile = SpxRecvConnList.pcl_Head) != NULL) + { + // Reset for each connection + numDerefs = 0; + + if ((SpxRecvConnList.pcl_Head = pSpxConnFile->scf_ProcessRecvNext) == NULL) + SpxRecvConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_ProcessRecvNext = NULL; + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromRecv: %lx\n", pSpxConnFile)); + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + do + { + // Complete pending requests. + while (!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) + { + pRequest = + LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqDoneLinkage.Flink); + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert (REQUEST_MINOR_FUNCTION(pRequest) != TDI_RECEIVE); + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + + // Call process pkts if we have any packets or if any receives to + // complete. Note this will call even when there are no receives + // queued and the first packet has already been indicated. + if ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage) || + (pSpxConnFile->scf_RecvListHead != NULL))) + { + // We have the flag reference on the connection. + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + +#if DBG + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: RecvDone left %lx\n", + pSpxConnFile)); + } +#endif + + // Hmm. This check is rather expensive, and essentially we are doing + // it twice. Should look to see if this can be modified safely. + } while ((!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + ((!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + (SPX_RECVPKT_BUFFERING | SPX_RECVPKT_INDICATED)) == + SPX_RECVPKT_BUFFERING))))); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RECVQ); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_RECV, + CFREF_VERIFY); + + numDerefs++; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + + // First see if we need to packetize. + while ((pSpxConnFile = SpxPktConnList.pcl_Head) != NULL) + { + if ((SpxPktConnList.pcl_Head = pSpxConnFile->scf_PktNext) == NULL) + SpxPktConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_PktNext = NULL; + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fConnLockHeld = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("SpxReceiveComplete: Packetizing %lx\n", pSpxConnFile)); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + if (SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle)) + { + // Done. + fConnLockHeld = FALSE; + } + } + + if (fConnLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_PKTIZE); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + if (fInterlockHeld) + { + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + return; +} + + + + +// +// PACKET HANDLING ROUTINES +// + + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming system packet. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld = FALSE; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvSysPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + if ((pktLen == SPX_CR_PKTLEN) && + (destConnId == 0xFFFF) && + (pHdr->hdr_ConnCtrl & SPX_CC_CR)) + { + spxConnHandleConnReq( + pHdr, + pRemoteAddr); + + return; + } + + // + // [SA] Bug #14917 + // Some SPX SYS packets (no extended ack field) may come in with the SPX2 bit set. + // Make sure we don't discard these packets. + // + + // if ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && (pktLen < MIN_IPXSPX2_HDRSIZE)) + // { + // return; + // } + + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnSysPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Id %lx NOT FOUND\n", destConnId)); + return; + } + + do + { + + DBGPRINT(RECEIVE, INFO, + ("SpxConnSysPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // This could be one of many packets. Connection ack/Session negotiate/ + // Session setup, Data Ack, Probe/Ack, Renegotiate/Ack. We shunt + // off all the packets to different routines but process the data + // ack packets here. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAddr); + + lockHeld = TRUE; + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromSrv( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_LISTENING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromClient( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // NOTE: Our ack to a session setup might get dropped. + // But the SS Ack is similar to a normal SPX2 ack. + // We dont have to do anything special. + + // Received ack/nack/reneg/reneg ack/disc associated packet. + // Disc packets except ordrel ack have non-zero datastream type. + if ((pHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) + { + // We received a renegotiate packet. Ignore all ack values + // in a reneg req. + SpxConnProcessRenegReq(pSpxConnFile, pHdr, pRemoteAddr, lockHandle); + lockHeld = FALSE; + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + if (((pHdr->hdr_ConnCtrl & SPX_CC_ACK) == 0) && + (pHdr->hdr_DataType == 0)) + { + SpxConnProcessAck(pSpxConnFile, pHdr, lockHandle); + lockHeld = FALSE; + } + else + { + // Just process the numbers we got. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + lockHeld = FALSE; + } + + // If the remote wants us to send an ack, do it. + if (pHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // First copy the remote address in connection. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (!lockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + } + + SpxConnSendAck(pSpxConnFile, lockHandle); + lockHeld = FALSE; + break; + } + + break; + + default: + + // Ignore this packet. + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Ignoring packet, state is not active\n")); + break; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (LookaheadSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvDiscPacket: Packet Size %lx\n", + pktLen)); + + return; + } + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDiscPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDiscPacket: Id %lx NOT FOUND", destConnId)); + + return; + } + + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: Id %lx Conn %lx DiscType %lx\n", + destConnId, pSpxConnFile, pHdr->hdr_DataType)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + + // Unless we are in the active/disconnecting, but send state = idle + // and recv state = idle/recv posted, we ignore all disconnect packets. + if (((SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_ACTIVE) && + (SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_DISCONN)) || + ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_IDLE) && + (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_POSTED)) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx, %lx, %lx.%lx, %d.%d\n", + pSpxConnFile, + SPX_MAIN_STATE(pSpxConnFile), + SPX_SEND_STATE(pSpxConnFile), SPX_RECV_STATE(pSpxConnFile), + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)), + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)))); + + break; + } + + // If we have received a disconnect, process received ack to complete any + // pending sends before we allow the disconnect. This ack number will be + // the last word on this session. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + switch (pHdr->hdr_DataType) + { + case SPX2_DT_ORDREL: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: Recd ORDREl!\n")); + + // BUGBUG: Need to deal with all sthe states. + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // On receive, we do check the seq num for the orderly release, just + // like for a data packet. + // If this was not already indicated, indicate it now. That is all + // we do for an orderly release. When our client does a orderly rel + // and we receive the ack for that, call abortive with success. + + // Verify ord rel packet, this checks if seq nums match also. + if ((pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) || + (pHdr->hdr_DataType != SPX2_DT_ORDREL) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: OR Failed/Ignored %lx, %lx.%lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + lockHeld = FALSE; + } + + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessOrdRel(pSpxConnFile, lockHandle); + lockHeld = FALSE; + } + else + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + // Set flag in last recd buffer + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_ORD_DISC; + } + + break; + + case SPX2_DT_IDISC: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC %lx!\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: SEND %d. RECV %d.%lx!\n", + IsListEmpty(&pSpxConnFile->scf_ReqLinkage), + IsListEmpty(&pSpxConnFile->scf_RecvLinkage), + pSpxConnFile->scf_RecvDoneLinkage)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + !(pHdr->hdr_ConnCtrl & SPX_CC_ACK) || + (pHdr->hdr_DataType != SPX2_DT_IDISC) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:IDISC Ignored %lx.%lx.%lx.%lx\n", + pSpxConnFile, seqNum, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO RECV DATA IDISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessIDisc(pSpxConnFile, lockHandle); + + lockHeld = FALSE; + } + else + { + // Set flag in last recd buffer + + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_IDISC; + } + + break; + + case SPX2_DT_IDISC_ACK: + + // Done with informed disconnect. Call abort connection with + // status success. That completes the pending disconnect request + // with status_success. + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC ack!\n", pSpxConnFile)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:Ver idisc ack Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + break; + } + + // We should be in the right state to accept this. + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_IDISC)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + break; + + default: + + KeBugCheck(0); + } + + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvBufferPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT LookaheadOffset, + IN PIPXSPX_HDR pIpxSpxHdr, + IN UINT PacketSize, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PSPX_RECV_RESD pRecvResd; + ULONG bytesCopied; + BOOLEAN fEom; + NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; + PBYTE pData = NULL; + PNDIS_BUFFER pNdisBuffer = NULL; + + if (PacketSize > 0) + { + // Allocate memory for this data. + if (pData = (PBYTE)SpxAllocateMemory(PacketSize)) + { + // Describe memory with a ndis buffer descriptor. + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + PacketSize); + } + else + { + ndisStatus = NDIS_STATUS_RESOURCES; + } + } + + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Queue the buffer into the packet if there is one. + if (pNdisBuffer) + { + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_DataOffset= 0; + +#if DBG + // Store seq number + GETSHORT2SHORT(&pRecvResd->rr_SeqNum , &pIpxSpxHdr->hdr_SeqNum); +#endif + + pRecvResd->rr_State = + (SPX_RECVPKT_BUFFERING | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fEom ? SPX_RECVPKT_EOM : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + } + + pRecvResd->rr_Request = NULL; + pRecvResd->rr_ConnFile = pSpxConnFile; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvBufferPkt: %lx Len %lx DataPts %lx F %lx\n", + pSpxConnFile, PacketSize, pData, pRecvResd->rr_State)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Call ndis transfer data. Copy ENTIRE packet. copySize has + // been modified so use original values. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (PacketSize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadOffset, + PacketSize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + + // BUG: FDDI returns pending which screws us up here. Stupid bug + ndisStatus = NDIS_STATUS_SUCCESS; + } + } + + // ASSERT: Lock will be freed in the success case. + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvBufferPkt: FAILED!\n")); + + END_PROCESS_PACKET(pSpxConnFile, FALSE, FALSE); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + if (pData != NULL) + { + SpxFreeMemory(pData); + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + + return; +} + + + + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadSize, + IN UINT LookaheadOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + ULONG receiveFlags; + PSPX_CONN_FILE pSpxConnFile; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PIRP pRecvIrp; + ULONG bytesTaken, iOffset, copySize, bytesCopied; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + NDIS_STATUS ndisStatus; + PREQUEST pRequest = NULL; + BOOLEAN fEom, + fImmedAck = FALSE, fLockHeld = FALSE, fPktDone = FALSE; + + pIpxSpxHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pIpxSpxHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + // We keep and use the remote id in the net format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: packet received dest %lx src %lx seq %lx\n", + pIpxSpxHdr->hdr_DestSkt, pIpxSpxHdr->hdr_SrcSkt, seqNum)); + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen < MIN_IPXSPX2_HDRSIZE)) + { + return; + } + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: Id %lx NOT FOUND", destConnId)); + return; + } + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + +#if 0 + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); +#endif + + fLockHeld = TRUE; + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + // Verify data packet, this checks if seq nums match also. + if ((pIpxSpxHdr->hdr_SrcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + !((pktLen >= MIN_IPXSPX_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen >= MIN_IPXSPX2_HDRSIZE)))) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + ++SpxDevice->dev_Stat.DataFramesRejected; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesRejected, + pktLen - (SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + + // + // Bug #16975: Set the remote ack addr for use in SpxConnSendAck() + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + + break; + } + + // If we have received an orderly release, we accept no more data + // packets. + if (SPX_CONN_FLAG( + pSpxConnFile, + (SPX_CONNFILE_IND_IDISC | + SPX_CONNFILE_IND_ODISC)) + + || + + ((pSpxConnFile->scf_RecvListTail != NULL) && + ((pSpxConnFile->scf_RecvListTail->rr_State & + SPX_RECVPKT_DISCMASK) != 0))) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDataPacket: After ord rel %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // We are processing a packet OR a receive is about to complete. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)) + { + BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum); + } + else + { + // Already processing a packet. Or a receive is waiting to + // complete. Get out. + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + iOffset = MIN_IPXSPX2_HDRSIZE; + if (!SPX2_CONN(pSpxConnFile)) + { + iOffset = 0; + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)) + { + iOffset = MIN_IPXSPX_HDRSIZE; + } + } + + copySize = pktLen - iOffset; + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + // Do we attempt to piggyback? If not, fImmedAck is true. + // For SPX1 we dont piggyback. + // Bug #18253 + fImmedAck = (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM) == 0)); + + // If we do not have EOM to indicate AND we are a zero-sized packet + // then just consume this packet. + if (!fEom && (copySize == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: ZERO LENGTH PACKET NO EOM %lx.%lx\n", + pSpxConnFile, seqNum)); + + fPktDone = TRUE; + break; + } + + receiveFlags = TDI_RECEIVE_NORMAL; + receiveFlags |= ((fEom ? TDI_RECEIVE_ENTIRE_MESSAGE : 0) | + (((MacOptions & + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0)); + + ++SpxDevice->dev_Stat.DataFramesReceived; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesReceived, + copySize); + + // Ok, we accept this packet. Depending on our state. + switch (SPX_RECV_STATE(pSpxConnFile)) + { + case SPX_RECV_PROCESS_PKTS: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: recv completions on %lx\n", + pSpxConnFile)); + + goto BufferPacket; + + case SPX_RECV_IDLE: + + // If recv q is non-empty we are buffering data. + // Also, if no receive handler goto buffer data. Also, if receives + // are being completed, buffer this packet. + if ((pSpxConnFile->scf_RecvListHead != NULL) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + !(pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: RecvListHead non-null %lx\n", + pSpxConnFile)); + + goto BufferPacket; + } + + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + + // Don't indicate this packet again. + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); + +#if DBG + CTEAssert(pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + + // + // Comment this out for Buf # 10394. we'r hitting this assert + // even when there was no data loss. + // + // CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != seqNum); + + pSpxConnFile->scf_PktSeqNum = seqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; + + pSpxConnFile->scf_IndBytes = copySize; + pSpxConnFile->scf_IndLine = __LINE__; + + +#endif + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + receiveFlags, + LookaheadSize - iOffset, + copySize, + &bytesTaken, + LookaheadBuffer + iOffset, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: IND Flags %lx.%lx ConnID %lx,\ + %lx Ctx %lx SEQ %lx Size %lx . %lx .%lx IND Status %lx\n", + pIpxSpxHdr->hdr_ConnCtrl, + receiveFlags, + destConnId, + pSpxConnFile, + pSpxConnFile->scf_ConnCtx, + seqNum, + LookaheadSize - iOffset, + copySize, + bytesTaken, + status)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: %x %x %x %x %x %x %x %x %x %x %x %x\n", + *(LookaheadBuffer+iOffset), + *(LookaheadBuffer+iOffset+1), + *(LookaheadBuffer+iOffset+2), + *(LookaheadBuffer+iOffset+3), + *(LookaheadBuffer+iOffset+4), + *(LookaheadBuffer+iOffset+5), + *(LookaheadBuffer+iOffset+6), + *(LookaheadBuffer+iOffset+7), + *(LookaheadBuffer+iOffset+8), + *(LookaheadBuffer+iOffset+9), + *(LookaheadBuffer+iOffset+10), + *(LookaheadBuffer+iOffset+11))); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. + CTEAssert((bytesTaken != 0) || fEom); + fPktDone = TRUE; + +#if DBG + // Set this to 0, since we just indicated, there could + // not have been other data. + pSpxConnFile->scf_IndBytes = 0; +#endif + + break; + } + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + break; + } + + // If there was indicated but not received data waiting + // (which in this path there will never be, the request + // could be completed given the data filled it up, and + // the lock released. + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + } + else if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + // Data was not accepted. Need to buffer data and + // reduce window. + goto BufferPacket; + } + + // Fall through to recv_posted. + } + else + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: !!!Ignoring %lx Seq %lx\n", + pSpxConnFile, + seqNum)); + + break; + } + + case SPX_RECV_POSTED: + + if (pSpxConnFile->scf_RecvListHead != NULL) + { + // This can happen also. Buffer packet if it does. + goto BufferPacket; + } + + // If a receive irp is posted, then process the receive irp. If + // we fell thru we MAY already will have an irp. + if (pRequest == NULL) + { + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_CurRecvReq != NULL); + pRequest = pSpxConnFile->scf_CurRecvReq; + } + + // Process receive. Here we do not need to worry about + // indicated yet not received data. We just deal with + // servicing the current packet. + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + if ((LookaheadSize == PacketSize) && + (pSpxConnFile->scf_CurRecvSize >= copySize)) + { + bytesCopied = 0; + status = STATUS_SUCCESS; + if (copySize > 0) + { + status = TdiCopyBufferToMdl( + LookaheadBuffer, + iOffset, + copySize, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + } + + DBGPRINT(RECEIVE, DBG, + ("BytesCopied %lx CopySize %lx, Recv Size %lx.%lx\n", + bytesCopied, copySize, + pSpxConnFile->scf_CurRecvSize, + pSpxConnFile->scf_CurRecvOffset)); + } + + // Update current request values and see if this request + // is to be completed. Either zero or fEom. + pSpxConnFile->scf_CurRecvOffset += bytesCopied; + pSpxConnFile->scf_CurRecvSize -= bytesCopied; + +#if DBG + // Decrement indicated data count + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + if (bytesCopied != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= bytesCopied; + } + } +#endif + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + CTELockHandle lockHandleInter; + + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest)= + pSpxConnFile->scf_CurRecvOffset; + + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnData: Completing recv %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + fPktDone = TRUE; + } + else + { + // Need to allocate a ndis receive packet for transfer + // data. + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: %lx.%lx Tranfer data needed!\n", + copySize, pSpxConnFile->scf_CurRecvSize)); + + if (copySize > pSpxConnFile->scf_CurRecvSize) + { + // Partial receive. Buffer and then deal with it. + goto BufferPacket; + } + + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Describe the receive irp's data with a ndis buffer + // descriptor. + if (copySize > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + copySize); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the recv packet + SpxPktRecvRelease(pNdisPkt); + break; + } + + // Queue the buffer into the packet + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + // Don't care about whether this is indicated or not here + // as it is not a buffering packet. + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = + ((fEom ? SPX_RECVPKT_EOM : 0) | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fImmedAck ? SPX_RECVPKT_IMMEDACK : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? + SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + } + + pRecvResd->rr_Request = pRequest; + pRecvResd->rr_ConnFile = pSpxConnFile; + + // reference receive request + REQUEST_INFORMATION(pRequest)++; + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + fLockHeld = FALSE; + + // Call ndis transfer data. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (copySize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + copySize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + } + + break; + + default: + + KeBugCheck(0); + break; + } + + break; + +BufferPacket: + + SpxRecvBufferPkt( + pSpxConnFile, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + pIpxSpxHdr, + copySize, + RemoteAddress, + lockHandle); + + fLockHeld = FALSE; + } + + } while (FALSE); + + // Here we process a received ack. + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // Send an ack if one was asked for. And we are done with this packet. + if (fPktDone) + { + END_PROCESS_PACKET(pSpxConnFile, FALSE, TRUE); + } + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) && fPktDone) + { + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // First copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + PBYTE pData; + ULONG dataLen, copyLen; + BOOLEAN fLockHeld = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numPkts = 0, numDerefs = 0; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Flush %lx\n", + pSpxConnFile, BytesToFlush)); + + while (((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + ((BytesToFlush > 0) || + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0))) + { + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + if ((BytesToFlush == 0) && (dataLen != 0)) + { + // Don't flush this packet. + break; + } + + // Allow for zero data, eom only packets. + copyLen = MIN((dataLen - pRecvResd->rr_DataOffset), BytesToFlush); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Pkt %lx DataLen %lx Copy %lx Flush %lx\n", + pSpxConnFile, pNdisPkt, dataLen, copyLen, BytesToFlush)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + BytesToFlush -= (ULONG)copyLen; + +#if DBG + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } +#endif + + if (pRecvResd->rr_DataOffset == dataLen) + { + // Packet consumed. Free it up. Check if disc happened. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + numDerefs++; + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: !!!ALL INDICATED on %lx.%lx.%lx.%lx\n", + pSpxConnFile, pNdisPkt, pNdisBuffer, pData)); + + INCREMENT_WINDOW(pSpxConnFile); + fWdwOpen = TRUE; + } + else + { + // Took only part of this packet. Get out. + break; + } + } + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxRecvFlushBytes: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN - Receive was queued => TRUE + +--*/ +{ + ULONG indicateFlags; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PREQUEST pRequest; + PIRP pRecvIrp; + ULONG bytesTaken, totalSize, bufSize; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PSPX_RECV_RESD pRecvResd; + NTSTATUS status; + PBYTE lookaheadData; + ULONG lookaheadSize; + BOOLEAN fLockHeld = TRUE, fRecvQueued = FALSE; + + + while ((pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) && + ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) == 0)) + { + // Once a receive is queued we better get out. + CTEAssert(!fRecvQueued); + + // Initialize lookahead values + lookaheadData = NULL; + lookaheadSize = 0; + + // We have no indicated but pending data, and there is some data to + // indicate. Figure out how much. Indicate upto end of message or as + // much as we have. + + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &lookaheadData, &lookaheadSize); + CTEAssert(lookaheadData != NULL); + CTEAssert(lookaheadSize >= 0); + } + + // Allow for zero data, eom only packets. + lookaheadSize -= pRecvResd->rr_DataOffset; + totalSize = lookaheadSize; + lookaheadData += pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((lookaheadSize > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + +#if DBG + CTEAssert (pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + // Go ahead and walk the list of waiting packets. Get total size. + while ((pRecvResd->rr_Next != NULL) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) == 0)) + { + // Check next packet. + pRecvResd = pRecvResd->rr_Next; + +#if DBG + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, NULL, &bufSize); + CTEAssert(bufSize >= 0); + + // Allow for zero data, eom only packets. + totalSize += bufSize; + } + +#if DBG + pSpxConnFile->scf_IndBytes = totalSize; + pSpxConnFile->scf_IndLine = __LINE__; + + // There better not be any pending receives. If so, we have data + // corruption about to happen. + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + indicateFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_COPY_LOOKAHEAD; + if ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0) + { + indicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + indicateFlags, + lookaheadSize, + totalSize, + &bytesTaken, + lookaheadData, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnIndicatePendingData: IND Flags %lx Size %lx .%lx IND Status %lx\n", + indicateFlags, + totalSize, + bytesTaken, + status)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. Free bytesTaken worth of data packets. + // Sometimes AFD returns STATUS_SUCCESS to just flush the data, so + // we can't assume it took only one packet (since lookahead only + // had that information). + CTEAssert(bytesTaken == totalSize); + SpxRecvFlushBytes(pSpxConnFile, totalSize, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + continue; + } + else if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + return (FALSE); + } + + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + fRecvQueued = TRUE; + } + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return fRecvQueued; +} + + + + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Handle buffered data, complete irp if necessary. Set state to idle + if list becomes empty. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN: More data left to indicate => TRUE + +--*/ +{ + ULONG remainingDataLen, copyLen, bytesCopied; + PREQUEST pRequest; + NTSTATUS status; + BOOLEAN fEom; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + ULONG dataLen; + PBYTE pData; + LIST_ENTRY *p; + BOOLEAN fLockHeld = TRUE, fMoreData = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numDerefs = 0; + + if (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_PROCESS_PKTS); + +ProcessReceives: + + while ((pSpxConnFile->scf_CurRecvReq != NULL) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL)) + { + // A buffering recv packet will have one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + // Allow for zero data, eom only packets. + remainingDataLen = dataLen - pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((remainingDataLen > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + + status = STATUS_SUCCESS; + copyLen = 0; + if (remainingDataLen > 0) + { + copyLen = MIN(remainingDataLen, pSpxConnFile->scf_CurRecvSize); + status = TdiCopyBufferToMdl( + pData, + pRecvResd->rr_DataOffset, + copyLen, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_CurRecvReq), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + copyLen = pSpxConnFile->scf_CurRecvSize; + } + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, pData, copyLen, pRecvResd->rr_State)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + pSpxConnFile->scf_CurRecvSize -= (USHORT)copyLen; + pSpxConnFile->scf_CurRecvOffset += (USHORT)copyLen; + +#if DBG + // If this packet was part of indicated data count, decrement. + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } + } +#endif + + // Set fEom/discState (init to 0) only if all of packet was consumed. + fEom = FALSE; + if (pRecvResd->rr_DataOffset == dataLen) + { + fEom = (BOOLEAN)((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + + // Remember if disconnect needed to happen. If set, this better be + // last packet received. Again, only if entire pkt was consumed. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + // Packet consumed. Free it up. + numDerefs++; + + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + INCREMENT_WINDOW(pSpxConnFile); + + fWdwOpen = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx DEQUEUED\n", + pSpxConnFile, pNdisPkt, pData)); + + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + } + else + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx PARTIAL USE %lx.%lx\n", + pSpxConnFile, pNdisPkt, pRecvResd->rr_DataOffset, dataLen)); + } + + // Don't complete until we are out of all packets and stream mode or... + if (((pSpxConnFile->scf_RecvListHead == NULL) && + SPX_CONN_STREAM(pSpxConnFile)) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + // Done with receive, move to completion or complete depending on + // call level. + pRequest = pSpxConnFile->scf_CurRecvReq; + + // Set status. Complete with error from TdiCopy if so. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + REQUEST_STATUS(pRequest) = status; + + // Ensure we dont overwrite an error status. + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom && + NT_SUCCESS(status)) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + // Dequeue this request, set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Recv %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + // Request is done. Move to receive completion list. There + // could already be previously queued requests in here. + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + } + + CTEAssert((discState == 0) || + (pSpxConnFile->scf_RecvListHead == NULL)); + } + + // Complete any completed receives + while ((p = pSpxConnFile->scf_RecvDoneLinkage.Flink) != + &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + + while (fMoreData) + { + // Bug #21036 + // If there is a receive waiting to be processed, we better not + // indicate data before we finish it. + if (pSpxConnFile->scf_CurRecvReq != NULL) + goto ProcessReceives; + + // If a receive was queued the goto beginning again. + if (SpxRecvIndicatePendingData(pSpxConnFile, LockHandleConn)) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + goto ProcessReceives; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + } + + // Set state + SPX_RECV_SETSTATE( + pSpxConnFile, + (pSpxConnFile->scf_CurRecvReq == NULL) ? + SPX_RECV_IDLE : SPX_RECV_POSTED); + } +#if DBG + else + { + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Already processing pkts %lx\n", + pSpxConnFile)); + } +#endif + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered IDISC %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isn/spx/spxreg.c b/private/ntos/tdi/isn/spx/spxreg.c new file mode 100644 index 000000000..4389dca5f --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxreg.c @@ -0,0 +1,400 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXREG + +// Local functions used to access the registry. +NTSTATUS +SpxInitReadIpxDeviceName( + VOID); + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitGetConfiguration) +#pragma alloc_text(INIT, SpxInitFreeConfiguration) +#pragma alloc_text(INIT, SpxInitGetConfigValue) +#pragma alloc_text(INIT, SpxInitReadIpxDeviceName) +#pragma alloc_text(INIT, SpxInitSetIpxDeviceName) +#endif + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + RegistryPath - The name of ST's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + NTSTATUS Status; + UINT i; + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + + ULONG Zero = 0; + ULONG Two = 2; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Twelve = 12; + ULONG Fifteen = 15; + ULONG Thirty = 30; + ULONG FiveHundred = 500; + ULONG Hex4000 = 0x4000; + ULONG Hex7FFF = 0x7FFF; + ULONG FourK = 4096; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"ConnectionCount", &Five }, + { L"ConnectionTimeout", &Two }, + { L"InitPackets", &Five }, + { L"MaxPackets", &Thirty}, + { L"InitialRetransmissionTime", &FiveHundred}, + { L"KeepAliveCount", &Eight}, + { L"KeepAliveTimeout", &Twelve}, + { L"WindowSize", &Four}, + { L"SpxSocketRangeStart", &Hex4000}, + { L"SpxSocketRangeEnd", &Hex7FFF}, + { L"SpxSocketUniqueness", &Eight}, + { L"MaxPacketSize", &FourK}, + { L"RetransmissionCount", &Eight}, + { L"DisableSpx2", &Zero}, + { L"RouterMtu", &Zero}, + { L"BackCompSpx", &Zero} + }; + + if (!NT_SUCCESS(SpxInitReadIpxDeviceName())) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Allocate memory for the main config structure. + Config = CTEAllocMem (sizeof(CONFIG)); + if (Config == NULL) { + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->cf_DeviceName.Buffer = NULL; + + // SpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + RegistryPathBuffer = (PWSTR)CTEAllocMem(RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory ( + RegistryPathBuffer, + RegistryPath->Buffer, + RegistryPath->Length); + + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->cf_RegistryPathBuffer = RegistryPathBuffer; + + // Read the per-transport (as opposed to per-binding) + // parameters. + // + // Set up QueryTable to do the following: + // 1) Switch to the Parameters key below SPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // 2-14) Call SpxSetBindingValue for each of the keys we + // care about. + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = SpxInitGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + } + + // 15) Stop + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->cf_RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return Status; + } + + CTEFreeMem (RegistryPathBuffer); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} // SpxInitGetConfiguration + + + + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get free any storage that was allocated + by SpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + CTEFreeMem (Config); + +} // SpxInitFreeConfig + + + + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + DBGPRINT(CONFIG, INFO, + ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + + Config->cf_Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + return STATUS_SUCCESS; + +} // SpxInitGetConfigValue + + + + +NTSTATUS +SpxInitReadIpxDeviceName( + VOID + ) + +{ + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = IPX_REG_PATH; + + // Set up QueryTable to do the following: + // + // 1) Call SetIpxDeviceName for the string in "Export" + QueryTable[0].QueryRoutine = SpxInitSetIpxDeviceName; + QueryTable[0].Flags = 0; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + + // 2) Stop + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + + + + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - NULL. + + EntryContext - NULL. + +Return Value: + + status + +--*/ + +{ + PWSTR fileName; + NTSTATUS status = STATUS_SUCCESS; + + fileName = (PWSTR)CTEAllocMem(ValueLength); + if (fileName != NULL) { + RtlCopyMemory(fileName, ValueData, ValueLength); + RtlInitUnicodeString (&IpxDeviceName, fileName); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return(status); +} + diff --git a/private/ntos/tdi/isn/spx/spxsend.c b/private/ntos/tdi/isn/spx/spxsend.c new file mode 100644 index 000000000..6b856953d --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxsend.c @@ -0,0 +1,262 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.c + +Abstract: + + This module contains code that implements the send engine for the + SPX transport provider. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXSEND + +VOID +SpxSendComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pNdisBuffer; + CTELockHandle lockHandle; + UINT bufCount; + PREQUEST pRequest = NULL; + BOOLEAN completeReq = FALSE, freePkt = FALSE, + orphaned = FALSE, lockHeld = FALSE; + + pSendResd = (PSPX_SEND_RESD)(pNdisPkt->ProtocolReserved); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: For %lx with status **%lx**\n", + pNdisPkt, NdisStatus)); + } +#endif + + // IPX changes the length set for the first ndis buffer descriptor. + // Change it back to its original value here. + NdisQueryPacket(pNdisPkt, NULL, &bufCount, &pNdisBuffer, NULL); + NdisAdjustBufferLength(pNdisBuffer, IpxMacHdrNeeded + MIN_IPXSPX2_HDRSIZE); + + do + { + pSpxConnFile = pSendResd->sr_ConnFile; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; +#if defined(__PNP) + // + // if IPX gave us a new LocalTarget, use for our next send. + // + // But if we are sending connect requests by iterating over NicIds, + // dont update the local target bcoz that will screw up our iteration + // logic. + // + if ( DEVICE_NETWORK_PATH_NOT_FOUND == NdisStatus + && + !( + SPX_CONN_CONNECTING(pSpxConnFile) && + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) + ) ) { + + pSpxConnFile->scf_LocalTarget = pSendResd->LocalTarget; + + // + // Renegotiate the max packet size if we have an active SPX2 + // session going on and we negotiated the max size originally. + // + if ( SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG) ) { + + // + // this call will get the local max size on this new local target + // from IPX. + // + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr ); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + + } +#endif __PNP + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0); + + // IPX dont own this packet nomore. + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + + // If a send packet has been aborted, then we need to call + // abort send to go ahead and free up this packet, and deref associated + // request, if there is one, potentially completing it. + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) != 0) + { + spxConnAbortSendPkt( + pSpxConnFile, + pSendResd, + SPX_CALL_TDILEVEL, + lockHandle); + + lockHeld = FALSE; + break; + } + + // If there is an associated request, remove reference on it. BUT for a + // sequenced packet only if it has been acked and is waiting for the request + // to be dereferenced. It is already dequeued from queue, just free it up. + if ((((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) && + ((pSendResd->sr_State & SPX_SENDPKT_SEQ) == 0)) || + ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0)) + { + freePkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0); + + pRequest = pSendResd->sr_Request; + CTEAssert(pRequest != NULL); + + DBGPRINT(SEND, DBG, + ("IpxSendComplete: ReqRef before dec %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest))); + + // Deref the request and see if we complete it now. We always have our + // own reference on the request. + // !!! Status should already have been set in request...!!! + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + + completeReq = TRUE; + + // If this is acked already, request is not on list. + // BUG #11626 + if ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) == 0) + { + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + } + } + } + + // Do we destroy this packet? + if ((pSendResd->sr_State & SPX_SENDPKT_DESTROY) != 0) + { + // Remove this packet from the send list in the connection. + DBGPRINT(SEND, INFO, + ("IpxSendComplete: destroy packet...\n")); + + SpxConnDequeueSendPktLock(pSpxConnFile, pNdisPkt); + freePkt = TRUE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (freePkt) + { + DBGPRINT(SEND, INFO, + ("IpxSendComplete: free packet...\n")); + + SpxPktSendRelease(pNdisPkt); + } + + if (completeReq) + { + // If this is a send request, set info to data sent, else it will be + // zero. + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_SEND) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + DBGPRINT(SEND, DBG, + ("IpxSendComplete: complete req %lx.%lx...\n", + REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(pRequest != NULL); + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + SpxCompleteRequest(pRequest); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx DISC Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx.%lx.%lx\n", + pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags, + pSpxConnFile->scf_Flags2)); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; + +} // SpxSendComplete + + + diff --git a/private/ntos/tdi/isn/spx/spxtimer.c b/private/ntos/tdi/isn/spx/spxtimer.c new file mode 100644 index 000000000..bdb4e1d7f --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxtimer.c @@ -0,0 +1,637 @@ +/* + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.c + +Abstract: + + This file implements the timer routines used by the stack. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + + +Revision History: + 23 Feb 1993 Initial Version + +Notes: Tab stop: 4 +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXTIMER + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxTimerInit) +#endif + +// Globals for this module +PTIMERLIST spxTimerList = NULL; +PTIMERLIST spxTimerTable[TIMER_HASH_TABLE] = {0}; +PTIMERLIST spxTimerActive = NULL; +CTELock spxTimerLock = {0}; +LARGE_INTEGER spxTimerTick = {0}; +KTIMER spxTimer = {0}; +KDPC spxTimerDpc = {0}; +ULONG spxTimerId = 1; +LONG spxTimerCount = 0; +USHORT spxTimerDispatchCount = 0; +BOOLEAN spxTimerStopped = FALSE; + + +NTSTATUS +SpxTimerInit( + VOID + ) +/*++ + +Routine Description: + + Initialize the timer component for the appletalk stack. + +Arguments: + + +Return Value: + + +--*/ +{ +#if !defined(_PNP_POWER) + BOOLEAN TimerStarted; +#endif !_PNP_POWER + + // Initialize the timer and its associated Dpc. timer will be kicked + // off when we get the first card arrival notification from ipx + KeInitializeTimer(&spxTimer); + CTEInitLock(&spxTimerLock); + KeInitializeDpc(&spxTimerDpc, spxTimerDpcRoutine, NULL); + spxTimerTick = RtlConvertLongToLargeInteger(SPX_TIMER_TICK); +#if !defined(_PNP_POWER) + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); +#endif !_PNP_POWER + return STATUS_SUCCESS; +} + + + + +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG MsTime, // Schedule after this much time + IN PVOID pContext // Context(s) to pass to the routine + ) +/*++ + +Routine Description: + + Insert an event in the timer event list. If the list is empty, then + fire off a timer. The time is specified in ms. We convert to ticks. + Each tick is currently 100ms. It may not be zero or negative. The internal + timer fires at 100ms granularity. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + ULONG DeltaTime; + ULONG Id = 0; + + // Convert to ticks. + DeltaTime = MsTime/SPX_MS_TO_TICKS; + if (DeltaTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + DeltaTime = 1; + } + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + // Negative or Zero DeltaTime is invalid. + CTEAssert (DeltaTime > 0); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Routine %lx, Time %d, Context %lx\n", + Worker, DeltaTime, pContext)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, FATAL, + ("SpxTimerScheduleEvent: Called after Flush !!\n")); + } + + else do + { + pList = SpxBPAllocBlock(BLKID_TIMERLIST); + + if (pList == NULL) + { + break; + } + +#if DBG + pList->tmr_Signature = TMR_SIGNATURE; +#endif + pList->tmr_Cancelled = FALSE; + pList->tmr_Worker = Worker; + pList->tmr_AbsTime = DeltaTime; + pList->tmr_Context = pContext; + + Id = pList->tmr_Id = spxTimerId++; + + // Take care of wrap around + if (spxTimerId == 0) + spxTimerId = 1; + + // Enqueue this handler + spxTimerEnqueue(pList); + } while (FALSE); + + CTEFreeLock(&spxTimerLock, lockHandle); + + return Id; +} + + + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This is called in at DISPATCH_LEVEL when the timer expires. The entry at + the head of the list is decremented and if ZERO unlinked and dispatched. + If the list is non-empty, the timer is fired again. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + BOOLEAN TimerStarted; + ULONG ReEnqueueTime; + CTELockHandle lockHandle; + + pKDpc; pContext; SystemArgument1; SystemArgument2; + +#if defined(_PNP_POWER) + CTEGetLock(&spxTimerLock, &lockHandle); + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + + CTEFreeLock(&spxTimerLock, lockHandle); + return; + } +#else + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + return; + } + + CTEGetLock(&spxTimerLock, &lockHandle); +#endif _PNP_POWER + + SpxTimerCurrentTime ++; // Update our relative time + +#ifdef PROFILING + // This is the only place where this is changed. And it always increases. + SpxStatistics.stat_ElapsedTime = SpxTimerCurrentTime; +#endif + + // We should never be here if we have no work to do + if ((spxTimerList != NULL)) + { + // Careful here. If two guys wanna go off together - let them !! + if (spxTimerList->tmr_RelDelta != 0) + (spxTimerList->tmr_RelDelta)--; + + // Dispatch the entry if it is ready to go + if (spxTimerList->tmr_RelDelta == 0) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + + // Unlink from the list + spxTimerList = pList->tmr_Next; + if (spxTimerList != NULL) + spxTimerList->tmr_Prev = &spxTimerList; + + // Unlink from the hash table now + for (ppList = &spxTimerTable[pList->tmr_Id % TIMER_HASH_TABLE]; + *ppList != NULL; + ppList = &((*ppList)->tmr_Overflow)) + { + CTEAssert(VALID_TMR(*ppList)); + if (*ppList == pList) + { + *ppList = pList->tmr_Overflow; + break; + } + } + + CTEAssert (*ppList == pList->tmr_Overflow); + + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Dispatching %lx\n", + pList->tmr_Worker)); + + spxTimerDispatchCount ++; + spxTimerCount --; + spxTimerActive = pList; + CTEFreeLock(&spxTimerLock, lockHandle); + + // If reenqueue time is 0, do not requeue. If 1, then requeue with + // current value, else use value specified. + ReEnqueueTime = (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Reenequeu time %lx.%lx\n", + ReEnqueueTime, pList->tmr_AbsTime)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerActive = NULL; + spxTimerDispatchCount --; + + if (ReEnqueueTime != TIMER_DONT_REQUEUE) + { + // If this chappie was cancelled while it was running + // and it wants to be re-queued, do it right away. + if (pList->tmr_Cancelled) + { + (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + else + { + if (ReEnqueueTime != TIMER_REQUEUE_CUR_VALUE) + { + pList->tmr_AbsTime = ReEnqueueTime/SPX_MS_TO_TICKS; + if (pList->tmr_AbsTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld\n", + pList->tmr_AbsTime)); + } + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld.%ld\n", + ReEnqueueTime, pList->tmr_AbsTime)); + } + + spxTimerEnqueue(pList); + } + } + else + { + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + } + } + +#if defined(_PNP_POWER) + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + // it is possible that while we were here in Dpc, PNP_ADD_DEVICE + // restarted the timer, so this assert is commented out for PnP +// CTEAssert(!TimerStarted); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +#else + CTEFreeLock(&spxTimerLock, lockHandle); + + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); + } +#endif _PNP_POWER +} + + +VOID +spxTimerEnqueue( + IN PTIMERLIST pListNew + ) +/*++ + +Routine Description: + + Here is a thesis on the code that follows. + + The timer events are maintained as a list which the timer dpc routine + looks at every timer tick. The list is maintained in such a way that only + the head of the list needs to be updated every tick i.e. the entire list + is never scanned. The way this is achieved is by keeping delta times + relative to the previous entry. + + Every timer tick, the relative time at the head of the list is decremented. + When that goes to ZERO, the head of the list is unlinked and dispatched. + + To give an example, we have the following events queued at time slots + X Schedule A after 10 ticks. + X+3 Schedule B after 5 ticks. + X+5 Schedule C after 4 ticks. + X+8 Schedule D after 6 ticks. + + So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and + D at X+14 (X+8+6). + + The above example covers all the situations. + + - NULL List. + - Inserting at head of list. + - Inserting in the middle of the list. + - Appending to the list tail. + + The list will look as follows. + + BEFORE AFTER + ------ ----- + + X Head -->| Head -> A(10) ->| + A(10) + + X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->| + B(5) + + X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->| + C(4) + + X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->| + D(6) + + The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + ULONG DeltaTime = pListNew->tmr_AbsTime; + + // The DeltaTime is adjusted in every pass of the loop to reflect the + // time after the previous entry that the new entry will schedule. + for (ppList = &spxTimerList; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Next) + { + CTEAssert(VALID_TMR(pList)); + if (DeltaTime <= pList->tmr_RelDelta) + { + pList->tmr_RelDelta -= DeltaTime; + break; + } + DeltaTime -= pList->tmr_RelDelta; + } + + + // Link this in the chain + pListNew->tmr_RelDelta = DeltaTime; + pListNew->tmr_Next = pList; + pListNew->tmr_Prev = ppList; + *ppList = pListNew; + if (pList != NULL) + { + pList->tmr_Prev = &pListNew->tmr_Next; + } + + // Now link it in the hash table + pListNew->tmr_Overflow = spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE]; + spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE] = pListNew; + spxTimerCount ++; +} + + + + +VOID +SpxTimerFlushAndStop( + VOID + ) +/*++ + +Routine Description: + + Force all entries in the timer queue to be dispatched immediately. No + more queue'ing of timer routines is permitted after this. The timer + essentially shuts down. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + + CTEAssert (KeGetCurrentIrql() == LOW_LEVEL); + + DBGPRINT(SYSTEM, ERR, + ("SpxTimerFlushAndStop: Entered\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerStopped = TRUE; + + KeCancelTimer(&spxTimer); + + if (spxTimerList != NULL) + { + // Dispatch all entries right away + while (spxTimerList != NULL) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + spxTimerList = pList->tmr_Next; + + DBGPRINT(SYSTEM, INFO, + ("spxTimerFlushAndStop: Dispatching %lx\n", + pList->tmr_Worker)); + + // The timer routines assume they are being called at DISPATCH + // level. This is OK since we are calling with SpinLock held. + + (*pList->tmr_Worker)(pList->tmr_Context, TRUE); + + spxTimerCount --; + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + RtlZeroMemory(spxTimerTable, sizeof(spxTimerTable)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + // Wait for all timer routines to complete + while (spxTimerDispatchCount != 0) + { + SpxSleep(SPX_TIMER_WAIT); + } +} + + + + +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue + ) +/*++ + +Routine Description: + + Cancel a previously scheduled timer event, if it hasn't fired already. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + CTELockHandle lockHandle; + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: Entered for TimerId %ld\n", TimerId)); + + CTEAssert(TimerId != 0); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (ppList = &spxTimerTable[TimerId % TIMER_HASH_TABLE]; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Overflow) + { + CTEAssert(VALID_TMR(pList)); + // If we find it, cancel it + if (pList->tmr_Id == TimerId) + { + // Unlink this from the hash table + *ppList = pList->tmr_Overflow; + + // ... and from the list + if (pList->tmr_Next != NULL) + { + pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta; + pList->tmr_Next->tmr_Prev = pList->tmr_Prev; + } + *(pList->tmr_Prev) = pList->tmr_Next; + + spxTimerCount --; + if (ReEnqueue) + spxTimerEnqueue(pList); + else SpxBPFreeBlock(pList, BLKID_TIMERLIST); + break; + } + } + + // If we could not find it in the list, see if it currently running. + // If so mark him to not reschedule itself, only if reenqueue was false. + if (pList == NULL) + { + if ((spxTimerActive != NULL) && + (spxTimerActive->tmr_Id == TimerId) && + !ReEnqueue) + { + spxTimerActive->tmr_Cancelled = TRUE; + } + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: %s for Id %ld\n", + (pList != NULL) ? "Success" : "Failure", TimerId)); + + return (pList != NULL); +} + + + + +#if DBG + +VOID +SpxTimerDumpList( + VOID + ) +{ + PTIMERLIST pList; + ULONG CumTime = 0; + CTELockHandle lockHandle; + + DBGPRINT(DUMP, FATAL, + ("TIMER LIST: (Times are in %dms units\n", 1000)); + DBGPRINT(DUMP, FATAL, + ("\tTimerId Time(Abs) Time(Rel) Routine Address\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (pList = spxTimerList; + pList != NULL; + pList = pList->tmr_Next) + { + CumTime += pList->tmr_RelDelta; + DBGPRINT(DUMP, FATAL, + ("\t% 6lx %5d %5ld %lx\n", + pList->tmr_Id, pList->tmr_AbsTime, CumTime, pList->tmr_Worker)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +} + +#endif diff --git a/private/ntos/tdi/isn/spx/spxutils.c b/private/ntos/tdi/isn/spx/spxutils.c new file mode 100644 index 000000000..024a36988 --- /dev/null +++ b/private/ntos/tdi/isn/spx/spxutils.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.c + +Abstract: + + This contains all utility routines for the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXUTILS + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + UINT length = 0; + + while (*Wstr++) + { + length += sizeof(WCHAR); + } + + return length; +} + + + + +LONG +SpxRandomNumber( + VOID + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LARGE_INTEGER Li; + static LONG seed = 0; + + // Return a positive pseudo-random number; simple linear congruential + // algorithm. ANSI C "rand()" function. + + if (seed == 0) + { + KeQuerySystemTime(&Li); + seed = Li.LowPart; + } + + seed *= (0x41C64E6D + 0x3039); + + return (seed & 0x7FFFFFFF); +} + + + + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType + ) +/*++ + +Routine Description: + + For PROTO_SPX, i'd return a device name from the dll of the form + \Device\IsnSpx\SpxStream (for SOCK_STREAM) or + \Device\IsnSpx\Spx (for SOCK_SEQPKT) + + and for PROTO_SPXII (the more common case we hope, even if + internally we degrade to SPX1 cause of the remote client's + limitations) + \Device\IsnSpx\Stream (for SOCK_STREAM) or + \Device\IsnSpx (for SOCK_SEQPKT) + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING typeString; + + *SocketType = SOCKET2_TYPE_SEQPKT; + + // Check for the socket type + do + { + if (RemainingFileName->Length == 0) + { + break; + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_STREAM; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_SEQPKT; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET2STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET2STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET2_TYPE_STREAM; + break; + } + } + + status = STATUS_NO_SUCH_DEVICE; + + } while (FALSE); + + return(status); +} + + + + +#define ONE_MS_IN_100ns -10000L // 1ms in 100ns units + +VOID +SpxSleep( + IN ULONG TimeInMs + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + KTIMER SleepTimer; + + ASSERT (KeGetCurrentIrql() == LOW_LEVEL); + + KeInitializeTimer(&SleepTimer); + + KeSetTimer(&SleepTimer, + RtlConvertLongToLargeInteger(TimeInMs * ONE_MS_IN_100ns), + NULL); + + KeWaitForSingleObject(&SleepTimer, UserRequest, KernelMode, FALSE, NULL); + return; +} + + + + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + for (i=0;iTAAddressCount;i++) + { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} // SpxParseTdiAddress + + + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: runt address\n")); + + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) + { + if (addressName->Address > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + return TRUE; + +} // SpxValidateTdiAddress + + + + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. It will write + less than the full address if the buffer is too short. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + AddressBufferLength - The length of the buffer. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + The number of bytes written into AddressBuffer. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + TA_IPX_ADDRESS TempAddress; + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + } + else + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)&TempAddress; + } + + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)Network; + SpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(SpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + return sizeof(TA_IPX_ADDRESS); + } + else + { + RtlCopyMemory(AddressBuffer, &TempAddress, AddressBufferLength); + return AddressBufferLength; + } + +} // SpxBuildTdiAddress + + + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr + ) +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)pIpxAddr; + RtlCopyMemory( + SpxAddress->Address[0].Address[0].NodeAddress, + pIpxAddr+4, + 6); + + GETSHORT2SHORT( + &SpxAddress->Address[0].Address[0].Socket, + pIpxAddr + 10); + + return; +} + + + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1 + ) +/*++ + +Routine Description: + + +Arguments: + + NewT1 - New value for the RTT in ms. + +Return Value: + + +--*/ +{ + int baseT1, error; + + // + // VAN JACOBSEN Algorithm. From Internetworking with Tcp/ip + // (Comer) book. + // + + error = NewT1 - (pSpxConnFile->scf_AveT1 >> 3); + pSpxConnFile->scf_AveT1 += error; + if (pSpxConnFile->scf_AveT1 <= 0) // Make sure not too small + { + pSpxConnFile->scf_AveT1 = SPX_T1_MIN; + } + + if (error < 0) + error = -error; + + error -= (pSpxConnFile->scf_DevT1 >> 2); + pSpxConnFile->scf_DevT1 += error; + if (pSpxConnFile->scf_DevT1 <= 0) + pSpxConnFile->scf_DevT1 = 1; + + baseT1 = (((pSpxConnFile->scf_AveT1 >> 2) + pSpxConnFile->scf_DevT1) >> 1); + + // If less then min - set it + if (baseT1 < SPX_T1_MIN) + baseT1 = SPX_T1_MIN; + + // Set the new value + DBGPRINT(TDI, DBG, + ("SpxCalculateNewT1: Old value %lx New %lx\n", + pSpxConnFile->scf_BaseT1, baseT1)); + + pSpxConnFile->scf_BaseT1 = baseT1; + + // At the time of restarting the timer,we convert this to a tick value. + return; +} + diff --git a/private/ntos/tdi/isn/spx/up/makefile b/private/ntos/tdi/isn/spx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isn/spx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isn/spx/up/sources b/private/ntos/tdi/isn/spx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isn/spx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/dirs b/private/ntos/tdi/isnp/dirs new file mode 100644 index 000000000..a93e8e700 --- /dev/null +++ b/private/ntos/tdi/isnp/dirs @@ -0,0 +1,28 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + ipx \ + nb \ + spx + +OPTIONAL_DIRS= diff --git a/private/ntos/tdi/isnp/inc/bind.h b/private/ntos/tdi/isnp/inc/bind.h new file mode 100644 index 000000000..60294349d --- /dev/null +++ b/private/ntos/tdi/isnp/inc/bind.h @@ -0,0 +1,563 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + bind.h + +Abstract: + + Private include file for the ISN transport. It defines the + structures used for binding between IPX and the upper drivers. + +Author: + + Adam Barr (adamba) 04-Oct-1993 + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#ifndef _ISN_BIND_ +#define _ISN_BIND_ + +// +// Retrieve the common definitions. +// + +#include + + +// +// Define the IOCTL used for binding between the upper +// drivers and IPX. +// + +#define _IPX_CONTROL_CODE(request,method) \ + CTL_CODE(FILE_DEVICE_TRANSPORT, request, method, FILE_ANY_ACCESS) + +#define IOCTL_IPX_INTERNAL_BIND _IPX_CONTROL_CODE( 0x1234, METHOD_BUFFERED ) + + +// +// Identifier for the drivers in ISN. +// + +#define IDENTIFIER_NB 0 +#define IDENTIFIER_SPX 1 +#define IDENTIFIER_RIP 2 +#define IDENTIFIER_IPX 3 + +#ifdef _PNP_POWER +// +// This the number of PVOIDs in the beginning of the SEND_RESERVED +// section of a packet header, to be set aside by the ISN clients (NB/SPX) +// for IPX's private use. +// +#define SEND_RESERVED_COMMON_SIZE 8 +#endif + +// +// Definition of a RIP router table entry. +// + +typedef struct _IPX_ROUTE_ENTRY { + UCHAR Network[4]; + USHORT NicId; + UCHAR NextRouter[6]; + NDIS_HANDLE NdisBindingContext; + USHORT Flags; + USHORT Timer; + UINT Segment; + USHORT TickCount; + USHORT HopCount; + LIST_ENTRY AlternateRoute; + LIST_ENTRY NicLinkage; + struct { + LIST_ENTRY Linkage; + ULONG Reserved[1]; + } PRIVATE; +} IPX_ROUTE_ENTRY, * PIPX_ROUTE_ENTRY; + +// +// Definition of the Flags values. +// + +#define IPX_ROUTER_PERMANENT_ENTRY 0x0001 // entry should never be deleted +#define IPX_ROUTER_LOCAL_NET 0x0002 // locally attached network +#define IPX_ROUTER_SCHEDULE_ROUTE 0x0004 // call ScheduleRouteHandler after using +#define IPX_ROUTER_GLOBAL_WAN_NET 0x0008 // this is for rip's global network number + + +// +// Definition of the structure provided on a find +// route/find route completion call. +// + +// +// [SA] Bug #15094 added node number to the structure. +// + +typedef struct _IPX_FIND_ROUTE_REQUEST { + UCHAR Network[4]; + UCHAR Node[6] ; + IPX_LOCAL_TARGET LocalTarget; + UCHAR Identifier; + UCHAR Type; + UCHAR Reserved1[2]; + PVOID Reserved2; + LIST_ENTRY Linkage; +} IPX_FIND_ROUTE_REQUEST, *PIPX_FIND_ROUTE_REQUEST; + +// +// Definitions for the Type value. +// + +#define IPX_FIND_ROUTE_NO_RIP 1 // fail if net is not in database +#define IPX_FIND_ROUTE_RIP_IF_NEEDED 2 // return net if in database, otherwise RIP out +#define IPX_FIND_ROUTE_FORCE_RIP 3 // re-RIP even if net is in database + + +// +// Structure used when querying the line information +// for a specific NID ID. +// + +typedef struct _IPX_LINE_INFO { + UINT LinkSpeed; + UINT MaximumPacketSize; + UINT MaximumSendSize; + UINT MacOptions; +} IPX_LINE_INFO, *PIPX_LINE_INFO; + + + +// +// Functions provided by the upper driver. +// + +typedef VOID +(*IPX_INTERNAL_RECEIVE) ( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize +); + +typedef VOID +(*IPX_INTERNAL_RECEIVE_COMPLETE) ( + IN USHORT NicId +); + +typedef VOID +(*IPX_INTERNAL_STATUS) ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength +); + +typedef VOID +(*IPX_INTERNAL_SEND_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE_COMPLETE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute +); + +typedef VOID +(*IPX_INTERNAL_LINE_UP) ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData +); + +typedef VOID +(*IPX_INTERNAL_LINE_DOWN) ( + IN USHORT NicId +); + +typedef VOID +(*IPX_INTERNAL_SCHEDULE_ROUTE) ( + IN PIPX_ROUTE_ENTRY RouteEntry +); + +#if defined(_PNP_POWER) + +// +// following opcodes are used when calling the +// above handler. +// +typedef enum _IPX_PNP_OPCODE { + IPX_PNP_ADD_DEVICE, // 0 - addition of the first adapter + IPX_PNP_DELETE_DEVICE, // 1 - deletion of the last adapter + IPX_PNP_TRANSLATE_DEVICE, // 2 - translate device resource + IPX_PNP_TRANSLATE_ADDRESS, // 3 - translate address resource + IPX_PNP_ADDRESS_CHANGE, // 4 - Adapter address or Reserved address changed + IPX_PNP_MAX_OPCODES, // 5 +} IPX_PNP_OPCODE, *PIPX_PNP_OPCODE; + +// +// PnP event notification handler. +// +typedef VOID +(*IPX_INTERNAL_PNP_NOTIFICATION) ( + IN IPX_PNP_OPCODE PnPOpcode, + IN OUT PVOID PnpData +); + +// +// Pointer to this structure is passed in PnPData portion of +// the above handler when the opcode is ADD_DEVICE or DELETE_DEVICE. +// +typedef struct _IPX_PNP_INFO { + ULONG NetworkAddress; + UCHAR NodeAddress[6]; + BOOLEAN NewReservedAddress; // where the above is a new reserved + // address for the Ipx clients. + BOOLEAN FirstORLastDevice; // is this a first card arrival or last card deletion. + IPX_LINE_INFO LineInfo; // New LineInfo. + NIC_HANDLE NicHandle; +} IPX_PNP_INFO, *PIPX_PNP_INFO; + +#endif _PNP_POWER + +// +// Input to the bind IOCTL +// + +typedef struct _IPX_INTERNAL_BIND_INPUT { + USHORT Version; + UCHAR Identifier; + BOOLEAN BroadcastEnable; + UINT LookaheadRequired; + UINT ProtocolOptions; + IPX_INTERNAL_RECEIVE ReceiveHandler; + IPX_INTERNAL_RECEIVE_COMPLETE ReceiveCompleteHandler; + IPX_INTERNAL_STATUS StatusHandler; + IPX_INTERNAL_SEND_COMPLETE SendCompleteHandler; + IPX_INTERNAL_TRANSFER_DATA_COMPLETE TransferDataCompleteHandler; + IPX_INTERNAL_FIND_ROUTE_COMPLETE FindRouteCompleteHandler; + IPX_INTERNAL_LINE_UP LineUpHandler; + IPX_INTERNAL_LINE_DOWN LineDownHandler; + IPX_INTERNAL_SCHEDULE_ROUTE ScheduleRouteHandler; +#if defined(_PNP_POWER) + IPX_INTERNAL_PNP_NOTIFICATION PnPHandler; +#endif _PNP_POWER + ULONG RipParameters; +} IPX_INTERNAL_BIND_INPUT, * PIPX_INTERNAL_BIND_INPUT; + +#if defined(_PNP_POWER) +#define ISN_VERSION 2 +#endif _PNP_POWER +// +// Bit mask values for RipParameters. +// + +#define IPX_RIP_PARAM_GLOBAL_NETWORK 0x00000001 // single network for all WANS + + + +// +// Functions provided by the lower driver. +// + +typedef NDIS_STATUS +(*IPX_INTERNAL_SEND) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest +); + +typedef NTSTATUS +(*IPX_INTERNAL_QUERY) ( + IN ULONG InternalQueryType, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif _PNP_POWER + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA)( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +// +// Definitions of the internal query types. In all cases +// STATUS_SUCCESS is returned if the request succeeds, and +// STATUS_BUFFER_TOO_SMALL is returned, and BufferLengthNeeded +// set if specified, if the buffer is too short. Other +// return codes are defined below. The routine never pends. +// + +// +// This is used to query the line info. NicId specifies which one +// to query. Buffer contains an IPX_LINE_INFO structure which is +// used to return the information. Other return values: +// +// STATUS_INVALID_PARAMETER - NicId is invalid. +// + +#define IPX_QUERY_LINE_INFO 1 + +// +// This is used to query the maximum NicId. NicId is unused. The +// Buffer contains a USHORT which is used to return the information. +// + +#define IPX_QUERY_MAXIMUM_NIC_ID 2 + +// +// This is used to determine if the IPX address specified was sent +// by our local machine. If the address is the source address of a +// received frame, NicId should be the ID that was indicated; otherwise +// it should be set to 0. Buffer holds a TDI_ADDRESS_IPX. This +// call returns STATUS_SUCCESS if the address is local, and +// STATUS_NO_SUCH_DEVICE if not. +// + +#define IPX_QUERY_IS_ADDRESS_LOCAL 3 + +// +// This is used to query the receive buffer space of a given NicId. +// Buffer contains a ULONG which is used to return the information. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_RECEIVE_BUFFER_SPACE 4 + +// +// This is used to query the local IPX address of a given NicId. +// Buffer contains a TDI_ADDRESS_IPX structure (the Socket is +// returned as 0). If it is queried on net 0 it returns the +// virtual network if there is one, otherwise STATUS_INVALID_PARAMETER. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_IPX_ADDRESS 5 + +// +// This is used to return the source routing information for +// a give remote address. NicId will be the NIC the packet was +// received from. The IPX_SOURCE_ROUTING_QUERY is contained +// in Buffer. Always returns STATUS_SUCCESS, although the +// SourceRoutingLength may be 0 for unknown remotes. +// +// The source routing is return in the direction it was received +// from the remote, not the direction used in replying. The +// MaximumSendSize includes the IPX header (as it does in +// IPX_LINE_INFO). +// + +#define IPX_QUERY_SOURCE_ROUTING 6 + +typedef struct _IPX_SOURCE_ROUTING_INFO { + USHORT Identifier; // input: the caller's IDENTIFIER_SPX, _NB, etc. + UCHAR RemoteAddress[6]; // input: the remote address + UCHAR SourceRouting[18]; // output: room for the maximum source route + USHORT SourceRoutingLength; // output: the valid length of source route + ULONG MaximumSendSize; // output: based on nic and source routing +} IPX_SOURCE_ROUTING_INFO, * PIPX_SOURCE_ROUTING_INFO; + +// +// This is used to query the maximum NicId over which outgoing type +// 20 packets should be sent. It will be less than or equal to +// the IPX_QUERY_MAXIMUM_NIC_ID value. What's excluded are down wan +// lines and dialin wan lines if DisableDialinNetbios bit 1 is set. +// + +#define IPX_QUERY_MAX_TYPE_20_NIC_ID 7 + +#if defined(_PNP_POWER) + +// +// This are used by NB to pass down these TDI queries which cannot +// be completed in NB. +// + +#define IPX_QUERY_DATA_LINK_ADDRESS 8 +#define IPX_QUERY_NETWORK_ADDRESS 9 + +#endif _PNP_POWER + +// +// Output of a non-RIP bind. +// + +typedef struct _IPX_INTERNAL_BIND_OUTPUT { + USHORT Version; + UCHAR Node[6]; + UCHAR Network[4]; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_LINE_INFO LineInfo; + IPX_INTERNAL_SEND SendHandler; + IPX_INTERNAL_FIND_ROUTE FindRouteHandler; + IPX_INTERNAL_QUERY QueryHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; +} IPX_INTERNAL_BIND_OUTPUT, * PIPX_INTERNAL_BIND_OUTPUT; + + + +// +// Lower driver functions provided only for RIP. +// + +typedef UINT +(*IPX_INTERNAL_GET_SEGMENT) ( + IN UCHAR Network[4] +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_ROUTE) ( + IN UINT Segment, + IN UCHAR Network[4] +); + +typedef BOOLEAN +(*IPX_INTERNAL_ADD_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef BOOLEAN +(*IPX_INTERNAL_DELETE_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_FIRST_ROUTE) ( + IN UINT Segment +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_NEXT_ROUTE) ( + IN UINT Segment +); + +typedef VOID +(*IPX_INTERNAL_INCREMENT_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +typedef ULONG +(*IPX_INTERNAL_QUERY_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif + +); + +// +// Describes a single network. +// + +typedef struct _IPX_NIC_DATA { + USHORT NicId; + UCHAR Node[6]; + UCHAR Network[4]; + IPX_LINE_INFO LineInfo; + NDIS_MEDIUM DeviceType; + ULONG EnableWanRouter; +} IPX_NIC_DATA, * PIPX_NIC_DATA; + + +// +// Describes all networks. +// + +typedef struct _IPX_NIC_INFO_BUFFER { + USHORT NicCount; + USHORT VirtualNicId; + UCHAR VirtualNetwork[4]; + IPX_NIC_DATA NicData[1]; +} IPX_NIC_INFO_BUFFER, * PIPX_NIC_INFO_BUFFER; + + +// +// Output from a RIP bind (the actual structure size is +// based on the number of IPX_NIC_DATA elements in the +// final IPX_NIC_INFO_BUFFER structure). +// + +typedef struct _IPX_INTERNAL_BIND_RIP_OUTPUT { + USHORT Version; + USHORT MaximumNicCount; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_INTERNAL_SEND SendHandler; + UINT SegmentCount; + KSPIN_LOCK * SegmentLocks; + IPX_INTERNAL_GET_SEGMENT GetSegmentHandler; + IPX_INTERNAL_GET_ROUTE GetRouteHandler; + IPX_INTERNAL_ADD_ROUTE AddRouteHandler; + IPX_INTERNAL_DELETE_ROUTE DeleteRouteHandler; + IPX_INTERNAL_GET_FIRST_ROUTE GetFirstRouteHandler; + IPX_INTERNAL_GET_NEXT_ROUTE GetNextRouteHandler; + IPX_INTERNAL_INCREMENT_WAN_INACTIVITY IncrementWanInactivityHandler; + IPX_INTERNAL_QUERY_WAN_INACTIVITY QueryWanInactivityHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; + IPX_NIC_INFO_BUFFER NicInfoBuffer; +} IPX_INTERNAL_BIND_RIP_OUTPUT, * PIPX_INTERNAL_BIND_RIP_OUTPUT; + +#endif // _ISN_BIND_ + + +#ifndef _IPXCP_CONFIG_ +#define _IPXCP_CONFIG_ + +typedef struct _IPXCP_CONFIGURATION { + USHORT Version; + USHORT Length; + UCHAR Network[4]; + UCHAR LocalNode[6]; + UCHAR RemoteNode[6]; + ULONG ConnectionClient; // 0 - Server, 1 - Client +} IPXCP_CONFIGURATION, *PIPXCP_CONFIGURATION; + +#endif // _IPXCP_CONFIG_ + diff --git a/private/ntos/tdi/isnp/inc/ioctls.h b/private/ntos/tdi/isnp/inc/ioctls.h new file mode 100644 index 000000000..e7dd7b81a --- /dev/null +++ b/private/ntos/tdi/isnp/inc/ioctls.h @@ -0,0 +1,155 @@ +#define VER_IOCH "@(#)MCS ipx/h/ioctls.h 1.00.00 - 08 APR 1993"; + +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Driver for Windows NT +* +* Module: ipx/h/ioctls.h +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* IOCTL defines +* +****************************************************************************/ + +/** Ioctls for IPX - (X) = User callable **/ + +/** + ioctls will values 100 - 150 were added for the NT port. +**/ + +#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8)) +#define MIPX_SETNODEADDR I_MIPX | 0 /* Set the node address */ +#define MIPX_SETNETNUM I_MIPX | 1 /* Set the network number */ +#define MIPX_SETPTYPE I_MIPX | 2 /* (X) Set the packet type */ +#define MIPX_SENTTYPE I_MIPX | 3 /* (X) Set the xport type */ +#define MIPX_SETPKTSIZE I_MIPX | 4 /* Set the packet size */ +#define MIPX_SETSAP I_MIPX | 5 /* Set the sap/type field */ +#define MIPX_SENDOPTS I_MIPX | 6 /* (X) Send options on recv */ +#define MIPX_NOSENDOPTS I_MIPX | 7 /* (X) Don't send options on recv */ +#define MIPX_SENDSRC I_MIPX | 8 /* (X) Send source address up */ +#define MIPX_NOSENDSRC I_MIPX | 9 /* (X) Don't Send source address up */ +#define MIPX_CONVBCAST I_MIPX | 10 /* Convert TKR bcast to func addr */ +#define MIPX_NOCONVBCAST I_MIPX | 11 /* Don't cnvrt TKR bcast to funcaddr */ +#define MIPX_SETCARDTYPE I_MIPX | 12 /* Set 802.3 or ETH type */ +#define MIPX_STARGROUP I_MIPX | 13 /* This is stargroup */ +#define MIPX_SWAPLENGTH I_MIPX | 14 /* Set flag for swapping 802.3 length */ +#define MIPX_SENDDEST I_MIPX | 15 /* (X) Send dest. address up */ +#define MIPX_NOSENDDEST I_MIPX | 16 /* (X) Don't send dest. address up */ +#define MIPX_SENDFDEST I_MIPX | 17 /* (X) Send final dest. address up */ +#define MIPX_NOSENDFDEST I_MIPX | 18 /* (X) Don't send final dest. up */ + +/** Added for NT port **/ + +#define MIPX_SETVERSION I_MIPX | 100 /* Set card version */ +#define MIPX_GETSTATUS I_MIPX | 101 +#define MIPX_SENDADDROPT I_MIPX | 102 /* (X) Send ptype w/addr on recv */ +#define MIPX_NOSENDADDROPT I_MIPX | 103 /* (X) Stop sending ptype on recv */ +#define MIPX_CHECKSUM I_MIPX | 104 /* Enable/Disable checksum */ +#define MIPX_GETPKTSIZE I_MIPX | 105 /* Get max packet size */ +#define MIPX_SENDHEADER I_MIPX | 106 /* Send header with data */ +#define MIPX_NOSENDHEADER I_MIPX | 107 /* Don't send header with data */ +#define MIPX_SETCURCARD I_MIPX | 108 /* Set current card for IOCTLs */ +#define MIPX_SETMACTYPE I_MIPX | 109 /* Set the Cards MAC type */ +#define MIPX_DOSROUTE I_MIPX | 110 /* Do source routing on this card*/ +#define MIPX_NOSROUTE I_MIPX | 111 /* Don't source routine the card*/ +#define MIPX_SETRIPRETRY I_MIPX | 112 /* Set RIP retry count */ +#define MIPX_SETRIPTO I_MIPX | 113 /* Set RIP timeout */ +#define MIPX_SETTKRSAP I_MIPX | 114 /* Set the token ring SAP */ +#define MIPX_SETUSELLC I_MIPX | 115 /* Put LLC hdr on packets */ +#define MIPX_SETUSESNAP I_MIPX | 116 /* Put SNAP hdr on packets */ +#define MIPX_8023LEN I_MIPX | 117 /* 1=make even, 0=dont make even*/ +#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/ +#define MIPX_NOSENDPTYPE I_MIPX | 119 /* Don't send ptype in options */ +#define MIPX_FILTERPTYPE I_MIPX | 120 /* Filter on recv ptype */ +#define MIPX_NOFILTERPTYPE I_MIPX | 121 /* Don't Filter on recv ptype */ +#define MIPX_SETSENDPTYPE I_MIPX | 122 /* Set pkt type to send with */ +#define MIPX_GETCARDINFO I_MIPX | 123 /* Get info on a card */ +#define MIPX_SENDCARDNUM I_MIPX | 124 /* Send card num up in options */ +#define MIPX_NOSENDCARDNUM I_MIPX | 125 /* Dont send card num in options*/ +#define MIPX_SETROUTER I_MIPX | 126 /* Set router enabled flag */ +#define MIPX_SETRIPAGE I_MIPX | 127 /* Set RIP age timeout */ +#define MIPX_SETRIPUSAGE I_MIPX | 128 /* Set RIP usage timeout */ +#define MIPX_SETSROUTEUSAGE I_MIPX| 129 /* Set the SROUTE usage timeout */ +#define MIPX_SETINTNET I_MIPX | 130 /* Set internal network number */ +#define MIPX_NOVIRTADDR I_MIPX | 131 /* Turn off virtual net num */ +#define MIPX_VIRTADDR I_MIPX | 132 /* Turn on virtual net num */ +#define MIPX_SETBCASTFLAG I_MIPX | 133 /* Turn on bcast flag in addr */ +#define MIPX_NOBCASTFLAG I_MIPX | 134 /* Turn off bcast flag in addr */ +#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */ +#define MIPX_SETDELAYTIME I_MIPX | 136 /* Set cards delay time */ +#define MIPX_SETROUTEADV I_MIPX | 137 /* Route advertise timeout */ +#define MIPX_SETSOCKETS I_MIPX | 138 /* Set default sockets */ +#define MIPX_SETLINKSPEED I_MIPX | 139 /* Set the link speed for a card*/ +#define MIPX_SETWANFLAG I_MIPX | 140 +#define MIPX_GETCARDCHANGES I_MIPX | 141 /* Wait for card changes */ +#define MIPX_GETMAXADAPTERS I_MIPX | 142 +#define MIPX_REUSEADDRESS I_MIPX | 143 +#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */ + +/** For Source Routing Support **/ + +#define MIPX_SRCLEAR I_MIPX | 200 /* Clear the source routing table*/ +#define MIPX_SRDEF I_MIPX | 201 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRBCAST I_MIPX | 202 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRMULTI I_MIPX | 203 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRREMOVE I_MIPX | 204 /* Remove a node from the table */ +#define MIPX_SRLIST I_MIPX | 205 /* Get the source routing table */ +#define MIPX_SRGETPARMS I_MIPX | 206 /* Get source routing parms */ + +#define MIPX_SETSHOULDPUT I_MIPX | 210 /* Turn on should put call */ +#define MIPX_DELSHOULDPUT I_MIPX | 211 /* Turn off should put call */ +#define MIPX_GETSHOULDPUT I_MIPX | 212 /* Get ptr to mipx_shouldput */ + +/** Added for ISN **/ + +#define MIPX_RCVBCAST I_MIPX | 300 /* (X) Enable broadcast reception */ +#define MIPX_NORCVBCAST I_MIPX | 301 /* (X) Disable broadcast reception */ +#define MIPX_ADAPTERNUM I_MIPX | 302 /* Get maximum adapter number */ +#define MIPX_NOTIFYCARDINFO I_MIPX | 303 /* Pend until card info changes */ +#define MIPX_LOCALTARGET I_MIPX | 304 /* Get local target for address */ +#define MIPX_NETWORKINFO I_MIPX | 305 /* Return info about remote net */ +#define MIPX_ZEROSOCKET I_MIPX | 306 /* Use 0 as source socket on sends */ + +/** Ioctls for SPX **/ + +#define I_MSPX (('S' << 24) | ('P' << 16) | ('P' << 8)) +#define MSPX_SETADDR I_MSPX | 0 /* Set the network address */ +#define MSPX_SETPKTSIZE I_MSPX | 1 /* Set the packet size per card */ +#define MSPX_SETDATASTREAM I_MSPX | 2 /* Set datastream type */ + +/** Added for NT port **/ + +#define MSPX_SETASLISTEN I_MSPX | 100 /* Set as a listen socket */ +#define MSPX_GETSTATUS I_MSPX | 101 /* Get running status */ +#define MSPX_GETQUEUEPTR I_MSPX | 102 /* Get ptr to the streams queue */ +#define MSPX_SETDATAACK I_MSPX | 103 /* Set DATA ACK option */ +#define MSPX_NODATAACK I_MSPX | 104 /* Turn off DATA ACK option */ +#define MSPX_SETMAXPKTSOCK I_MSPX | 105 /* Set the packet size per socket */ +#define MSPX_SETWINDOWCARD I_MSPX | 106 /* Set window size for card */ +#define MSPX_SETWINDOWSOCK I_MSPX | 107 /* Set window size for 1 socket */ +#define MSPX_SENDHEADER I_MSPX | 108 /* Send header with data */ +#define MSPX_NOSENDHEADER I_MSPX | 109 /* Don't send header with data */ +#define MSPX_GETPKTSIZE I_MSPX | 110 /* Get the packet size per card */ +#define MSPX_SETCONNCNT I_MSPX | 111 /* Set the conn req count */ +#define MSPX_SETCONNTO I_MSPX | 112 /* Set the conn req timeout */ +#define MSPX_SETALIVECNT I_MSPX | 113 /* Set the keepalive count */ +#define MSPX_SETALIVETO I_MSPX | 114 /* Set the keepalive timeout */ +#define MSPX_SETALWAYSEOM I_MSPX | 115 /* Turn on always EOM flag */ +#define MSPX_NOALWAYSEOM I_MSPX | 116 /* Turn off always EOM flag */ diff --git a/private/ntos/tdi/isnp/inc/isn.h b/private/ntos/tdi/isnp/inc/isn.h new file mode 100644 index 000000000..7b7e23601 --- /dev/null +++ b/private/ntos/tdi/isnp/inc/isn.h @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + isn.h + +Abstract: + + Private include file for the ISN transport. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include +#include +#include +#include +#include + diff --git a/private/ntos/tdi/isnp/ipx/action.c b/private/ntos/tdi/isnp/ipx/action.c new file mode 100644 index 000000000..807391cae --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/action.c @@ -0,0 +1,1802 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAction + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#include + +typedef struct _GET_PKT_SIZE { + ULONG Unknown; + ULONG MaxDatagramSize; +} GET_PKT_SIZE, *PGET_PKT_SIZE; + + +// +// These structures are used to set and query information +// about our source routing table. +// + +typedef struct _SR_GET_PARAMETERS { + ULONG BoardNumber; // 0-based + ULONG SrDefault; // 0 = single route, 1 = all routes + ULONG SrBroadcast; + ULONG SrMulticast; +} SR_GET_PARAMETERS, *PSR_GET_PARAMETERS; + +typedef struct _SR_SET_PARAMETER { + ULONG BoardNumber; // 0-based + ULONG Parameter; // 0 = single route, 1 = all routes +} SR_SET_PARAMETER, *PSR_SET_PARAMETER; + +typedef struct _SR_SET_REMOVE { + ULONG BoardNumber; // 0-based + UCHAR MacAddress[6]; // remote to drop routing for +} SR_SET_REMOVE, *PSR_SET_REMOVE; + +typedef struct _SR_SET_CLEAR { + ULONG BoardNumber; // 0-based +} SR_SET_CLEAR, *PSR_SET_CLEAR; + +#include + +typedef struct _ISN_ACTION_GET_DETAILS { + USHORT NicId; // passed by caller, returns count if it is 0 + BOOLEAN BindingSet; // returns TRUE if in a set + UCHAR Type; // 1 = lan, 2 = up wan, 3 = down wan + ULONG FrameType; // returns 0 through 3 + ULONG NetworkNumber; // returns virtual net if NicId is 0 + UCHAR Node[6]; // adapter MAC address + WCHAR AdapterName[64]; // terminated with Unicode NULL +} ISN_ACTION_GET_DETAILS, *PISN_ACTION_GET_DETAILS; + + + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiAction request for the transport + provider. + +Arguments: + + Device - The device for the operation. + + Request - Describes the action request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS_FILE AddressFile; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + union { + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PISN_ACTION_GET_NETWORK_INFO GetNetworkInfo; + PISN_ACTION_GET_DETAILS GetDetails; + PSR_GET_PARAMETERS GetSrParameters; + PSR_SET_PARAMETER SetSrParameter; + PSR_SET_REMOVE SetSrRemove; + PSR_SET_CLEAR SetSrClear; + PIPX_ADDRESS_DATA IpxAddressData; + PGET_PKT_SIZE GetPktSize; + PIPX_NETNUM_DATA IpxNetnumData; + } u; // BUGBUG: Make these unaligned?? + PIPX_ROUTE_ENTRY RouteEntry; + PNWLINK_ACTION NwlinkAction; + ULONG Segment; + ULONG AdapterNum; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + IPX_DEBUG (ACTION, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + IPX_DEBUG (ACTION, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + //DbgPrint("NwlinkAction->Option is (%x)\n", NwlinkAction->Option); + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MIPX_SETSENDPTYPE: + + // + // IPX_PTYPE: Data is a single byte packet type. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_SETSENDPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->DefaultPacketType = NwlinkAction->Data[0]; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_FILTERPTYPE: + + // + // IPX_FILTERPTYPE: Data is a single byte to filter on. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_FILTERPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->FilteredType = NwlinkAction->Data[0]; + AddressFile->FilterOnPacketType = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_NOFILTERPTYPE: + + // + // IPX_STOPFILTERPTYPE. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOFILTERPTYPE\n", AddressFile)); + AddressFile->FilterOnPacketType = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->ReceiveIpxHeader || AddressFile->IsSapSocket); + break; + + case MIPX_SENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (TRUE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (FALSE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ReceiveFlagsAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_SETRCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SETRCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NORCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_SENDHEADER: + + // + // IPX_RECVHDR (TRUE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDHEADER: + + // + // IPX_RECVHDR (FALSE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_RCVBCAST: + + // + // Broadcast reception enabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_RCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (!AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = TRUE; + IpxAddBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_NORCVBCAST: + + // + // Broadcast reception disabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETPKTSIZE: + + // + // IPX_MAXSIZE. + // + // BUGBUG: Figure out what the first length is for. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETPKTSIZE\n", AddressFile)); + if (DataLength >= sizeof(GET_PKT_SIZE)) { + u.GetPktSize = (PGET_PKT_SIZE)(NwlinkAction->Data); + u.GetPktSize->Unknown = 0; + u.GetPktSize->MaxDatagramSize = Device->Information.MaxDatagramSize; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = Device->SapNicCount; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM2: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM2\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = MIN (Device->MaxBindings, Device->ValidBindings); + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_GETCARDINFO: + case MIPX_GETCARDINFO2: + + // + // GETCARDINFO is IPX_ADDRESS. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETCARDINFO (%d)\n", + AddressFile, *(UNALIGNED UINT *)NwlinkAction->Data)); + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if (((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) || + ((NwlinkAction->Option == MIPX_GETCARDINFO2) && (AdapterNum <= (ULONG) MIN (Device->MaxBindings, Device->ValidBindings)))) { + +#ifdef _PNP_POWER +// Get lock + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(Device, AdapterNum); +#else + Binding = Device->Bindings[AdapterNum]; +#endif + if (Binding == NULL) { + + // + // This should be a binding in the WAN range + // of an adapter which is currently not + // allocated. We scan back to the previous + // non-NULL binding, which should be on the + // same adapter, and return a down line with + // the same characteristics as that binding. + // + + UINT i = AdapterNum; + + do { + --i; +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + } while (Binding == NULL); + + CTEAssert (Binding->Adapter->MacInfo.MediumAsync); + CTEAssert (i >= Binding->Adapter->FirstWanNicId); + CTEAssert (AdapterNum <= Binding->Adapter->LastWanNicId); + + u.IpxAddressData->status = FALSE; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + + } else { + + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Device->WanGlobalNetworkNumber)) { + + // + // In this case we make it look like one big wan + // net, so the line is "up" or "down" depending + // on whether we have given him the first indication + // or not. + // + + u.IpxAddressData->status = Device->GlobalNetworkIndicated; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Device->GlobalWanNetwork; + + } else { + + u.IpxAddressData->status = Binding->LineUp; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + } + + } + + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + + Adapter = Binding->Adapter; + u.IpxAddressData->wan = Adapter->MacInfo.MediumAsync; + u.IpxAddressData->maxpkt = + (NwlinkAction->Option == MIPX_GETCARDINFO) ? + Binding->AnnouncedMaxDatagramSize : + Binding->RealMaxDatagramSize; + u.IpxAddressData->linkspeed = Binding->MediumSpeed; +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + } else { +#if 1 + // + // Support the old format query for now. + // + + typedef struct _IPX_OLD_ADDRESS_DATA { + UINT adapternum; + UCHAR netnum[4]; + UCHAR nodenum[6]; + } IPX_OLD_ADDRESS_DATA, *PIPX_OLD_ADDRESS_DATA; + + if (DataLength >= sizeof(IPX_OLD_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if ((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, AdapterNum)) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[AdapterNum]) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_INVALID_PARAMETER; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } +#else + Status = STATUS_BUFFER_TOO_SMALL; +#endif + } + break; + + case MIPX_NOTIFYCARDINFO: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOTIFYCARDINFO (%lx)\n", AddressFile, Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + InsertTailList( + &Device->AddressNotifyQueue, + REQUEST_LINKAGE(Request) + ); + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->AddressNotifyQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_ADDRESS_NOTIFY); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_LINECHANGE: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("MIPX_LINECHANGE (%lx)\n", Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + InsertTailList( + &Device->LineChangeQueue, + REQUEST_LINKAGE(Request) + ); + + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->LineChangeQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_LINE_CHANGE); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETNETINFO_NR: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // A query on network 0 means that the caller wants + // information about our directly attached net. + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + // + // The tick count is the number of 1/18.21 second ticks + // it takes to deliver a 576-byte packet. Our link speed + // is in 100 bit-per-second units. We calculate it as + // follows (LS is the LinkSpeed): + // + // 576 bytes 8 bits 1 second 1821 ticks + // * ------ * ------------- * ---------- + // 1 byte LS * 100 bits 100 seconds + // + // which becomes 839 / LinkSpeed -- we add LinkSpeed + // to the top to round up. + // + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + +#ifdef _PNP_POWER + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + // + // To maintain the lock order: BindAccessLock > RIP table + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +#else + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + + } + + break; + + case MIPX_RERIPNETNUM: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + Segment = RipGetSegment(u.IpxNetnumData->netnum); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId]) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_GETNETINFO: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + Segment = RipGetSegment(u.IpxNetnumData->netnum); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_SENDPTYPE: + case MIPX_NOSENDPTYPE: + + // + // For the moment just use OptionsLength >= 1 to indicate + // that the send options include the packet type. + // + // BUGBUG: Do we need to worry about card num being there? + // + +#if 0 + IPX_DEBUG (ACTION, ("%lx: MIPS_%sSENDPTYPE\n", AddressFile, + NwlinkAction->Option == MIPX_SENDPTYPE ? "" : "NO")); +#endif + break; + + case MIPX_ZEROSOCKET: + + // + // Sends from this address should be from socket 0; + // This is done the simple way by just putting the + // information in the address itself, instead of + // making it per address file (this is OK since + // this call is not exposed through winsock). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ZEROSOCKET\n", AddressFile)); + AddressFile->Address->SendSourceSocket = 0; + AddressFile->Address->LocalAddress.Socket = 0; + break; + + + // + // This next batch are the source routing options. They + // are submitted by the IPXROUTE program. + // + // BUGBUG: Do we expose all binding set members to this? + + case MIPX_SRGETPARMS: + + if (DataLength >= sizeof(SR_GET_PARAMETERS)) { + u.GetSrParameters = (PSR_GET_PARAMETERS)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.GetSrParameters->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.GetSrParameters->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRDEF: + case MIPX_SRBCAST: + case MIPX_SRMULTI: + + if (DataLength >= sizeof(SR_SET_PARAMETER)) { + u.SetSrParameter = (PSR_SET_PARAMETER)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrParameter->BoardNumber+1)) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrParameter->BoardNumber+1]) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRREMOVE: + + if (DataLength >= sizeof(SR_SET_REMOVE)) { + u.SetSrRemove = (PSR_SET_REMOVE)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrRemove->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrRemove->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRCLEAR: + + if (DataLength >= sizeof(SR_SET_CLEAR)) { + u.SetSrClear = (PSR_SET_CLEAR)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrClear->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrClear->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + case MIPX_LOCALTARGET: + + // + // A request for the local target for an IPX address. + // + + if (DataLength < sizeof(ISN_ACTION_GET_LOCAL_TARGET)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)(NwlinkAction->Data); + Segment = RipGetSegment((PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See if this route is local. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + if ((RouteEntry != NULL) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + // + // This is a local net, to send to it you just use + // the appropriate NIC ID and the real MAC address. + // + + if ((RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) == 0) { + + // + // It's the virtual net, send via the first card. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, 1); +#else + u.GetLocalTarget->LocalTarget.NicId = 1; +#endif + + } else { + +#ifdef _PNP_POWER + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); + + } else { + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, RouteEntry->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[RouteEntry->NicId]; + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + u.GetLocalTarget->LocalTarget.NicId = Binding->NicId; + } else { + + u.GetLocalTarget->LocalTarget.NicId = RouteEntry->NicId; + + } +#endif + + } + + RtlCopyMemory( + u.GetLocalTarget->LocalTarget.MacAddress, + u.GetLocalTarget->IpxAddress.NodeAddress, + 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (u.GetLocalTarget->IpxAddress.NetworkAddress, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.GetLocalTarget; + InsertTailList( + &Device->Segments[Segment].WaitingLocalTarget, + REQUEST_LINKAGE(Request)); + + } + +#ifdef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } +#ifndef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + + + break; + + case MIPX_NETWORKINFO: + + // + // A request for network information about the immediate + // route to a network. + // + + if (DataLength < sizeof(ISN_ACTION_GET_NETWORK_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetNetworkInfo = (PISN_ACTION_GET_NETWORK_INFO)(NwlinkAction->Data); + + if (u.GetNetworkInfo->Network == 0) { + + // + // This is information about the local card. + // + + u.GetNetworkInfo->LinkSpeed = Device->LinkSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Device->Information.MaxDatagramSize; + + } else { + + Segment = RipGetSegment((PUCHAR)&u.GetNetworkInfo->Network); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetNetworkInfo->Network); + + if ((RouteEntry != NULL) && +#ifdef _PNP_POWER + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { +#else + (Binding = Device->Bindings[RouteEntry->NicId])) { +#endif + + // + // Our medium speed is stored in 100 bps, we + // convert to bytes/sec by multiplying by 12 + // (should really be 100/8 = 12.5). + // + + u.GetNetworkInfo->LinkSpeed = Binding->MediumSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Binding->AnnouncedMaxDatagramSize; + + } else { + + // + // Fail the call, we don't have a route yet. + // BUGBUG: This requires that a packet has been + // sent to this net already; nwrdr says this is + // OK, they will send their connect request + // before they query. On the server it should + // have RIP running so all nets should be in + // the database. + // + + Status = STATUS_BAD_NETWORK_PATH; + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + + break; + + case MIPX_CONFIG: + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(ISN_ACTION_GET_DETAILS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetDetails = (PISN_ACTION_GET_DETAILS)(NwlinkAction->Data); + + if (u.GetDetails->NicId == 0) { + + // + // This is information about the local card. We also + // tell him the total number of bindings in NicId. + // + + u.GetDetails->NetworkNumber = Device->VirtualNetworkNumber; + u.GetDetails->NicId = (USHORT)MIN (Device->MaxBindings, Device->ValidBindings); + + } else { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, u.GetDetails->NicId); +#else + Binding = Device->Bindings[u.GetDetails->NicId]; +#endif + + if ((Binding != NULL) && + (u.GetDetails->NicId <= MIN (Device->MaxBindings, Device->ValidBindings))) { + + ULONG StringLoc; +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + u.GetDetails->NetworkNumber = Binding->LocalAddress.NetworkAddress; + if (Binding->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) { + u.GetDetails->FrameType = ISN_FRAME_TYPE_ARCNET; + } else { + u.GetDetails->FrameType = Binding->FrameType; + } + u.GetDetails->BindingSet = Binding->BindingSetMember; + if (Binding->Adapter->MacInfo.MediumAsync) { + if (Binding->LineUp) { + u.GetDetails->Type = 2; + } else { + u.GetDetails->Type = 3; + } + } else { + u.GetDetails->Type = 1; + } + + RtlCopyMemory (u.GetDetails->Node, Binding->LocalMacAddress.Address, 6); + + // + // Copy the adapter name, including the final NULL. + // + + StringLoc = (Binding->Adapter->AdapterNameLength / sizeof(WCHAR)) - 2; + while (Binding->Adapter->AdapterName[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory( + u.GetDetails->AdapterName, + &Binding->Adapter->AdapterName[StringLoc+1], + Binding->Adapter->AdapterNameLength - ((StringLoc+1) * sizeof(WCHAR))); + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } else { + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Status = STATUS_INVALID_PARAMETER; + + } + } + + break; + + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + IPX_DEBUG (ACTION, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* IpxTdiAction */ + + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel an Action. + What is done to cancel it is specific to each action. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PREQUEST Request = (PREQUEST)Irp; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN Found; + UINT IOCTLType; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Find the request on the address notify queue. + // + + Found = FALSE; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (p = Device->AddressNotifyQueue.Flink; + p != &Device->AddressNotifyQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_NOTIFYCARDINFO; + break; + } + } + + if (!Found) { + for (p = Device->LineChangeQueue.Flink; + p != &Device->LineChangeQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_LINECHANGE; + break; + } + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + if (IOCTLType == MIPX_NOTIFYCARDINFO) { + IPX_DEBUG(ACTION, ("Cancelled action NOTIFYCARDINFO %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } else { + IPX_DEBUG(ACTION, ("Cancelled action LINECHANGE %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + } + + } +#if DBG + else { + IPX_DEBUG(ACTION, ("Cancelled action orphan %lx\n", Request)); + } +#endif + +} /* IpxCancelAction */ + + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ) + +/*++ + +Routine Description: + + This routine aborts any line change IRPs posted by the + control channel with the specified open context. It is + called when a control channel is being shut down. + +Arguments: + + ControlChannelContext - The context assigned to the control + channel when it was opened. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + CTELockHandle LockHandle; + LIST_ENTRY AbortList; + PLIST_ENTRY p; + PREQUEST Request; + KIRQL irql; + + + InitializeListHead (&AbortList); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Device->Lock, &LockHandle); + + p = Device->LineChangeQueue.Flink; + + while (p != &Device->LineChangeQueue) { + LARGE_INTEGER ControlChId; + + Request = LIST_ENTRY_TO_REQUEST(p); + + CCID_FROM_REQUEST(ControlChId, Request); + + p = p->Flink; + + if (ControlChId.QuadPart == ((PLARGE_INTEGER)ControlChannelContext)->QuadPart) { + RemoveEntryList (REQUEST_LINKAGE(Request)); + InsertTailList (&AbortList, REQUEST_LINKAGE(Request)); + } + } + + while (!IsListEmpty (&AbortList)) { + + p = RemoveHeadList (&AbortList); + Request = LIST_ENTRY_TO_REQUEST(p); + + IPX_DEBUG(ACTION, ("Aborting line change %lx\n", Request)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Device->Lock, &LockHandle); + } + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); +} /* IpxAbortLineChanges */ + diff --git a/private/ntos/tdi/isnp/ipx/adapter.c b/private/ntos/tdi/isnp/ipx/adapter.c new file mode 100644 index 000000000..479570e48 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/adapter.c @@ -0,0 +1,636 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + adapter.c + +Abstract: + + This module contains code which implements the ADAPTER object. + Routines are provided to reference, and dereference transport + adapter objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// These are init only until binding is really dynamic. +// +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateAdapter) +#endif +#endif _PNP_POWER + + + +VOID +IpxRefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Binding->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Binding->ReferenceCount); + +} /* IpxRefBinding */ + + +VOID +IpxDerefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Binding->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyBinding (Binding); + } + +} /* IpxDerefBinding */ + + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Adapter - Pointer to a pointer to a transport device context object. + + AdapterName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PADAPTER Adapter; +#if 0 + UINT i, j; +#endif + + Adapter = (PADAPTER)IpxAllocateMemory (sizeof(ADAPTER) + AdapterName->Length + sizeof(WCHAR), MEMORY_ADAPTER, "Adapter"); + +#ifdef _PNP_POWER + if (Adapter == NULL) { + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create adapter %lx failed\n", AdapterName)); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %lx %lx succeeded\n", Adapter, AdapterName)); +#else + if (Adapter == NULL) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName->Buffer)); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %ws succeeded\n", AdapterName->Buffer)); +#endif + + RtlZeroMemory(Adapter, sizeof(ADAPTER)); + + // + // Copy over the adapter name. + // + + Adapter->AdapterNameLength = AdapterName->Length + sizeof(WCHAR); + Adapter->AdapterName = (PWCHAR)(Adapter+1); + RtlCopyMemory( + Adapter->AdapterName, + AdapterName->Buffer, + AdapterName->Length); + Adapter->AdapterName[AdapterName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + +#if DBG + RtlCopyMemory(Adapter->Signature1, "IAD1", 4); +#endif + + Adapter->Type = IPX_ADAPTER_SIGNATURE; + Adapter->Size = sizeof(ADAPTER); + + CTEInitLock (&Adapter->Lock); + + InitializeListHead (&Adapter->RequestCompletionQueue); + + InitializeListHead (&Adapter->ReceiveBufferPoolList); + + ExInitializeSListHead (&Adapter->ReceiveBufferList); + + Adapter->Device = Device; + Adapter->DeviceLock = &Device->Lock; + IpxReferenceDevice (Device, DREF_ADAPTER); + +#if 0 + Adapter->ReceiveBufferPool.Next = NULL; + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Adapter->Bindings[i] = NULL; + } + Adapter->BindingCount = 0; + + for (i = 0; i < IDENTIFIER_TOTAL; i++) { + for (j = 0; j < SOURCE_ROUTE_HASH_SIZE; j++) { + Adapter->SourceRoutingHeads[i][j] = (PSOURCE_ROUTE)NULL; + } + } +#endif + + // + // BUGBUG: For the moment, we have to do the source + // routing operation on any type where broadcast + // may not be used for discovery -- improve this + // hopefully. + // + + Adapter->SourceRoutingEmpty[IDENTIFIER_RIP] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_IPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_SPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_NB] = TRUE; + +#ifdef _PNP_POWER + // + // Lock here? [BUGBUGZZ] + // + Adapter->ReferenceCount = 1; +#endif + + *AdapterPtr = Adapter; + + return STATUS_SUCCESS; + +} /* IpxCreateAdapter */ + + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Adapter - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ULONG Database, Hash; + PSOURCE_ROUTE Current; + ULONG ReceiveBufferPoolSize; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + PLIST_ENTRY p; + UINT i; + + IPX_DEBUG (ADAPTER, ("Destroy adapter %lx\n", Adapter)); + + // + // Free any receive buffer pools this adapter has. + // + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (Adapter->MaxReceivePacketSize * Device->InitReceiveBuffers); + + while (!IsListEmpty (&Adapter->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Adapter->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, IPX_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + IpxDeinitializeReceiveBuffer (Adapter, ReceiveBuffer, Adapter->MaxReceivePacketSize); + + } + + IPX_DEBUG (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + IpxFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } + + // + // Free all the source routing information for this adapter. + // + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + } + } + } + + IpxDereferenceDevice (Adapter->Device, DREF_ADAPTER); + IpxFreeMemory (Adapter, sizeof(ADAPTER) + Adapter->AdapterNameLength, MEMORY_ADAPTER, "Adapter"); + +} /* IpxDestroyAdapter */ + + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a binding structure. + +Arguments: + + Device - The device. + + ConfigBinding - Information about this binding. If this is + NULL then this is a WAN binding and all the relevant + information will be filled in by the caller. + + NetworkNumberIndex - The index in the frame type array for + ConfigBinding indicating which frame type this binding is for. + Not used if ConfigBinding is not provided. + + AdapterName - Used for error logging. + + BindingPtr - Returns the allocated binding structure. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PBINDING Binding; +#ifdef _PNP_POWER + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBinding; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBinding(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } + +GotBinding: + + Binding = CONTAINING_RECORD (s, BINDING, PoolLinkage); + +#else + Binding = (PBINDING)IpxAllocateMemory (sizeof(BINDING), MEMORY_ADAPTER, "Binding"); + + // + // We can't vsprintf a %ws at DPC level, so we check for + // that. Only WAN bindings will be created then. + // + + if (Binding == NULL) { +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } +#endif + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws succeeded, %lx\n", AdapterName, Binding)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN succeeded\n")); + } +#endif + + RtlZeroMemory(Binding, sizeof(BINDING)); + + // + // Initialize the reference count. + // + + Binding->ReferenceCount = 1; +#if DBG + Binding->RefTypes[BREF_BOUND] = 1; +#endif + +#if DBG + RtlCopyMemory(Binding->Signature1, "IBI1", 4); +#endif + + Binding->Type = IPX_BINDING_SIGNATURE; + Binding->Size = sizeof(BINDING); + + Binding->Device = Device; + Binding->DeviceLock = &Device->Lock; + + if (ConfigBinding != NULL) { + + ULONG Temp = ConfigBinding->NetworkNumber[NetworkNumberIndex]; + Binding->ConfiguredNetworkNumber = REORDER_ULONG (Temp); + + Binding->AutoDetect = ConfigBinding->AutoDetect[NetworkNumberIndex]; + Binding->DefaultAutoDetect = ConfigBinding->DefaultAutoDetect[NetworkNumberIndex]; + + Binding->AllRouteDirected = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_DEF]; + Binding->AllRouteBroadcast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_BC]; + Binding->AllRouteMulticast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_MC]; + + } + + Binding->ReceiveBroadcast = TRUE; +#if 0 + Binding->BindingSetMember = FALSE; + Binding->NextBinding = (PBINDING)NULL; + Binding->DialOutAsync = FALSE; +#endif + + // + // We set Binding->FrameType later, after we can map it based on the + // media type of the adapter we bind to. + // + + *BindingPtr = Binding; + + return STATUS_SUCCESS; + +} /* IpxCreateBinding */ + + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine destroys a binding structure. + +Arguments: + + Binding - Pointer to a transport binding structure. + +Return Value: + + None. + +--*/ + +{ + IPX_DEBUG (ADAPTER, ("Destroy binding %lx\n", Binding)); + +#ifdef _PNP_POWER + + IPX_PUSH_ENTRY_LIST( + &IpxDevice->BindingList, + &Binding->PoolLinkage, + &IpxDevice->SListsLock); +#else + IpxFreeMemory (Binding, sizeof(BINDING), MEMORY_ADAPTER, "Binding"); +#endif + +} /* IpxDestroyBinding */ + + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 bindings to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + UINT BindingNum; + PBINDING Binding; + CTELockHandle LockHandle; + + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + BindingPool = (PIPX_BINDING_POOL)IpxAllocateMemory (BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + if (BindingPool == NULL) { + IPX_DEBUG (PNP, ("Could not allocate binding pool memory\n")); + return; + } + + + IPX_DEBUG (PNP, ("Initializing Binding pool %lx, %d bindings\n", + BindingPool, Device->InitBindings)); + + BindingPool->BindingCount = Device->InitBindings; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BindingNum = 0; BindingNum < BindingPool->BindingCount; BindingNum++) { + + Binding = &BindingPool->Bindings[BindingNum]; + IPX_PUSH_ENTRY_LIST (&Device->BindingList, &Binding->PoolLinkage, &Device->SListsLock); + +#ifdef IPX_TRACK_POOL + Binding->Pool = BindingPool; +#endif + } + + InsertTailList (&Device->BindingPoolList, &BindingPool->Linkage); + + Device->AllocatedBindings += BindingPool->BindingCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBindingPool */ + + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a binding from the device context's pool. + If there are no bindings in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated binding. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedBindings < Device->MaxPoolBindings) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBindingPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBinding */ +#endif diff --git a/private/ntos/tdi/isnp/ipx/address.c b/private/ntos/tdi/isnp/ipx/address.c new file mode 100644 index 000000000..1049bc8de --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/address.c @@ -0,0 +1,1843 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* IpxParseTdiAddress */ + + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + IpxPrint0 ("IpxValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* IpxValidateTdiAddress */ + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + None. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * IpxAddress; + + IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + + IpxAddress->TAAddressCount = 1; + IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + IpxAddress->Address[0].Address[0].NetworkAddress = Network; + IpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(IpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + +} /* IpxBuildTdiAddress */ +#endif + + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the IPX transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED *AddressName; + USHORT Socket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // + // If we are a dedicated router, we cannot let addresses + // be opened. + // + + if (Device->DedicatedRouter) { + return STATUS_NOT_SUPPORTED; + } + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + IpxPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + // + + for (i=0;iTAAddressCount;i++) { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + Socket = ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + found = TRUE; + } + break; + + } else { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + + } + + } + + if (!found) { + IPX_DEBUG (ADDRESS, ("OpenAddress, request %lx has no IPX Address\n", Request)); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + if (Socket == 0) { + + Socket = IpxAssignSocket (Device); + + if (Socket == 0) { + IPX_DEBUG (ADDRESS, ("OpenAddress, no unique socket found\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } else { + IPX_DEBUG (ADDRESS, ("OpenAddress, assigned socket %lx\n", REORDER_USHORT(Socket))); + } + + } else { + + IPX_DEBUG (ADDRESS, ("OpenAddress, socket %lx\n", REORDER_USHORT(Socket))); + + } + + // + // get an address file structure to represent this address. + // + + AddressFile = IpxCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return status; + } + + // + // We mark this socket specially. + // + + if (Socket == SAP_SOCKET) { + AddressFile->IsSapSocket = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + + CTEGetLock (&Device->Lock, &LockHandle); + + Address = IpxLookupAddress (Device, Socket); + + if (Address == NULL) { + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // This address doesn't exist. Create it. + // registering it. + // + + Address = IpxCreateAddress ( + Device, + Socket); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (Device->State == DEVICE_STATE_STOPPING) { + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Request = Request; + AddressFile->Address = Address; + + CTEGetLock (&Address->Lock, &LockHandle); + InsertTailList (&Address->AddressFileDatabase, &AddressFile->Linkage); + CTEFreeLock (&Address->Lock, LockHandle); + + AddressFile->Request = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + IpxDestroyAddressFile (AddressFile); + + } + + } else { + + CTEFreeLock (&Device->Lock, LockHandle); + + IPX_DEBUG (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + CTEGetLock (&Address->Lock, &LockHandle); + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + AddressFile->Request = NULL; + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + IpxReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + CTEFreeLock (&Address->Lock, LockHandle); + + status = STATUS_SUCCESS; + + } + } + + // + // Remove the reference from IpxLookupAddress. + // + + IpxDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* IpxOpenAddress */ + + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + USHORT InitialSocket, CurrentSocket, AddressSocket; + ULONG CurrentHash; + BOOLEAN Conflict; + PLIST_ENTRY p; + PADDRESS Address; + CTELockHandle LockHandle; + + // + // Loop through all possible sockets, starting at + // Device->CurrentSocket, looking for a suitable one. + // Device->CurrentSocket rotates through the possible + // sockets to improve the chances of finding one + // quickly. + // + + CTEGetLock (&Device->Lock, &LockHandle); + + InitialSocket = Device->CurrentSocket; + Device->CurrentSocket = (USHORT)(Device->CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(Device->CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + Device->CurrentSocket = Device->SocketStart; + } + + CurrentSocket = InitialSocket; + + do { + + // + // Scan all addresses; if we find one with a socket + // that conflicts with this one, we can't use it. + // + // NOTE: Device->Lock is acquired here. + // + + Conflict = FALSE; + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + AddressSocket = REORDER_USHORT(Address->Socket); + + if ((AddressSocket + Device->SocketUniqueness > CurrentSocket) && + (AddressSocket < CurrentSocket + Device->SocketUniqueness)) { + Conflict = TRUE; + break; + } + } + + // + // If we've found a conflict, no need to check the other + // queues. + // + + if (Conflict) { + break; + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // We intentionally free the lock here so that we + // never spend too much time with it held. + // + + if (!Conflict) { + + // + // We went through the address list without + // finding a conflict; use this socket. + // + + return REORDER_USHORT(CurrentSocket); + } + + CurrentSocket = (USHORT)(CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + CurrentSocket = Device->SocketStart; + } + + CTEGetLock (&Device->Lock, &LockHandle); + + } while (CurrentSocket != InitialSocket); + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Could not find one to assign. + // + + return (USHORT)0; + +} /* IpxAssignSocket */ + + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + PIPX_SEND_RESERVED SendReserved; + PIPX_RECEIVE_RESERVED ReceiveReserved; + NDIS_STATUS Status; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + Address = (PADDRESS)IpxAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + IPX_DEBUG (ADDRESS, ("Create address %lx failed\n", REORDER_USHORT(Socket))); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address %lx (%lx)\n", Address, REORDER_USHORT(Socket))); + RtlZeroMemory (Address, sizeof(ADDRESS)); + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->SendPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail1; + } +#endif + + if (IpxInitializeSendPacket (Device, &Address->SendPacket, Address->SendPacketHeader) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail1: +#endif + Address->SendPacketInUse = TRUE; + } else { + SendReserved = SEND_RESERVED(&Address->SendPacket); + SendReserved->Address = Address; + SendReserved->OwnedByAddress = TRUE; + Address->SendPacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + + +#if BACK_FILL + { + PIPX_SEND_RESERVED BackFillReserved; + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->BackFillPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail2; + } +#endif + if (IpxInitializeBackFillPacket (Device, &Address->BackFillPacket, NULL) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail2: +#endif + Address->BackFillPacketInUse = TRUE; + } else { + BackFillReserved = SEND_RESERVED(&Address->BackFillPacket); + BackFillReserved->Address = Address; + Address->BackFillPacketInUse = FALSE; + BackFillReserved->OwnedByAddress = TRUE; +#ifdef IPX_TRACK_POOL + BackFillReserved->Pool = NULL; +#endif + } + } +#endif + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleReceivePacket(Device, &Address->ReceivePacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail3; + } +#endif + if (IpxInitializeReceivePacket (Device, &Address->ReceivePacket) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail3: +#endif + Address->ReceivePacketInUse = TRUE; + } else { + ReceiveReserved = RECEIVE_RESERVED(&Address->ReceivePacket); + ReceiveReserved->Address = Address; + ReceiveReserved->OwnedByAddress = TRUE; + Address->ReceivePacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + ReceiveReserved->Pool = NULL; +#endif + } + + Address->Type = IPX_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + Address->Socket = Socket; + Address->SendSourceSocket = Socket; + + // + // Save our local address for building datagrams quickly. + // + + RtlCopyMemory (&Address->LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + Address->LocalAddress.Socket = Socket; + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + InsertTailList (&Device->AddressDatabases[IPX_HASH_SOCKET(Socket)], &Address->Linkage); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* IpxCreateAddress */ + + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == IPX_ADDRESS_SIGNATURE) ) { + + CTEGetLock (&Address->Lock, &LockHandle); + + if (!Address->Stopping) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->Lock, LockHandle); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + IpxPrint1("IpxVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + IpxPrint1("IpxVerifyAddressFile: AF %lx exception\n", Address); + return GetExceptionCode(); + } + + return status; + +} /* IpxVerifyAddressFile */ + + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by IpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by IpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + IPX_DEBUG (ADDRESS, ("Destroy address %lx (%lx)\n", Address, REORDER_USHORT(Address->Socket))); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Address->Linkage); + CTEFreeLock (&Device->Lock, LockHandle); + + if (!Address->SendPacketInUse) { + IpxDeinitializeSendPacket (Device, &Address->SendPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->SendPacket); +#endif + } + + if (!Address->ReceivePacketInUse) { + IpxDeinitializeReceivePacket (Device, &Address->ReceivePacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleReceivePacket (Device, Address->ReceivePacket); +#endif + } + +#if BACK_FILL + if (!Address->BackFillPacketInUse) { + IpxDeinitializeBackFillPacket (Device, &Address->BackFillPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->BackFillPacket); +#endif + } +#endif + IpxFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + IpxDereferenceDevice (Device, DREF_ADDRESS); + +} /* IpxDestroyAddress */ + + +#if DBG +VOID +IpxRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddress */ + + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + // ++Address->ReferenceCount; + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddressLock */ +#endif + + +VOID +IpxDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddress */ + + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. This routine can + only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddressSync */ + + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + + CTEGetLock (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)IpxAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + IPX_DEBUG (ADDRESS, ("Create address file failed\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = IPX_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + + CTEFreeLock (&Device->Lock, LockHandle); + +#if 0 + AddressFile->SpecialReceiveProcessing = FALSE; + AddressFile->ExtendedAddressing = FALSE; + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->FilterOnPacketType = FALSE; + AddressFile->DefaultPacketType = 0; + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif +#endif + + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + AddressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + + // + // [CH] Added these handlers for chained buffer receives + // + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + AddressFile->ChainedReceiveDatagramHandler = TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + + AddressFile->RegisteredErrorHandler = FALSE; + AddressFile->ErrorHandler = TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + + return AddressFile; + +} /* IpxCreateAddressFile */ + + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by IpxDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + + IPX_DEBUG (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + CTEGetLock (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + CTEGetLock (&Device->Lock, &LockHandle1); + Address->Stopping = TRUE; + if (Device->LastAddress == Address) { + Device->LastAddress = NULL; + } + CTEFreeLock (&Device->Lock, LockHandle1); + + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + IpxFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + IpxCompleteRequest (CloseRequest); + IpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* IpxDestroyAddressFile */ + + +#if DBG +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFile */ + + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + //++AddressFile->ReferenceCount; + (VOID)InterlockedIncrement(&AddressFile->ReferenceCount); + +} /* IpxRefAddressFileLock */ + + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFileSync */ + + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFile */ + + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. This routine + can only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFileSync */ +#endif + + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + ULONG Hash = IPX_HASH_SOCKET (Socket); + + p = Device->AddressDatabases[Hash].Flink; + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->Stopping) { + continue; + } + + if (Address->Socket == Socket) { + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + IpxReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } + + } + + // + // The specified address was not found. + // + + return NULL; + +} /* IpxLookupAddress */ + + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + CTELockHandle LockHandle; + PREQUEST Request; + PADDRESS Address = AddressFile->Address; + PLIST_ENTRY p; + KIRQL irql; + + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Address->Lock, &LockHandle); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + CTEFreeLock (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + return STATUS_SUCCESS; + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!(IsListEmpty(&AddressFile->ReceiveDatagramQueue))) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Address->Lock, &LockHandle); + + } + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + +} /* IpxStopAddressFile */ + + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + // + // If this address file had broadcasts enabled, turn it off. + // + + CTEGetLock (&Device->Lock, &LockHandle); + if (AddressFile->EnableBroadcast) { + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + CTEFreeLock (&Device->Lock, LockHandle); + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* IpxCloseAddressFile */ + + diff --git a/private/ntos/tdi/isnp/ipx/config.c b/private/ntos/tdi/isnp/ipx/config.c new file mode 100644 index 000000000..f5d8aefbf --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/config.c @@ -0,0 +1,1715 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN IPX module. + +Revision History: + + Sanjay Anand (SanjayAn) 19-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxGetConfiguration) +#pragma alloc_text(INIT,IpxFreeConfiguration) + +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxGetConfigValue) +#pragma alloc_text(INIT,IpxGetBindingValue) +#pragma alloc_text(INIT,IpxGetFrameType) +#pragma alloc_text(INIT,IpxWriteDefaultAutoDetectType) +#endif + +#pragma alloc_text(INIT,IpxAddBind) +#pragma alloc_text(INIT,IpxAddExport) +#pragma alloc_text(INIT,IpxReadLinkageInformation) +#endif + + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of IPX's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ + +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Ten = 10; + ULONG Fifteen = 15; + ULONG Fifty = 50; + ULONG DefaultSocketStart = 0x4000; + ULONG DefaultSocketEnd = 0x8000; + ULONG RipSegments = RIP_SEGMENTS; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"DedicatedRouter", &Zero } , + { L"InitDatagrams", &Ten } , + { L"MaxDatagrams", &Fifty } , + { L"RipAgeTime", &Five } , // minutes + { L"RipCount", &Five } , + { L"RipTimeout", &One } , // half-second + { L"RipUsageTime", &Fifteen } , // minutes + { L"SourceRouteUsageTime", &Ten } , // minutes + { L"SocketUniqueness", &Eight } , + { L"SocketStart", &DefaultSocketStart } , + { L"SocketEnd", &DefaultSocketEnd } , + { L"VirtualNetworkNumber", &Zero } , + { L"MaxMemoryUsage", &Zero } , + { L"RipTableSize", &RipSegments } , + { L"VirtualNetworkOptional", &One } , + { L"EthernetPadToEven", &One } , + { L"EthernetExtraPadding", &Zero } , + { L"SingleNetworkActive", &Zero } , + { L"DisableDialoutSap", &Zero } , + { L"DisableDialinNetbios", &One } , + { L"VerifySourceAddress", &One } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = IpxAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + InitializeListHead (&Config->BindingList); + Config->DriverObject = DriverObject; + + // + // Read in the NDIS binding information. + // + // IpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)IpxAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + IpxFreeConfiguration(Config); + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + // + // Determine what name to export and who to bind to. + // + + Status = IpxReadLinkageInformation (Config); + if (Status != STATUS_SUCCESS) { + + // + // It logged an error if it failed. + // + + IpxFreeConfiguration(Config); + return Status; + } + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-14) Call IpxGetConfigValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = IpxGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 15) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxFreeConfiguration(Config); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // For PnP, we need to keep this path around + // +#ifndef _PNP_POWER + IpxFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); +#endif _PNP_POWER + + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* IpxGetConfiguration */ + + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get free any storage that was allocated + by IpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PBINDING_CONFIG Binding; + + while (!IsListEmpty (&Config->BindingList)) { + p = RemoveHeadList (&Config->BindingList); + Binding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + IpxFreeMemory (Binding->AdapterName.Buffer, Binding->AdapterName.MaximumLength, MEMORY_CONFIG, "NameBuffer"); + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + } + + if (Config->DeviceName.Buffer) { + IpxFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + IpxFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* IpxFreeConfiguration */ + + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 904, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Config->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetConfigValue */ + + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the NetConfig\DriverNN + node to set the per-binding values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - The index in Binding->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Binding parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Binding->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetBindingValue */ + + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues. + It is called for each of the entry in the "PktType" and + "NetworkNumber" multi-strings for a given binding. + +Arguments: + + ValueName - The name of the value ("PktType" or "NetworkNumber" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - A pointer to a count of multi-string entries. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + ULONG IntegerValue; + PWCHAR Cur; + PULONG Count = (PULONG)EntryContext; + + if ((ValueType != REG_SZ) || + (*Count >= 4)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IntegerValue = 0; + for (Cur = (PWCHAR)(ValueData); ; Cur++) { + if (*Cur >= L'0' && *Cur <= L'9') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'0'); + } else if (*Cur >= L'A' && *Cur <= L'F') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'A' + 10); + } else if (*Cur >= L'a' && *Cur <= L'f') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'a' + 10); + } else { + break; + } + } + + if (((PWCHAR)ValueName)[0] == L'P') { + + // + // PktType. We map arcnet to 802_3 so the code around + // here can assume there are only four packets type -- + // the frame type is ignored later for arcnet. + // + + if ((IntegerValue > ISN_FRAME_TYPE_ARCNET) && + (IntegerValue != ISN_FRAME_TYPE_AUTO)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("PktType(%d) is %lx\n", *Count, IntegerValue)); + if (IntegerValue == ISN_FRAME_TYPE_ARCNET) { + Binding->FrameType[*Count] = ISN_FRAME_TYPE_802_3; + } else { + Binding->FrameType[*Count] = IntegerValue; + } + + } else { + + // + // NetworkNumber + // + + IPX_DEBUG (CONFIG, ("NetworkNumber(%d) is %d\n", *Count, IntegerValue)); + Binding->NetworkNumber[*Count] = IntegerValue; + + } + + ++(*Count); + + return STATUS_SUCCESS; + +} /* IpxGetFrameType */ + + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. It + also queries the per-binding information and stores it. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PBINDING_CONFIG Binding; + PULONG CurBindNum = ((PULONG)EntryContext); + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + PWSTR ValueDataWstr = (PWSTR)ValueData; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + + Binding = (PBINDING_CONFIG)IpxAllocateMemory (sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + if (Binding == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(BINDING_CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "NameBuffer"); + if (NameBuffer == NULL) { + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Binding->AdapterName.Buffer = NameBuffer; + Binding->AdapterName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Binding->AdapterName.MaximumLength = (USHORT)ValueLength; + + Binding->DriverObject = Config->DriverObject; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + + InsertTailList (&Config->BindingList, &Binding->Linkage); + ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + + StringLoc = (ValueLength / sizeof(WCHAR)) - 2; + while (ValueDataWstr[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &ValueDataWstr[StringLoc+1], ValueLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", ValueData, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if (Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; + +} /* IpxAddBind */ + + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + IPX_DEBUG (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* IpxAddExport */ + + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call IpxAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = IpxAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 901, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + +#ifndef _PNP_POWER +// +// This will be done as and when adapters appear. +// + // + // 1) Change to call IpxAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = IpxAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&Config->BindCount; + QueryTable[1].DefaultType = REG_NONE; + + Config->BindCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + // + // For the moment fail if we find no bindings -- eventually when + // we support dynamic binding we should stick around in this case. + // + + if ((Status != STATUS_SUCCESS) || (Config->BindCount == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 902, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } +#endif + return STATUS_SUCCESS; + +} /* IpxReadLinkageInformation */ + + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ) + +/*++ + +Routine Description: + + This routine is called when we were unable to detect the default + auto-detect type and instead found a different one. We update + the "DefaultAutoDetectType" in the registry. + +Arguments: + + RegistryPath - The name of IPX's node in the registry. + + Adapter - The adapter which we auto-detected on. + + FrameType - The new auto-detected value. + +Return Value: + + None. + +--*/ + +{ + PWSTR FullRegistryPath; + PUCHAR CurRegistryPath; + ULONG FullRegistryPathLength; + ULONG AdapterNameLength; + WCHAR NetConfigName[] = L"\\NetConfig"; + static PWCHAR FrameTypeNames[4] = { L"Ethernet II", L"802.3", L"802.2", L"SNAP" }; + PWCHAR CurAdapterName; + NTSTATUS Status; + + + // + // We need to allocate a buffer which contains the registry path, + // followed by "NetConfig", followed by the adapter name, and + // then NULL-terminated. + // + + CurAdapterName = &Adapter->AdapterName[(Adapter->AdapterNameLength/sizeof(WCHAR))-2]; + while (*CurAdapterName != L'\\') { + --CurAdapterName; + } + CurAdapterName; + AdapterNameLength = Adapter->AdapterNameLength - ((CurAdapterName - Adapter->AdapterName) * sizeof(WCHAR)) - sizeof(WCHAR); + + FullRegistryPathLength = RegistryPath->Length + sizeof(NetConfigName) + AdapterNameLength; + + FullRegistryPath = (PWSTR)IpxAllocateMemory (FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + if (FullRegistryPath == NULL) { + IpxWriteResourceErrorLog( + IpxDevice->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + FullRegistryPathLength, + MEMORY_CONFIG); + return; + } + + CurRegistryPath = (PUCHAR)FullRegistryPath; + RtlCopyMemory (CurRegistryPath, RegistryPath->Buffer, RegistryPath->Length); + CurRegistryPath += RegistryPath->Length; + RtlCopyMemory (CurRegistryPath, NetConfigName, sizeof(NetConfigName) - sizeof(WCHAR)); + CurRegistryPath += (sizeof(NetConfigName) - sizeof(WCHAR)); + RtlCopyMemory (CurRegistryPath, CurAdapterName, AdapterNameLength); + CurRegistryPath += AdapterNameLength; + *(PWCHAR)CurRegistryPath = L'\0'; + + Status = RtlWriteRegistryValue( + RTL_REGISTRY_ABSOLUTE, + FullRegistryPath, + L"DefaultAutoDetectType", + REG_DWORD, + &FrameType, + sizeof(ULONG)); + + IpxFreeMemory (FullRegistryPath, FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + + IpxWriteGeneralErrorLog( + IpxDevice->DeviceObject, + EVENT_IPX_NEW_DEFAULT_TYPE, + 888, + STATUS_SUCCESS, + FrameTypeNames[FrameType], + 0, + NULL); + +} /* IpxWriteDefaultAutoDetectType */ + + +#ifdef _PNP_POWER +// +// Vnet# and VnetOptional +// +#define VIRTUAL_NETWORK_PARAMETERS 2 + +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read the virtual network number + from the registry. This is called on appearance/disappearance of an + adapter from the system. We read the registry, starting at RegistryPath, + to get the value of the VirtualNetworkNumber parameter. If it doesn't + exist, we use the default set in ipxcnfg.h file. + Adapted from IpxGetConfiguration(). + +Arguments: + + Config - Contians the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ + +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[VIRTUAL_NETWORK_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[VIRTUAL_NETWORK_PARAMETERS] = { + { L"VirtualNetworkNumber", &Zero } , + { L"VirtualNetworkOptional", &One } }; + UINT i; + + // + // Read the virtual net number from the parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2) Call IpxGetConfigValue for the virtual net number key + // + + QueryTable[1].QueryRoutine = IpxGetConfigValue; + QueryTable[1].Flags = 0; + QueryTable[1].Name = ParameterValues[0].KeyName; + QueryTable[1].EntryContext = (PVOID)CONFIG_VIRTUAL_NETWORK; + QueryTable[1].DefaultType = REG_DWORD; + QueryTable[1].DefaultData = (PVOID)(ParameterValues[0].DefaultValue); + QueryTable[1].DefaultLength = sizeof(ULONG); + + // + // 2) Call IpxGetConfigValue for the virtual net optional key + // + + QueryTable[2].QueryRoutine = IpxGetConfigValue; + QueryTable[2].Flags = 0; + QueryTable[2].Name = ParameterValues[1].KeyName; + QueryTable[2].EntryContext = (PVOID)CONFIG_VIRTUAL_OPTIONAL; + QueryTable[2].DefaultType = REG_DWORD; + QueryTable[2].DefaultData = (PVOID)(ParameterValues[1].DefaultValue); + QueryTable[2].DefaultLength = sizeof(ULONG); + + // + // 15) Stop + // + + QueryTable[3].QueryRoutine = NULL; + QueryTable[3].Flags = 0; + QueryTable[3].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* IpxPnPGetNetworkNumber */ + + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ) +/*++ + +Routine Description: + + This routine is called by IPX to read the adapter-specific parameters + from the registry on PnP appearance of an adapter in the system. + We read the registry, starting at RegistryPath\NetConfig\DeviceName. + + Adapted from IpxAddBind(). + +Arguments: + + Config - Config structure - supplies the DeviceObject and RegistryPathBuffer. + + DeviceName - name of the adapter that was added. + + Binding - Returns the configuration information per adapter. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + +// InsertTailList (&Config->BindingList, &Binding->Linkage); +// ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + StringLoc = (DeviceName->MaximumLength / sizeof(WCHAR)) - 2; + while (DeviceName->Buffer[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &DeviceName->Buffer[StringLoc+1], DeviceName->MaximumLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", DeviceName->Buffer, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if ((Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO)) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + // + // Fixed this loop which can spill over if the FrameTypeCount is 4 and the SlideCount > 0. + // Here, the FrameTypeCount is 1-based, whereas the indices are 0-based, we need to make + // the index 1-based for this to work. So, instead of (3-Binding->FrameTypeCount), we use + // (4-Binding->FrameTypeCount). This loop copies all the non-auto-detect frametypes down to + // the bottom of the array to make space after the last auto-detect frame-type for filling + // in the frametypes in the preference order. + // +#if 0 + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } +#else + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(4-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(4-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(4-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(4-Binding->FrameTypeCount)]; + } +#endif + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; +} /* IpxPnPGetAdapterParameters */ + +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isnp/ipx/config.h b/private/ntos/tdi/isnp/ipx/config.h new file mode 100644 index 000000000..ba6e76d83 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/config.h @@ -0,0 +1,132 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN IPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_DEDICATED_ROUTER 0 +#define CONFIG_INIT_DATAGRAMS 1 +#define CONFIG_MAX_DATAGRAMS 2 +#define CONFIG_RIP_AGE_TIME 3 +#define CONFIG_RIP_COUNT 4 +#define CONFIG_RIP_TIMEOUT 5 +#define CONFIG_RIP_USAGE_TIME 6 +#define CONFIG_ROUTE_USAGE_TIME 7 +#define CONFIG_SOCKET_UNIQUENESS 8 +#define CONFIG_SOCKET_START 9 +#define CONFIG_SOCKET_END 10 +#define CONFIG_VIRTUAL_NETWORK 11 +#define CONFIG_MAX_MEMORY_USAGE 12 +#define CONFIG_RIP_TABLE_SIZE 13 +#define CONFIG_VIRTUAL_OPTIONAL 14 +#define CONFIG_ETHERNET_PAD 15 +#define CONFIG_ETHERNET_LENGTH 16 +#define CONFIG_SINGLE_NETWORK 17 +#define CONFIG_DISABLE_DIALOUT_SAP 18 +#define CONFIG_DISABLE_DIALIN_NB 19 +#define CONFIG_VERIFY_SOURCE_ADDRESS 20 + +#define CONFIG_PARAMETERS 21 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + PWSTR RegistryPathBuffer; // path to config info + ULONG BindCount; // entries in BindingList + LIST_ENTRY BindingList; // one per binding + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +// +// These are used to index into the Parameters array in BINDING_CONFIG. +// + +#define BINDING_MAX_PKT_SIZE 0 +#define BINDING_BIND_SAP 1 +#define BINDING_DEFAULT_AUTO_DETECT 2 +#define BINDING_SOURCE_ROUTE 3 +#define BINDING_ALL_ROUTE_DEF 4 +#define BINDING_ALL_ROUTE_BC 5 +#define BINDING_ALL_ROUTE_MC 6 +#define BINDING_ENABLE_FUNC_ADDR 7 +#define BINDING_ENABLE_WAN 8 + +#define BINDING_PARAMETERS 9 + + +// +// One of these is allocated per adapter we are to bind to. +// + +typedef struct _BINDING_CONFIG { + + LIST_ENTRY Linkage; // for chaining on BindingList + NDIS_STRING AdapterName; // NDIS adapter to bind to + ULONG FrameTypeCount; // number of frame types defined (max. 4) + // == number of valid entries in arrays: + ULONG FrameType[ISN_FRAME_TYPE_MAX]; // ISN_FRAME_TYPE_XXX + ULONG NetworkNumber[ISN_FRAME_TYPE_MAX]; // may be 0 + BOOLEAN AutoDetect[ISN_FRAME_TYPE_MAX]; // remove if net number can't be found + BOOLEAN DefaultAutoDetect[ISN_FRAME_TYPE_MAX]; // use this if multiple or none found + ULONG Parameters[BINDING_PARAMETERS]; // index defined above + PDRIVER_OBJECT DriverObject; // used for logging errors + +} BINDING_CONFIG, * PBINDING_CONFIG; + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ); + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ); + +#ifdef _PNP_POWER +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ); + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ); +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/ipx/device.c b/private/ntos/tdi/isnp/ipx/device.c new file mode 100644 index 000000000..f2d9d19f4 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/device.c @@ -0,0 +1,599 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateDevice) +#endif + + + +VOID +IpxRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Device->ReferenceCount); + +} /* IpxRefDevice */ + + +VOID +IpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyDevice (Device); + } + +} /* IpxDerefDevice */ + + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + SegmentCount - The number of segments in the RIP router table. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG LocksOffset; + ULONG SegmentsOffset; + ULONG DeviceNameOffset; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) + + (sizeof(CTELock) * SegmentCount) + + (sizeof(ROUTER_SEGMENT) * SegmentCount) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + IPX_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject->DeviceExtension; + + IPX_DEBUG(DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + Device->DeviceObject = deviceObject; + + LocksOffset = sizeof(DEVICE); + SegmentsOffset = LocksOffset + (sizeof(CTELock) * SegmentCount); + DeviceNameOffset = SegmentsOffset + (sizeof(ROUTER_SEGMENT) * SegmentCount); + + // + // Set some internal pointers. + // + + Device->SegmentLocks = (CTELock *)(((PUCHAR)Device) + LocksOffset); + Device->Segments = (PROUTER_SEGMENT)(((PUCHAR)Device) + SegmentsOffset); + Device->SegmentCount = SegmentCount; + + for (i = 0; i < SegmentCount; i++) { + + CTEInitLock (&Device->SegmentLocks[i]); + InitializeListHead (&Device->Segments[i].WaitingForRoute); + InitializeListHead (&Device->Segments[i].FindWaitingForRoute); + InitializeListHead (&Device->Segments[i].WaitingLocalTarget); + InitializeListHead (&Device->Segments[i].WaitingReripNetnum); + InitializeListHead (&Device->Segments[i].Entries); + Device->Segments[i].EnumerateLocation = &Device->Segments[i].Entries; + + } + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "IDC1", 4); + RtlCopyMemory(Device->Signature2, "IDC2", 4); +#endif + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 0; // no sends allowed + Device->Information.MaxConnectionUserData = 0; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTIONLESS_MODE | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_ROUTE_DIRECTED; + Device->Information.MinimumLookaheadData = 128; + Device->Information.NumberOfResources = IPX_TDI_RESOURCES; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + +#if 0 + // + // These will be filled in after all the binding is done. + // + + Device->Information.MaxDatagramSize = 0; + Device->Information.MaximumLookaheadData = 0; + + + Device->SourceRoutingUsed = FALSE; + Device->SourceRoutingTime = 0; + Device->RipPacketCount = 0; + + Device->RipShortTimerActive = FALSE; + Device->RipSendTime = 0; +#endif + + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + +#ifdef _PNP_POWER + // + // Init the resource that guards the binding array/indices + // + CTEInitLock (&Device->BindAccessLock); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingRipPackets); + CTEInitTimer (&Device->RipShortTimer); + CTEInitTimer (&Device->RipLongTimer); + + CTEInitTimer (&Device->SourceRoutingTimer); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock); + CTEInitLock (&Device->Lock); + CTEInitLock (&Device->SListsLock); + + Device->ControlChannelIdentifier.QuadPart = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); +#if BACK_FILL + InitializeListHead (&Device->GlobalBackFillPacketList); +#endif + + InitializeListHead (&Device->AddressNotifyQueue); + InitializeListHead (&Device->LineChangeQueue); + + for (i = 0; i < IPX_ADDRESS_HASH_COUNT; i++) { + InitializeListHead (&Device->AddressDatabases[i]); + } + +#if BACK_FILL + InitializeListHead (&Device->BackFillPoolList); +#endif + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + +#ifdef _PNP_POWER + InitializeListHead (&Device->BindingPoolList); +#endif + + ExInitializeSListHead (&Device->SendPacketList); + ExInitializeSListHead (&Device->ReceivePacketList); +#if BACK_FILL + ExInitializeSListHead (&Device->BackFillPacketList); +#endif + +#ifdef _PNP_POWER + ExInitializeSListHead (&Device->BindingList); +#endif + +#if 0 + Device->MemoryUsage = 0; + Device->SendPacketList.Next = NULL; + Device->ReceivePacketList.Next = NULL; + Device->Bindings = NULL; + Device->BindingCount = 0; +#endif + + KeQuerySystemTime (&Device->IpxStartTime); + + Device->State = DEVICE_STATE_CLOSED; + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + Device->Type = IPX_DEVICE_SIGNATURE; + Device->Size = sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* IpxCreateDevice */ + + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PIPX_SEND_POOL SendPool; + PIPX_SEND_PACKET SendPacket; + PIPX_RECEIVE_POOL ReceivePool; + PIPX_RECEIVE_PACKET ReceivePacket; + PIPX_ROUTE_ENTRY RouteEntry; + UINT SendPoolSize; + UINT ReceivePoolSize; + UINT i; +#if BACK_FILL + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + PIPX_SEND_PACKET BackFillPacket; +#endif + +#ifdef _PNP_POWER + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + PBINDING Binding; +#endif + + IPX_DEBUG (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the packets out of its pools. + // + +#if _PNP_POWER + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + while (!IsListEmpty (&Device->BindingPoolList)) { + + p = RemoveHeadList (&Device->BindingPoolList); + BindingPool = CONTAINING_RECORD (p, IPX_BINDING_POOL, Linkage); + IPX_DEBUG (PACKET, ("Free binding pool %lx\n", BindingPool)); + IpxFreeMemory (BindingPool, BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + } +#endif + +#ifdef IPX_OWN_PACKETS + +#if BACK_FILL + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < BackFillPool->PacketCount; i++) { + BackFillPacket = &BackFillPool->Packets[i]; + IpxDeinitializeBackFillPacket (Device, BackFillPacket); + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + IpxFreeMemory (BackFillPool, BackFillPoolSize, MEMORY_PACKET, "BackPool"); + } +#endif + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (((IPX_MAXIMUM_MAC + sizeof(IPX_HEADER) + 3) & ~3) * Device->InitDatagrams); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + IpxDeinitializeSendPacket (Device, SendPacket); + + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + IpxFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + IpxDeinitializeReceivePacket (Device, ReceivePacket); + + } + + IPX_DEBUG (PACKET, ("Free receive packet pool %lx\n", ReceivePool)); + IpxFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } +#else + +#if BACK_FILL + + while (s = IPX_POP_ENTRY_LIST(&Device->BackFillPacketList, &Device->Lock)) { + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET BackFillPacket; + + BackFillPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeBackFillPacket (Device, &BackFillPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + NdisFreePacketPool (BackFillPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (BackFillPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + } +#endif + + while (s = IPX_POP_ENTRY_LIST(&Device->SendPacketList, &Device->Lock)){ + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET SendPacket; + PUCHAR Header = Reserved->Header; + + SendPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeSendPacket (Device, &SendPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + NdisFreePacketPool (SendPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (SendPool->Header, PACKET_HEADER_SIZE * Device->InitDatagrams, MEMORY_PACKET, "SendPool"); + + IpxFreeMemory (SendPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + } + + while (s = IPX_POP_ENTRY_LIST(&Device->ReceivePacketList, &Device->Lock)){ + PIPX_RECEIVE_RESERVED Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + IPX_RECEIVE_PACKET ReceivePacket; + + ReceivePacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeReceivePacket (Device, &ReceivePacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED)); + } + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", ReceivePool)); + NdisFreePacketPool (ReceivePool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (ReceivePool, sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + } + +#endif IPX_OWN_PACKETS + // + // Destroy all rip table entries. + // + + for (i = 0; i < Device->SegmentCount; i++) { + + RouteEntry = RipGetFirstRoute(i); + while (RouteEntry != NULL) { + + (VOID)RipDeleteRoute(i, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + RouteEntry = RipGetNextRoute(i); + + } + + } + + IPX_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (IpxMemoryTag[i].BytesAllocated != 0) { + IPX_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, IpxMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice (Device->DeviceObject); + } + +} /* IpxDestroyDevice */ + diff --git a/private/ntos/tdi/isnp/ipx/dirs b/private/ntos/tdi/isnp/ipx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/ipx/driver.c b/private/ntos/tdi/isnp/ipx/driver.c new file mode 100644 index 000000000..7c32cd0e3 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/driver.c @@ -0,0 +1,4219 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 18-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include +#include +#include + + +PDEVICE IpxDevice = NULL; +PIPX_PADDING_BUFFER IpxPaddingBuffer = NULL; + +#if DBG + +UCHAR IpxTempDebugBuffer[150]; +ULONG IpxDebug = 0x0; +ULONG IpxMemoryDebug = 0xffffffd3; +UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +PUCHAR IpxDebugMemoryLoc = IpxDebugMemory[0]; +PUCHAR IpxDebugMemoryEnd = IpxDebugMemory[IPX_MEMORY_LOG_SIZE]; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (IpxTempDebugBuffer, 150); + ArgLen = vsprintf(IpxTempDebugBuffer, FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (IpxDebugMemoryLoc, 64); + RtlCopyMemory( IpxDebugMemoryLoc, IpxTempDebugBuffer, ArgLen ); + + IpxDebugMemoryLoc += 64; + if (IpxDebugMemoryLoc >= IpxDebugMemoryEnd) { + IpxDebugMemoryLoc = IpxDebugMemory[0]; + } + } +} + + +DEFINE_LOCK_STRUCTURE(IpxMemoryInterlock); +MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +DEFINE_LOCK_STRUCTURE(IpxGlobalInterlock); + +#endif + +#if DBG + +// +// Use for debug printouts +// + +PUCHAR FrameTypeNames[5] = { "Ethernet II", "802.3", "802.2", "SNAP", "Arcnet" }; +#define OutputFrameType(_Binding) \ + (((_Binding)->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) ? \ + FrameTypeNames[4] : \ + FrameTypeNames[(_Binding)->FrameType]) +#endif + + +#ifdef IPX_PACKET_LOG + +ULONG IpxPacketLogDebug = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_SEND_OTHER; +USHORT IpxPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(IpxPacketLogLock); +IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc = IpxPacketLog; +PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd = &IpxPacketLog[IPX_PACKET_LOG_LENGTH]; + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PIPX_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&IpxPacketLogLock, &LockHandle); + + PacketLog = IpxPacketLogLoc; + + ++IpxPacketLogLoc; + if (IpxPacketLogLoc >= IpxPacketLogEnd) { + IpxPacketLogLoc = IpxPacketLog; + } + *(UNALIGNED ULONG *)IpxPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&IpxPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(IPX_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, Length); + } else { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* IpxLogPacket */ + +#endif // IPX_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +// +// This is now shared with other modules +// +#ifndef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); +#endif _PNP_POWER + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) + +// +// These routines can be called at any time in case of PnP. +// +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxResolveAutoDetect) +#pragma alloc_text(INIT,IpxResolveBindingSets) +#pragma alloc_text(INIT,IpxBindToAdapter) +#endif + +#endif + +UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + +ULONG IpxFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the IPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of IPX's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UINT SuccessfulOpens, ValidBindings; +#ifdef _PNP_POWER + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("NWLNKIPX"); +#else + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("IPX Transport"); +#endif + PDEVICE Device; + PBINDING Binding; + PADAPTER Adapter; + ULONG BindingCount, BindingIndex; + PBINDING * BindingArray; + PLIST_ENTRY p; + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG Temp; + UINT i; + BOOLEAN CountedWan; + + PCONFIG Config = NULL; + PBINDING_CONFIG ConfigBinding; + +#if 0 + DbgPrint ("IPX: FailLoad at %lx\n", &IpxFailLoad); + DbgBreakPoint(); + + if (IpxFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + + // + // This ordering matters because we use it to quickly + // determine if packets are internally generated or not. + // + + CTEAssert (IDENTIFIER_NB < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_SPX < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP_INTERNAL > IDENTIFIER_IPX); + + // + // We assume that this structure is not packet in between + // the fields. + // + + CTEAssert (FIELD_OFFSET (TDI_ADDRESS_IPX, Socket) + sizeof(USHORT) == 12); + + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + + IPX_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&IpxGlobalInterlock); + CTEInitLock (&IpxMemoryInterlock); + for (i = 0; i < MEMORY_MAX; i++) { + IpxMemoryTag[i].Tag = i; + IpxMemoryTag[i].BytesAllocated = 0; + } +#endif +#ifdef IPX_PACKET_LOG + CTEInitLock (&IpxPacketLogLock); +#endif + +#ifdef IPX_OWN_PACKETS + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + IPX_DEBUG (DEVICE, ("IPX loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = IpxGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed, it logged an error. + // + + PANIC (" Failed to initialize transport, IPX initialization failed.\n"); + return status; + + } + +#ifdef _PNP_POWER + // + // Initialize the TDI layer. + // + TdiInitialize(); +#endif + + // + // make ourselves known to the NDIS wrapper. + // + + status = IpxRegisterProtocol ((PNDIS_STRING)&ProtocolName); + + if (!NT_SUCCESS (status)) { + + IpxFreeConfiguration(Config); + PANIC ("IpxInitialize: RegisterProtocol failed!\n"); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return status; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = IpxDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = IpxDispatchDeviceControl; + + DriverObject->DriverUnload = IpxUnload; + + SuccessfulOpens = 0; + + status = IpxCreateDevice( + DriverObject, + &Config->DeviceName, + Config->Parameters[CONFIG_RIP_TABLE_SIZE], + &Device); + + if (!NT_SUCCESS (status)) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return status; + } + + IpxDevice = Device; + + + // + // Save the relevant configuration parameters. + // + + Device->DedicatedRouter = (BOOLEAN)(Config->Parameters[CONFIG_DEDICATED_ROUTER] != 0); + Device->InitDatagrams = Config->Parameters[CONFIG_INIT_DATAGRAMS]; + Device->MaxDatagrams = Config->Parameters[CONFIG_MAX_DATAGRAMS]; + Device->RipAgeTime = Config->Parameters[CONFIG_RIP_AGE_TIME]; + Device->RipCount = Config->Parameters[CONFIG_RIP_COUNT]; + Device->RipTimeout = + ((Config->Parameters[CONFIG_RIP_TIMEOUT] * 500) + (RIP_GRANULARITY/2)) / + RIP_GRANULARITY; + Device->RipUsageTime = Config->Parameters[CONFIG_RIP_USAGE_TIME]; + Device->SourceRouteUsageTime = Config->Parameters[CONFIG_ROUTE_USAGE_TIME]; + Device->SocketUniqueness = Config->Parameters[CONFIG_SOCKET_UNIQUENESS]; + Device->SocketStart = (USHORT)Config->Parameters[CONFIG_SOCKET_START]; + Device->SocketEnd = (USHORT)Config->Parameters[CONFIG_SOCKET_END]; + Device->MemoryLimit = Config->Parameters[CONFIG_MAX_MEMORY_USAGE]; + Device->VerifySourceAddress = (BOOLEAN)(Config->Parameters[CONFIG_VERIFY_SOURCE_ADDRESS] != 0); + + Device->InitReceivePackets = (Device->InitDatagrams + 1) / 2; + Device->InitReceiveBuffers = (Device->InitDatagrams + 1) / 2; + + Device->MaxReceivePackets = 10; // BUGBUG: config this? + Device->MaxReceiveBuffers = 10; + +#ifdef _PNP_POWER + Device->InitBindings = 5; // BUGBUG: config this? + + // + // RAS max is 240 (?) + 10 max LAN + // + Device->MaxPoolBindings = 250; // BUGBUG: config this? +#endif + + // + // Have to reverse this. + // +#ifndef _PNP_POWER +// +// Look at this only when the first adapter appears. +// + Temp = Config->Parameters[CONFIG_VIRTUAL_NETWORK]; + Device->VirtualNetworkNumber = REORDER_ULONG (Temp); +#endif + + Device->VirtualNetworkOptional = (BOOLEAN)(Config->Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + Device->CurrentSocket = Device->SocketStart; + + Device->EthernetPadToEven = (BOOLEAN)(Config->Parameters[CONFIG_ETHERNET_PAD] != 0); + Device->EthernetExtraPadding = (Config->Parameters[CONFIG_ETHERNET_LENGTH] & 0xfffffffe) + 1; + + Device->SingleNetworkActive = (BOOLEAN)(Config->Parameters[CONFIG_SINGLE_NETWORK] != 0); + Device->DisableDialoutSap = (BOOLEAN)(Config->Parameters[CONFIG_DISABLE_DIALOUT_SAP] != 0); + Device->DisableDialinNetbios = (UCHAR)(Config->Parameters[CONFIG_DISABLE_DIALIN_NB]); + +#ifdef _PNP_POWER +// +// Used later to access the registry. +// + Device->RegistryPathBuffer = Config->RegistryPathBuffer; + Device->RegistryPath.Length = RegistryPath->Length; + Device->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + Device->RegistryPath.Buffer = Device->RegistryPathBuffer; +#endif _PNP_POWER + + // + // ActiveNetworkWan will start as FALSE, which is correct. + // + + // + // Allocate our initial packet pool. We do not allocate + // receive and receive buffer pools until we need them, + // because in many cases we never do. + // + +#if BACK_FILL + IpxAllocateBackFillPool (Device); +#endif + + IpxAllocateSendPool (Device); + +#ifdef _PNP_POWER + IpxAllocateBindingPool (Device); +#endif + + // + // Allocate one 1-byte buffer for odd length packets. + // + + IpxPaddingBuffer = IpxAllocatePaddingBuffer(Device); + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + 801, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the loopback structures + // + IpxInitLoopback(); + +// +// All this will be done on appearance of adapters. +// + +#ifndef _PNP_POWER + + // + // Bind to all the configured adapters. + // + + InitializeListHead (&Device->InitialBindingList); + + p = Config->BindingList.Flink; + + while (p != &Config->BindingList) { + + ConfigBinding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + p = p->Flink; + + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. + // + + status = IpxBindToAdapter (Device, ConfigBinding, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (status == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + if (status != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding->AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + ++SuccessfulOpens; + + } + + } + + + IpxFreeConfiguration(Config); + + if (SuccessfulOpens == 0) { + + IpxDereferenceDevice (Device, DREF_CREATE); + + } else { + + IPX_DEFINE_SYNC_CONTEXT (SyncContext); + + // + // Allocate the device binding array and transfer those + // on the list to it. First count up the bindings. + // + + BindingCount = 0; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + Adapter->FirstWanNicId = (USHORT)(BindingCount+1); + Adapter->LastWanNicId = (USHORT)(BindingCount + Adapter->WanNicIdCount); + BindingCount += Adapter->WanNicIdCount; + } else { + ++BindingCount; + } + } + + BindingArray = (PBINDING *)IpxAllocateMemory ((BindingCount+1) * sizeof(BINDING), MEMORY_BINDING, "Binding array"); + + if (BindingArray == NULL) { + + while (!IsListEmpty (&Device->InitialBindingList)) { + p = RemoveHeadList (&Device->InitialBindingList); + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + IpxDestroyBinding (Binding); + } + + IpxDereferenceDevice (Device, DREF_CREATE); + SuccessfulOpens = 0; + goto InitFailed; + } + + RtlZeroMemory (BindingArray, (BindingCount+1) * sizeof(BINDING)); + + // + // Now walk the list transferring bindings to the array. + // + + BindingIndex = 1; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + ) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + p = p->Flink; // we overwrite the linkage in here, so save it. + + BindingArray[BindingIndex] = Binding; + Binding->NicId = (USHORT)BindingIndex; + + if (Binding->ConfiguredNetworkNumber != 0) { + + // + // If the configured network number is non-zero, then + // use it, unless we are unable to insert a rip table + // entry for it (duplicates are OK because they will + // become binding set members -- BUGBUG: What if the + // duplicate is a different media or frame type, then + // it won't get noted as a binding set). + // + + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // These are a union with the InitialLinkage fields. + // + + Binding->NextBinding = NULL; + Binding->CurrentSendBinding = NULL; + + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + CTEAssert (Adapter->FirstWanNicId == BindingIndex); + BindingIndex += Adapter->WanNicIdCount; + } else { + ++BindingIndex; + } + } + + CTEAssert (BindingIndex == BindingCount+1); + + Device->Bindings = BindingArray; + Device->BindingCount = BindingCount; + + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + status = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (status == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + for (i = 1; i <= Device->BindingCount; i++) { + + Binding = Device->Bindings[i]; + + // + // Skip empty WAN slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + status = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((status != STATUS_SUCCESS) && + (status != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + } + + ValidBindings = Device->BindingCount; + + if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, RegistryPath); + + } + + Device->ValidBindings = ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This sets Device->HighestExternalNicId + // and Device->HighestType20NicId. + // + + IpxResolveBindingSets (Device, ValidBindings); + + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // Success; see if there is a virtual network configured. + // + + if (Device->VirtualNetworkNumber != 0) { + + status = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + Device->Bindings[1]->Adapter->NdisBindingHandle, + 1); + + if (status != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (status == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + + // + // This will get set to FALSE if RIP binds. + // + + Device->RipResponder = TRUE; + + } else { + +NoVirtualNetwork: + + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + RtlCopyMemory(&Device->SourceAddress, &Device->Bindings[1]->LocalAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + } + + + // + // Now get SapNicCount -- regular adapters are counted + // as one, but all the WAN lines together only count for one. + // We also calculate FirstLanNicId and FirstWanNicId here. + // + + CountedWan = FALSE; + Device->SapNicCount = 0; + + Device->FirstLanNicId = (USHORT)-1; + Device->FirstWanNicId = (USHORT)-1; + + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + + if (Device->Bindings[i]) { + + if (Device->Bindings[i]->Adapter->MacInfo.MediumAsync) { + + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = i; + } + + if (CountedWan) { + continue; + } else { + CountedWan = TRUE; + } + + } else { + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = i; + } + + } + + } else { + + // + // NULL bindings are WANs and are not the first one, + // so don't count them. + // + + CTEAssert (Device->FirstWanNicId != -1); + CTEAssert (CountedWan); + continue; + } + + ++Device->SapNicCount; + + } + } + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = 1; + } + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = 1; + } + + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = Device->Bindings[1]->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = Device->Bindings[1]->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = Device->Bindings[1]->RealMaxDatagramSize; // smallest binding value + + if (Device->Bindings[1]->LineUp) { + LinkSpeed = Device->Bindings[1]->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = Device->Bindings[1]->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= ValidBindings; i++) { + + Binding = Device->Bindings[i]; + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = Device->Bindings[1]->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + Device->State = DEVICE_STATE_OPEN; + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + } + +InitFailed: + + if (SuccessfulOpens == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + return STATUS_DEVICE_DOES_NOT_EXIST; + + } else { + + return STATUS_SUCCESS; + } + +#else // _PNP_POWER +{ + PBIND_ARRAY_ELEM BindingArray; + PTA_ADDRESS TdiRegistrationAddress; + + // + // Pre-allocate the binding array + // Later, we will allocate the LAN/WAN and SLAVE bindings separately + // [BUGBUGZZ] Read the array size from registry? + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + Device->MaxBindings = MAX_BINDINGS; + + // + // Allocate the TA_ADDRESS structure - this will be used in all TdiRegisterNetAddress + // notifications. + // + TdiRegistrationAddress = (PTA_ADDRESS)IpxAllocateMemory ( + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + if (TdiRegistrationAddress == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxFreeMemory(BindingArray, sizeof(BindingArray), MEMORY_BINDING, "Binding Array"); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + RtlZeroMemory (BindingArray, MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM)); + RtlZeroMemory (TdiRegistrationAddress, 2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)); + + Device->Bindings = BindingArray; + + TdiRegistrationAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + TdiRegistrationAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + + // + // Store the pointer in the Device. + // + Device->TdiRegistrationAddress = TdiRegistrationAddress; + + // + // Device state is loaded, but not opened. It is opened when at least + // one adapter has appeared. + // + Device->State = DEVICE_STATE_LOADED; + + Device->FirstLanNicId = Device->FirstWanNicId = (USHORT)1; // will be changed later + + IpxFreeConfiguration(Config); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + return STATUS_SUCCESS; +} +#endif // _PNP_POWER +} /* DriverEntry */ + + +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, +#ifdef _PNP_POWER + IN CTELockHandle *LockHandle1, +#endif + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is called for auto-detect bindings to + remove any bindings that were not successfully found. + It also updates "DefaultAutoDetectType" in the registry + if needed. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + + RegistryPath - The path to the ipx registry, used if we have + to write a value back. + +Return Value: + + The updated number of bindings. + +--*/ + +{ + PBINDING Binding, TmpBinding; + UINT i, j; + + // + // Get rid of any auto-detect devices which we + // could not find nets for. We also remove any + // devices which are not the first ones + // auto-detected on a particular adapter. + // + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + if (!Binding) { + continue; + } + + // + // If this was auto-detected and was not the default, + // or it was the default, but nothing was detected for + // it *and* something else *was* detected (which means + // we will use that frame type when we get to it), + // we may need to remove this binding. + // + + if (Binding->AutoDetect && + (!Binding->DefaultAutoDetect || + (Binding->DefaultAutoDetect && + (Binding->LocalAddress.NetworkAddress == 0) && + Binding->Adapter->AutoDetectResponse))) { + + if ((Binding->LocalAddress.NetworkAddress == 0) || + (Binding->Adapter->AutoDetectFound)) { + + // + // Remove this binding. + // + + if (Binding->LocalAddress.NetworkAddress == 0) { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) no net found\n", + i, Binding->FrameType)); + } else { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) adapter already auto-detected\n", + i, Binding->FrameType)); + } + + CTEAssert (Binding->NicId == i); + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + // + // Remove any routes through this NIC, and + // adjust any NIC ID's above this one in the + // database down by one. + // + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + Binding->Adapter->Bindings[Binding->FrameType] = NULL; + for (j = i+1; j <= ValidBindings; j++) { +#ifndef _PNP_POWER + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#else + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#endif _PNP_POWER + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, NULL); +#else + Device->Bindings[ValidBindings] = NULL; +#endif + --Binding->Adapter->BindingCount; + --ValidBindings; + + --i; // so we check the binding that was just moved. + + // + // Wait 100 ms before freeing the binding, + // in case an indication is using it. + // + + KeStallExecutionProcessor(100000); + + IpxDestroyBinding (Binding); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detected OK\n", + i, Binding->FrameType)); + +#if DBG + DbgPrint ("IPX: Auto-detected non-default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + + // + // Save it in the registry for the next boot. + // +#ifdef _PNP_POWER +// +// This cannot be done at DPC, so, drop the IRQL +// + IPX_FREE_LOCK1(&Device->BindAccessLock, *LockHandle1); + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); + IPX_GET_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + } + + } else { + + if (Binding->AutoDetect) { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detect default\n", + i, Binding->FrameType)); + +#if DBG + if (Binding->LocalAddress.NetworkAddress != 0) { + DbgPrint ("IPX: Auto-detected default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); + } else { + DbgPrint ("IPX: Using default auto-detect frame type %s\n", + OutputFrameType(Binding)); + } +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) not auto-detected\n", + i, Binding->FrameType)); + } + + } + + } + + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (Binding = Device->Bindings[i]) { +#endif + CTEAssert (Binding->NicId == i); + IPX_DEBUG (AUTO_DETECT, ("Binding %lx, type %d, auto %d\n", + Binding, Binding->FrameType, Binding->AutoDetect)); + } + + } + + return ValidBindings; + +} /* IpxResolveAutoDetect */ + + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ) + +/*++ + +Routine Description: + + This routine is called to determine if we have any + binding sets and rearrange the bindings the way we + like. The order is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding, MasterBinding, TmpBinding; + UINT i, j; + ULONG WanCount, DuplicateCount; + + // + // First loop through and push all the wan bindings + // to the end. + // +#ifdef _PNP_POWER + + WanCount = Device->HighestExternalNicId - Device->HighestLanNicId; + +#else + + WanCount = 0; + + // + // For PnP, we dont do this as the bindings are in order + // at the time of insertion + // + for (i = 1; i <= (ValidBindings-WanCount); ) { + + Binding = Device->Bindings[i]; + + if ((Binding == NULL) || Binding->Adapter->MacInfo.MediumAsync) { + + // + // Put this binding at the end, and slide all the + // others down. If it is a NULL WAN binding then we + // don't have to do some of this. + // + +#if DBG + // + // Any non-NULL bindings should be correct in this + // respect at any point. + // + + if (Binding != NULL) { + CTEAssert (Binding->NicId == i); + } +#endif + + // + // If the Binding is NULL we won't have anything in the + // database at this binding, but we still need to adjust + // any NIC ID's in the database which are above this. + // + + RipAdjustForBindingChange ((USHORT)i, (USHORT)ValidBindings, IpxBindingMoved); + + // + // Slide the bindings above this down. + // + + for (j = i+1; j <= ValidBindings; j++) { + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } + + // + // Put this binding at the end. + // + + Device->Bindings[ValidBindings] = Binding; + if (Binding != NULL) { + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Binding->Adapter->FirstWanNicId == Binding->NicId)) { + Binding->Adapter->FirstWanNicId = (USHORT)ValidBindings; + Binding->Adapter->LastWanNicId += (USHORT)(ValidBindings - Binding->NicId); + } + Binding->NicId = (USHORT)ValidBindings; + } + ++WanCount; + + // + // Keep i the same, to check the new binding at + // this position. + // + + } else { + + i++; + + } + + } +#endif _PNP_POWER + // + // Now go through and find the LAN duplicates and + // create binding sets from them. + // + + DuplicateCount = 0; + + for (i = 1; i <= (ValidBindings-(WanCount+DuplicateCount)); ) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + CTEAssert (Binding != NULL); // because we are only looking at LAN bindings + + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + if (Binding->LocalAddress.NetworkAddress == 0) { + i++; + continue; + } + + // + // See if any previous bindings match the + // frame type, medium type, and number of + // this network (for the moment we match on + // frame type and medium type too so that we + // don't have to worry about different frame + // formats and header offsets within a set). + // + + for (j = 1; j < i; j++) { +#ifdef _PNP_POWER + MasterBinding = NIC_ID_TO_BINDING(Device, j); +#else + MasterBinding = Device->Bindings[j]; +#endif + if ((MasterBinding->LocalAddress.NetworkAddress == Binding->LocalAddress.NetworkAddress) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + break; + } + + } + + if (j == i) { + i++; + continue; + } + + // + // We have a duplicate. First slide it down to the + // end. Note that we change any router entries that + // use our real NicId to use the real NicId of the + // master (there should be no entries in the rip + // database that have the NicId of a binding slave). + // + + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + + for (j = i+1; j <= ValidBindings; j++) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#else + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#endif + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, Binding); +#else + Device->Bindings[ValidBindings] = Binding; +#endif + + Binding->NicId = (USHORT)ValidBindings; + ++DuplicateCount; + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %lx is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Keep i the same, to check the new binding at + // this position. + // + + } +#ifndef _PNP_POWER + Device->HighestExternalNicId = (USHORT)(ValidBindings - DuplicateCount); + Device->HighestType20NicId = (USHORT)(ValidBindings-(WanCount+DuplicateCount)); +#else + Device->HighestLanNicId -= (USHORT)DuplicateCount; + + if (Device->HighestLanNicId == 0) { + CTEAssert(FALSE); + } + + Device->HighestExternalNicId -= (USHORT)DuplicateCount; + Device->HighestType20NicId -= (USHORT)DuplicateCount; + Device->SapNicCount -= (USHORT)DuplicateCount; +#endif _PNP_POWER +} /* IpxResolveBindingSets */ + + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding, +#ifdef _PNP_POWER + IN PADAPTER *AdapterPtr, +#endif + IN ULONG FrameTypeIndex + ) + +/*++ + +Routine Description: + + This routine handles binding the transport to a new + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Device - The IPX device object. + + ConfigBinding - The configuration info for this binding. + + AdapterPtr - pointer to the adapter to bind to in case of PnP. + + FrameTypeIndex - The index into ConfigBinding's array of frame + types for this adapter. The routine is called once for + every valid frame type. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + +#ifndef _PNP_POWER + // + // Adapter came in as a parameter + // + PADAPTER Adapter = NULL; +#else + PADAPTER Adapter = *AdapterPtr; +#endif + + PBINDING Binding, OldBinding; + ULONG FrameType, MappedFrameType; + PLIST_ENTRY p; + + // + // We can't bind more than one adapter unless we have a + // virtual network configured or we are allowed to run + // with a virtual network of 0. + // + + if (Device->BindingCount == 1) { + if ((Device->VirtualNetworkNumber == 0) && + (!Device->VirtualNetworkOptional)) { + + IPX_DEBUG (ADAPTER, ("Cannot bind to more than one adapter\n")); + DbgPrint ("IPX: Disallowing multiple bind ==> VirtualNetwork is 0\n"); + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 666, + STATUS_NOT_SUPPORTED, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + return STATUS_NOT_SUPPORTED; + } + } + + + // + // First allocate the memory for the binding. + // + + status = IpxCreateBinding( + Device, + ConfigBinding, + FrameTypeIndex, + ConfigBinding->AdapterName.Buffer, + &Binding); + + if (status != STATUS_SUCCESS) { + return status; + } + + FrameType = ConfigBinding->FrameType[FrameTypeIndex]; + +// +// In PnP case, we dont need to check for existing adapters since +// we supply a NULL adapter in the parameters if it needs to be created +// +#ifndef _PNP_POWER + + // + // Check if there is already an NDIS binding to this adapter, + // and if so, that there is not already a binding with this + // frame type. + // + + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + OldBinding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + if (RtlEqualMemory( + OldBinding->Adapter->AdapterName, + ConfigBinding->AdapterName.Buffer, + OldBinding->Adapter->AdapterNameLength)) { + + Adapter = OldBinding->Adapter; + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + break; + + } + } +#endif _PNP_POWER + + if (Adapter == NULL) { + + // + // No binding to this adapter exists, so create a + // new one. + // + + status = IpxCreateAdapter( + Device, + &ConfigBinding->AdapterName, + &Adapter); + + if (status != STATUS_SUCCESS) { + IpxDestroyBinding(Binding); + return status; + } + + // + // Save these now (they will be the same for all bindings + // on this adapter). + // + + Adapter->ConfigMaxPacketSize = ConfigBinding->Parameters[BINDING_MAX_PKT_SIZE]; + Adapter->SourceRouting = (BOOLEAN)ConfigBinding->Parameters[BINDING_SOURCE_ROUTE]; + Adapter->EnableFunctionalAddress = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_FUNC_ADDR]; + Adapter->EnableWanRouter = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_WAN]; + + Adapter->BindSap = (USHORT)ConfigBinding->Parameters[BINDING_BIND_SAP]; + Adapter->BindSapNetworkOrder = REORDER_USHORT(Adapter->BindSap); + CTEAssert (Adapter->BindSap == 0x8137); + CTEAssert (Adapter->BindSapNetworkOrder == 0x3781); + + // + // Now fire up NDIS so this adapter talks + // + + status = IpxInitializeNdis( + Adapter, + ConfigBinding); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + IpxDestroyAdapter (Adapter); + IpxDestroyBinding (Binding); + + // + // Returning this status informs the caller to not + // try any more frame types on this adapter. + // + + return STATUS_DEVICE_DOES_NOT_EXIST; + + } + + // + // For 802.5 bindings we need to start the source routing + // timer to time out old entries. + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->SourceRouting)) { + + if (!Device->SourceRoutingUsed) { + + Device->SourceRoutingUsed = TRUE; + IpxReferenceDevice (Device, DREF_SR_TIMER); + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + } + } + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + IPX_DEBUG (ADAPTER, ("Create new bind to adapter %ws, type %d\n", + ConfigBinding->AdapterName.Buffer, + MappedFrameType)); + + IpxAllocateReceiveBufferPool (Adapter); + +#ifdef _PNP_POWER + *AdapterPtr = Adapter; +#endif + } +#ifdef _PNP_POWER + else { + // + // get the mapped frame type + // + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + } +#endif _PNP_POWER + + // + // The local node address starts out the same as the + // MAC address of the adapter (on WAN this will change). + // The local MAC address can also change for WAN. + // + + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Adapter->LocalMacAddress.Address, 6); + RtlCopyMemory (Binding->LocalMacAddress.Address, Adapter->LocalMacAddress.Address, 6); + + + // + // Save the send handler. + // + + Binding->SendFrameHandler = NULL; + Binding->FrameType = MappedFrameType; + + // + // BUGBUG: Put this in InitializeBindingInfo. + // + + switch (Adapter->MacInfo.RealMediumType) { + case NdisMedium802_3: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrame802_3802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_3802_2; break; + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrame802_3EthernetII; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_3Snap; break; + } + break; + case NdisMedium802_5: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_5802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_5Snap; break; + } + break; + case NdisMediumFddi: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameFddi802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrameFddi802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrameFddiSnap; break; + } + break; + case NdisMediumArcnet878_2: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameArcnet878_2; break; + } + break; + case NdisMediumWan: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrameWanEthernetII; break; + } + break; + } + + if (Binding->SendFrameHandler == NULL) { + DbgPrint ("BUGBUG!: SendFrameHandler is NULL\n"); + } + + Adapter->Bindings[MappedFrameType] = Binding; + ++Adapter->BindingCount; + + Binding->Adapter = Adapter; + +#ifndef _PNP_POWER + InsertTailList (&Device->InitialBindingList, &Binding->InitialLinkage); +#endif _PNP_POWER + + // + // NicId and ExternalNicId will be filled in later when the binding + // is assigned a spot in the Device->Bindings array. + // + + // + // Initialize the per-binding MAC information + // + + if ((Adapter->ConfigMaxPacketSize == 0) || + (Adapter->MaxSendPacketSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = Adapter->MaxSendPacketSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + Binding->MediumSpeed = Adapter->MediumSpeed; + if (Adapter->MacInfo.MediumAsync) { + Binding->LineUp = FALSE; + } else { + Binding->LineUp = TRUE; + } + + MacInitializeBindingInfo( + Binding, + Adapter); + + return STATUS_SUCCESS; + +} /* IpxBindToAdapter */ + + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ) + +/*++ + +Routine Description: + + This routine returns TRUE if the specified SourceAddress indicates + the packet was sent by us, and FALSE otherwise. + +Arguments: + + SourceAddress - The source IPX address. + +Return Value: + + TRUE if the address is local. + +--*/ + +{ + PBINDING Binding; + UINT i; + + // + // First see if it is a virtual network address or not. + // + + if (RtlEqualMemory (VirtualNode, SourceAddress->NodeAddress, 6)) { + + // + // This is us if we have a virtual network configured. + // If we don't have a virtual node, we fall through to the + // other check -- an arcnet card configured as node 1 will + // have what we think of as the "virtual node" as its + // real node address. + // + + if ((IpxDevice->VirtualNetwork) && + (IpxDevice->VirtualNetworkNumber == SourceAddress->NetworkAddress)) { + return TRUE; + } + + } + + // + // Check through our list of adapters to see if one of + // them is the source node. + // + { + ULONG Index = MIN (IpxDevice->MaxBindings, IpxDevice->ValidBindings); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + if (((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) && +#else + if (((Binding = IpxDevice->Bindings[i]) != NULL) && +#endif _PNP_POWER + (RtlEqualMemory (Binding->LocalAddress.NodeAddress, SourceAddress->NodeAddress, 6))) { + return TRUE; + } + } + } + + return FALSE; + +} /* IpxIsAddressLocal */ + + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine handles unbinding the transport from an + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Binding - The adapter to unbind. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + PADAPTER Adapter = Binding->Adapter; + + Adapter->Bindings[Binding->FrameType] = NULL; + --Adapter->BindingCount; + + IpxDereferenceBinding (Binding, BREF_BOUND); + + if (Adapter->BindingCount == 0) { + + // + // DereferenceAdapter is a NULL macro for load-only. + // + // BUGBUG: Revisit Post 4.0 + // +#ifdef _PNP_LATER + // + // Take away the creation reference. When the in-use ref is taken off, + // we destroy this adapter. + // + IpxDereferenceAdapter(Adapter); +#else + // + // Free the packet pools, etc. and close the + // adapter. + // + + IpxCloseNdis (Adapter); + + IpxDestroyAdapter (Adapter); +#endif + } + + return STATUS_SUCCESS; + +} /* IpxUnBindFromAdapter */ + + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has IPX open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + + + UNREFERENCED_PARAMETER (DriverObject); + + IpxDevice->State = DEVICE_STATE_STOPPING; + + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + + // + // Walk the list of device contexts. + // + + for (i = 1; i <= IpxDevice->BindingCount; i++) { +#ifdef _PNP_POWER + if ((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) { + INSERT_BINDING(IpxDevice, i, NULL); +#else + if (IpxDevice->Bindings[i] != NULL) { + Binding = IpxDevice->Bindings[i]; + IpxDevice->Bindings[i] = NULL; +#endif _PNP_POWER + + IpxUnBindFromAdapter (Binding); + + } + + } + +#ifdef _PNP_POWER + + IpxFreeMemory ( IpxDevice->Bindings, + IpxDevice->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + // + // Deallocate the TdiRegistrationAddress and RegistryPathBuffer. + // + IpxFreeMemory ( IpxDevice->TdiRegistrationAddress, + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + IpxFreeMemory ( IpxDevice->RegistryPathBuffer, + IpxDevice->RegistryPath.Length + sizeof(WCHAR), + MEMORY_CONFIG, + "RegistryPathBuffer"); + +#endif + + KeResetEvent( + &IpxDevice->UnloadEvent + ); + IpxDevice->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + IpxDereferenceDevice (IpxDevice, DREF_CREATE); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Now free the padding buffer. + // + + IpxFreePaddingBuffer (IpxDevice); + + // + // Now do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&IpxDevice->AddressResource); + IoDeleteDevice (IpxDevice->DeviceObject); + + // + // Finally, remove ourselves as an NDIS protocol. + // + + IpxDeregisterProtocol(); + +} /* IpxUnload */ + + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPX device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = IpxDevice; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PREQUEST Request; + UINT i; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + +#ifdef _PNP_POWER + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#else + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = IpxOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = STATUS_NOT_SUPPORTED; + break; + } + + } else { + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // LowPart is in the OPEN_CONTEXT directly. + // HighPart goes into the upper 2 bytes of the OPEN_TYPE. + // + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier.LowPart); + + (ULONG)(REQUEST_OPEN_TYPE(Request)) = (Device->ControlChannelIdentifier.HighPart << 16); + (ULONG)(REQUEST_OPEN_TYPE(Request)) |= IPX_FILE_TYPE_CONTROL; + + ++(Device->ControlChannelIdentifier.QuadPart); + + if (Device->ControlChannelIdentifier.QuadPart > MAX_CCID) { + Device->ControlChannelIdentifier.QuadPart = 1; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile->Address + // which is removed by IpxCloseAddressFile. + // + + Status = IpxVerifyAddressFile(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = IpxCloseAddressFile (Device, Request); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + IPX_DEBUG (DEVICE, ("CCID: (%d, %d)\n", ControlChannelId.HighPart, ControlChannelId.LowPart)); + + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + if (Device->UpperDriverControlChannel[i].QuadPart == + ControlChannelId.QuadPart) { + Status = IpxInternalUnbind (Device, i); + break; + } + } + + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // Check for any line change IRPs submitted by this + // address. + // + + IpxAbortLineChanges ((PVOID)&ControlChannelId); + + Status = STATUS_SUCCESS; + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* IpxDispatchOpenClose */ + +#define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED ) + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + static NDIS_STRING SpxServiceName = NDIS_STRING_CONST ("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NwlnkSpx"); + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TDI_QUERY_DIRECT_SENDDG_HANDLER: { + + PULONG EntryPoint; + + // + // This is the LanmanServer trying to get the send + // entry point. + // + + IPX_DEBUG (BIND, ("Direct send entry point being returned\n")); + + EntryPoint = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + *EntryPoint = (ULONG)IpxTdiSendDatagram; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + break; + } + + case IOCTL_IPX_INTERNAL_BIND: + + // + // This is a client trying to bind. + // + + CTEAssert ((IOCTL_IPX_INTERNAL_BIND & 0x3) == METHOD_BUFFERED); + CTEAssert (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + +#ifdef _PNP_POWER + + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { +#else + if (Device->State != DEVICE_STATE_OPEN) { +#endif + Status = STATUS_INVALID_DEVICE_STATE; + + } else { + + Status = IpxInternalBind (Device, Irp); + + } + + CTEAssert (Status != STATUS_PENDING); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + case IOCTL_IPX_LOAD_SPX: + + // + // The SPX helper dll is asking us to load SPX. + // + + Status = ZwLoadDriver (&SpxServiceName); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = IpxDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + return Status; + +} /* IpxDispatchDeviceControl */ + + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PREQUEST Request; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + if (Device->State == DEVICE_STATE_OPEN) { + + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); +#if DBG + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; +#endif + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (REQUEST_MINOR_FUNCTION(Request)) { + + case TDI_SEND_DATAGRAM: + Status = IpxTdiSendDatagram (DeviceObject, Request); + break; + + case TDI_ACTION: + Status = IpxTdiAction (Device, Request); + break; + + case TDI_QUERY_INFORMATION: + Status = IpxTdiQueryInformation (Device, Request); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = IpxTdiReceiveDatagram (Request); + break; + + case TDI_SET_EVENT_HANDLER: + Status = IpxTdiSetEventHandler (Request); + break; + + case TDI_SET_INFORMATION: + Status = IpxTdiSetInformation (Device, Request); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Return the immediate status code to the caller. + // + + if (Status == STATUS_PENDING) { + + return STATUS_PENDING; + + } else { + + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + return Status; + } + + } else { + + // + // The device was not open. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + +} /* IpxDispatchInternal */ + + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = IpxDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + IpxPrint1 ("IPX: Could not allocate %d: limit\n", BytesNeeded); + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' XPI'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + IpxPrint1("IPX: Could not allocate %d: no pool\n", BytesNeeded); + if (ChargeDevice) { + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; +} /* IpxpAllocateMemory */ + + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* IpxpFreeMemory */ + +#if DBG + + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = IpxpAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &IpxMemoryInterlock); + } + + return Memory; + +} /* IpxpAllocateTaggedMemory */ + + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &IpxMemoryInterlock); + + IpxpFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* IpxpFreeTaggedMemory */ + +#endif + + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry which has + a %3 value that needs to be converted to a string. It is currently + used for EVENT_TRANSPORT_RESOURCE_POOL and EVENT_IPX_INTERNAL_NET_ + INVALID. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - The transport event code. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated -- will be put in the dump data. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet and converted for use as the %3 string. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + PDEVICE Device = IpxDevice; + static WCHAR UniqueErrorBuffer[9] = L"00000000"; + UINT CurrentDigit; + INT i; + + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + i = 8; + do { + CurrentDigit = TempUniqueError & 0xf; + TempUniqueError >>= 4; + i--; + if (CurrentDigit >= 0xa) { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } while (TempUniqueError); + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR)); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer + i, sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR))); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteResourceErrorLog */ + + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceObject - Pointer to the system device object, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR DriverName[9] = L"NwlnkIpx"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceObject->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceObject->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteGeneralErrorLog */ + + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteOidErrorLog */ + + +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + Updates datagram sizes, lookahead sizes, etc. in the Device as a result + of a new binding coming in. + +Arguments: + + Device - The IPX device object. + +Return Value: + + None. + +--*/ +{ + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + IPX_GET_LOCK(&Device->BindAccessLock, &LockHandle); + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->RealMaxDatagramSize; // smallest binding value + + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->LineUp) { + LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= Device->ValidBindings; i++) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, i); + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + IPX_FREE_LOCK(&Device->BindAccessLock, LockHandle); +} + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine is called to update the binding array to + add the new bindings that appeared in this PnP event. + The order of bindings in the array is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + This routine inserts the bindings while maintaining this + order by resolving binding sets. + + The bindings are also inserted into the RIP database. + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + + Updates the SapNicCount, Device->FirstLanNicId and Device->FirstWanNicId + +Arguments: + + Device - The IPX device object. + + Adapter - The adapter added in this PnP event + + ValidBindings - the number of bindings valid for this adapter (if LAN) + +Return Value: + + None. + +--*/ +{ + ULONG i, j; + PBINDING Binding, MasterBinding; + NTSTATUS status; + + // + // Insert in proper place; if WAN, after all the WAN bindings + // If LAN, check for binding sets and insert in proper place + // Also, insert into the Rip Tables. + // + + // + // Go thru' the bindings for this adapter, inserting into the + // binding array in place + // + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + ULONG MappedFrameType; + + // + // Store in the preference order. + // Map the frame types since we could have a case where the user selects a FrameType (say, EthernetII on FDDI) + // which maps to a different FrameType (802.2). Then we would fail to find the binding in the adapter array; + // we could potentialy add a binding twice (if two frame types map to the same Frame, then we would go to the + // mapped one twice). This is taken care of by purging dups from the ConfigBinding->FrameType array when we + // create the bindings off of the Adapter (see call to IpxBindToAdapter). + // + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + ConfigBinding->FrameType[i], + &MappedFrameType); + + Binding = Adapter->Bindings[MappedFrameType]; + + if (!Binding){ + continue; + } + + CTEAssert(Binding->FrameType == MappedFrameType); + + if (Adapter->MacInfo.MediumAsync) { + // + // WAN: Place after the HighestExternalNicId, with space for WanLine # of bindings. + // Update the First/LastWanNicId. + // + Adapter->FirstWanNicId = (USHORT)Device->HighestExternalNicId+1; + Adapter->LastWanNicId = (USHORT)(Device->HighestExternalNicId + Adapter->WanNicIdCount); + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+Adapter->WanNicIdCount >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, Adapter->WanNicIdCount); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // Move Slaves down by WanNicIdCount# of entries + // + for (j = Device->ValidBindings; j > Device->HighestExternalNicId; j--) { + INSERT_BINDING(Device, j+Adapter->WanNicIdCount, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)->NicId += (USHORT)Adapter->WanNicIdCount; + } + } + + // + // Insert the WAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestExternalNicId+1, Binding); + SET_VERSION(Device, Device->HighestExternalNicId+1); + + Binding->NicId = (USHORT)Device->HighestExternalNicId+1; + + // + // Update the indices + // + Device->HighestExternalNicId += (USHORT)Adapter->WanNicIdCount; + Device->ValidBindings += (USHORT)Adapter->WanNicIdCount; + Device->BindingCount += (USHORT)Adapter->WanNicIdCount; + Device->SapNicCount++; + + // + // Since we initialize FirstWanNicId to 1, we need to compare against that. + // In case of no LAN bindings, we are fine since we have only one WAN binding initally + // (all the other WAN lines have place holders). + // + if (Device->FirstWanNicId == (USHORT)1) { + Device->FirstWanNicId = Binding->NicId; + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + + // + // Since WAN can have only one frame type, break + // + break; + + } else { + + Device->BindingCount++; + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+1 >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, 1); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // LAN: Figure out if it is a slave binding only for non-auto-detect bindings. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (j = 1; j < Index; j++) { + MasterBinding = NIC_ID_TO_BINDING_NO_ILOCK(Device, j); + if ((MasterBinding->ConfiguredNetworkNumber) && + (MasterBinding->ConfiguredNetworkNumber == Binding->ConfiguredNetworkNumber) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + + CTEAssert(Binding->ConfiguredNetworkNumber); + break; + } + } + } + + if (j < Device->HighestExternalNicId) { + // + // Slave binding + // + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %ws is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Place the binding after the last slave binding + // + INSERT_BINDING(Device, Device->ValidBindings+1, Binding); + SET_VERSION(Device, Device->ValidBindings+1); + + Binding->NicId = (USHORT)Device->ValidBindings+1; + + // + // Update the indices + // + Device->ValidBindings++; + + } else { + + // + // Not a binding set slave binding - just add it after the last LAN binding + // + + // + // Move WAN and Slaves down by 1 entry + // + for (j = Device->ValidBindings; j > Device->HighestLanNicId; j--) { + INSERT_BINDING(Device, j+1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)->NicId++; + } + } + + // + // Insert the LAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestLanNicId+1, Binding); + SET_VERSION(Device, Device->HighestLanNicId+1); + Binding->NicId = (USHORT)Device->HighestLanNicId+1; + + // + // Update the indices + // + Device->HighestLanNicId++; + Device->HighestExternalNicId++; + Device->ValidBindings++; + Device->HighestType20NicId++; + Device->SapNicCount++; + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = Binding->NicId; + } + + } + + } + + // + // Insert this binding in the RIP Tables + // + if (Binding->ConfiguredNetworkNumber != 0) { + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + } +} /* IpxPnPUpdateBindingArray */ + + +VOID +IpxPnPToLoad() +/*++ + +Routine Description: + + This routine takes the driver to LOADED state (from OPEN) when all + PnP adapters have been removed from the machine. + +Arguments: + + None. + +Return Value: + + None. When the function returns, the driver is in LOADED state. + +--*/ + +{ + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + NTSTATUS ntStatus; + + IPX_DEBUG(PNP, ("Going back to loaded state\n")); + + // + // Inform TDI clients about the open of our device object. + // + if ((ntStatus = TdiDeregisterDeviceObject(IpxDevice->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterDeviceObject failed: %lx", ntStatus)); + } + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } +} /* IpxPnPToLoad */ + + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ) +/*++ + +Routine Description: + + This routine reallocates the binding array when the number of bindings go above + Device->MaxBindings. + +Arguments: + + Device - pointer to the device. + Size - the number of new entries required. + +Return Value: + + None. + +--*/ +{ + PBIND_ARRAY_ELEM BindingArray; + ULONG Pad=2; // extra bindings we keep around + ULONG NewSize = Size + Pad + Device->MaxBindings; + + // + // The absolute max WAN bindings. + // + CTEAssert(Size < 2048); + + // + // Re-allocate the new array + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + NewSize * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)Device->DeviceObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + + DbgPrint ("Failed to allocate memory in binding array expansion\n"); + + // + // Unload the driver here? In case of WAN, we can tolerate this failure. What about LAN? [BUGBUGZZ] + // + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory (BindingArray, NewSize * sizeof(BIND_ARRAY_ELEM)); + + // + // Copy the old array into the new one. + // + RtlCopyMemory (BindingArray, Device->Bindings, (Device->ValidBindings+1) * sizeof(BIND_ARRAY_ELEM)); + + // + // Free the old one. + // + IpxFreeMemory ( Device->Bindings, + Device->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + IPX_DEBUG(PNP, ("Expand bindarr old: %lx, new: %lx, oldsize: %lx\n", + Device->Bindings, BindingArray, Device->MaxBindings)); + + // + // Use interlocked exchange to assign this since we dont take the BindAccessLock anymore. + // + // Device->Bindings = BindingArray; + SET_VALUE(Device->Bindings, BindingArray); + + Device->MaxBindings = (USHORT)NewSize; + + return STATUS_SUCCESS; +} +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isnp/ipx/event.c b/private/ntos/tdi/isnp/ipx/event.c new file mode 100644 index 000000000..a64f85d34 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/event.c @@ -0,0 +1,143 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added a new event type - TDI_EVENT_CHAINED_RECEIVE_DATAGRAM +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Request - Pointer to the request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile (AddressFile); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + CTEGetLock (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + + switch (Parameters->EventType) { + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + // + // [SA] New event handler to receive chained buffers + // + case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + } else { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredChainedReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (Parameters->EventHandler == NULL) { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + AddressFile->RegisteredErrorHandler = FALSE; + } else { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)Parameters->EventHandler; + AddressFile->ErrorHandlerContext = Parameters->EventContext; + AddressFile->RegisteredErrorHandler = TRUE; + } + + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + + } /* switch */ + + CTEFreeLock (&AddressFile->Address->Lock, LockHandle); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + REQUEST_INFORMATION(Request) = 0; + + return Status; + +} /* IpxTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isnp/ipx/ind.c b/private/ntos/tdi/isnp/ipx/ind.c new file mode 100644 index 000000000..f43a524bc --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ind.c @@ -0,0 +1,4047 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ind.c + +Abstract: + + This module contains code which implements the indication handler + for the IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added IpxReceivePacket which receives buffers that can be owned + 2. Changed IpxReceiveIndication to call a new function IpxReceiveIndicationNew + which takes an extra parameter to indicate whether this is a chained receive or + not. + 3. Changed IpxProcessDatagram to take the MDL ptr to indicate chained receive, + a client count and the headerbuffersize as params. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is declared here so it will be in the same function +// as IpxReceiveIndication and we can inline it. +// + + +#if defined(_M_IX86) +_inline +#endif +VOID +IpxProcessDatagram( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING Binding, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_DATAGRAM_OPTIONS DatagramOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast, + IN PINT pTdiClientCount, + IN UINT HeaderBufferSize, + IN PMDL pMdl, + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routing handles incoming IPX datagrams. + +Arguments: + + Device - The IPX device. + + Adapter - The adapter the frame was received on. + + Binding - The binding of the adapter it was received on. + + MacReceiveContext - The context to use when calling + NdisTransferData. + + DatagramOptions - Contains the datagram options, which + consists of room for the packet type, padding, and + the local target of the remote the frame was received from. + + LookaheadBuffer - The lookahead data. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The length of the packet, starting at the IPX + header. + + Broadcast - TRUE if the packet was broadcast. + + pTdiClientCount - to return count of the number of TDI clients above us + so NDIS can obtain that many ref counts on the buffer. + + HeaderBufferSize - the size of the MAC header buffer - used to determine + the offsets into the TSDU. + + pMdl - Mdl chain pointer - non-NULL if chained receive + + BindingContext - In case of loopback, this contains IPX_LOOPBACK_COOKIE + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)LookaheadBuffer; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PADDRESS_FILE ReferencedAddressFile; + PREQUEST Request; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_IPX UNALIGNED * DatagramAddress; + ULONG IndicateBytesCopied; + IPX_ADDRESS_EXTENDED_FLAGS SourceAddress; + ULONG SourceAddressLength; + ULONG RequestCount; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PIRP Irp; + UINT ByteOffset, BytesToTransfer; + ULONG BytesTransferred; + BOOLEAN LastAddressFile; + ULONG IndicateOffset; + PNDIS_PACKET ReceivePacket; + PIPX_RECEIVE_RESERVED Reserved; + PLIST_ENTRY p, q; + PSINGLE_LIST_ENTRY s; + USHORT DestinationSocket; + USHORT SourceSocket; + ULONG Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // First scan the device's address database, looking for + // the destination socket of this frame. + // + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Address = Device->LastAddress) && + (Address->Socket == DestinationSocket)) { + + // + // Device->LastAddress cannot be stopping, so + // we use it. + // + + IpxReferenceAddressLock (Address, AREF_RECEIVE); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + + Hash = IPX_DEST_SOCKET_HASH (IpxHeader); + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if ((Address->Socket == DestinationSocket) && + (!Address->Stopping)) { + IpxReferenceAddressLock (Address, AREF_RECEIVE); + Device->LastAddress = Address; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If we had found an address we would have jumped + // past here. + // + + return; + +FoundAddress: + + SourceSocket = *(USHORT UNALIGNED *)&IpxHeader->SourceSocket; + IpxBuildTdiAddress( + &SourceAddress.IpxAddress, + (*(ULONG UNALIGNED *)(IpxHeader->SourceNetwork) == 0) ? + Binding->LocalAddress.NetworkAddress : + *(UNALIGNED ULONG *)(IpxHeader->SourceNetwork), + IpxHeader->SourceNode, + SourceSocket); + + DatagramOptions->PacketType = IpxHeader->PacketType; + + + // + // Now that we have found the address, scan its list of + // address files for clients that want this datagram. + // + // If we have to release the address lock to indicate to + // a client, we reference the current address file. If + // we get an IRP we transfer the reference to that; + // otherwise we store the address file in ReferencedAddressFile + // and deref it the next time we release the lock. + // + + ReferencedAddressFile = NULL; + RequestCount = 0; + + ++Device->TempDatagramsReceived; + Device->TempDatagramBytesReceived += (PacketSize - sizeof(IPX_HEADER)); + + // + // If LastAddressFile is TRUE, it means we did an indication + // to the client on the last address file in the address' + // list, and we did not reacquire the lock when we were + // done. + // + + LastAddressFile = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; // next address file + } + + // + // Set these to the common values, then change them. + // + + SourceAddressLength = sizeof(TA_IPX_ADDRESS); + IndicateOffset = sizeof(IPX_HEADER); + + if (AddressFile->SpecialReceiveProcessing) { + + // + // On dial out lines, we don't indicate packets to + // the SAP socket if DisableDialoutSap is set. + // + + if ((AddressFile->IsSapSocket) && + (Binding->DialOutAsync) && + (Device->DisableDialoutSap || Device->SingleNetworkActive)) { + + // + // Go to the next address file (although it will + // likely fail this test too). + // + + continue; + + } + + // + // Set this, since generally we want it. + // + + SourceAddress.PacketType = IpxHeader->PacketType; + + // + // See if we fail a packet type filter. + // + + if (AddressFile->FilterOnPacketType) { + if (AddressFile->FilteredType != IpxHeader->PacketType) { + continue; + } + } + + // + // Calculate how long the addresses expected are. + // + + if (AddressFile->ReceiveFlagsAddressing || + AddressFile->ExtendedAddressing) { + + SourceAddress.Flags = 0; + if (Broadcast) { + SourceAddress.Flags = IPX_EXTENDED_FLAG_BROADCAST; + } + if (IpxIsAddressLocal((TDI_ADDRESS_IPX UNALIGNED *) + &SourceAddress.IpxAddress.Address[0].Address[0])) { + SourceAddress.Flags |= IPX_EXTENDED_FLAG_LOCAL; + } + SourceAddressLength = sizeof(IPX_ADDRESS_EXTENDED_FLAGS); + SourceAddress.IpxAddress.Address[0].AddressLength += + (sizeof(IPX_ADDRESS_EXTENDED_FLAGS) - sizeof(TA_IPX_ADDRESS)); + + } + + // + // Determine how much of the packet the client wants. + // + + if (AddressFile->ReceiveIpxHeader) { + IndicateOffset = 0; + } + } + + // + // First scan the address' receive datagram queue + // for datagrams that match. We do a quick check + // to see if the list is empty. + // + + q = AddressFile->ReceiveDatagramQueue.Flink; + if (q != &AddressFile->ReceiveDatagramQueue) { + + do { + + Request = LIST_ENTRY_TO_REQUEST(q); + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReceiveDatagramInformation; + + if ((DatagramInformation != NULL) && + (DatagramInformation->RemoteAddress != NULL) && + (DatagramAddress = IpxParseTdiAddress(DatagramInformation->RemoteAddress)) && + (DatagramAddress->Socket != SourceSocket)) { + + // + // The address that this datagram is looking for is + // not satisfied by this frame. + // + // BUGBUG: Speed this up; worry about node and network? + // + + q = q->Flink; + continue; // next receive datagram on this address file + + } else { + + // + // We found a datagram on the queue. + // + + IPX_DEBUG (RECEIVE, ("Found RDG on %lx\n", AddressFile)); + RemoveEntryList (q); + REQUEST_INFORMATION(Request) = 0; + + goto HandleDatagram; + + } + + } while (q != &AddressFile->ReceiveDatagramQueue); + + } + + // + // If we found a datagram we would have jumped past here, + // so looking for a datagram failed; see if the + // client has a receive datagram handler registered. + // + + // + // Look for the chained receive handler if the MDL is not NULL + // + if (pMdl && AddressFile->RegisteredChainedReceiveDatagramHandler) { + + // + // Chained receive both above and below => we indicate the entire MDL up. + // Offset the LookaheadBuffer by the size of the MAC header. + // + LookaheadBufferOffset += HeaderBufferSize; + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IndicateBytesCopied = 0; + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IPX_DEBUG(RECEIVE, ("ChainedIndicate RecvLen: %d, StartOffset: %d, Tsdu: %lx\n", + PacketSize - IndicateOffset, IndicateOffset, pMdl)); + + // + // Will return SUCCESS if the client did not take ownership of the Tsdu + // PENDING if the client took ownership and will free it later (using TdiFreeReceiveChain). + // DATA_NOT_ACCEPTED if the client did not take ownership and did not copy the data. + // + + // + // Since NDIS needs an array of PNDIS_PACKETs when the TDI client returns this packet, + // we pass the Packet as the ReceiveContext here. The TDI client will pass in the address + // of this context on a ReturnPacket. + // Also, NDIS needs the PacketArray (not to be confused with the array of packetptrs. mentioned + // above) on an NdisTransferData call. These clients dont do this, but other clients like + // NB, SPX, RIP or TDI clients that do not have this new interface, can call NdisTransferData + // so we pass in the PacketArray as a parameter to them. + // + Status = (*AddressFile->ChainedReceiveDatagramHandler)( + AddressFile->ChainedReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, // TdiRcvFlags|Adapter->MacInfo.CopyLookahead, Receive datagram flags + PacketSize - IndicateOffset, // ReceiveLength + IndicateOffset+LookaheadBufferOffset, // StartingOffset + pMdl, // Tsdu - MDL chain + (PNDIS_PACKET)MacReceiveContext); // TransportContext - pointer to the packet + + if (Status != STATUS_DATA_NOT_ACCEPTED) { + + if (Status == STATUS_PENDING) { + // + // We assume here that the client referenced the packet which will + // be removed when the packet is freed. + // Increment the Tdi client count + // + (*pTdiClientCount)++; + } + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + // RequestCount should always be 0 here. + // + + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + + } else { + // + // Since no IRP can be returned here, we continue to the next addressfile + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + } + + } else if (AddressFile->RegisteredReceiveDatagramHandler) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IndicateBytesCopied = 0; + + if (PacketSize > LookaheadBufferSize) { + IPX_DEBUG(RECEIVE, ("Indicate %d/%d to %lx on %lx\n", + LookaheadBufferSize, PacketSize, + AddressFile->ReceiveDatagramHandler, AddressFile)); + } + + Status = (*AddressFile->ReceiveDatagramHandler)( + AddressFile->ReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, + LookaheadBufferSize - IndicateOffset, // indicated + PacketSize - IndicateOffset, // available + &IndicateBytesCopied, // taken + LookaheadBuffer + IndicateOffset, // data + &Irp); + + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + if (RequestCount == 0) { + return; + } + goto BreakWithoutLock; + } + + } else { + + // + // The client returned an IRP. + // + + IPX_DEBUG (RECEIVE, ("Indicate IRP %lx, taken %d\n", Irp, IndicateBytesCopied)); + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ReferencedAddressFile = AddressFile; + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + if (!LastAddressFile) { + IPX_GET_LOCK (&Address->Lock, &LockHandle); + } + +#if DBG + // + // Make sure the IRP file object is right. + // + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext != AddressFile) { + DbgPrint ("IRP %lx does not match AF %lx, H %lx C %lx\n", + Irp, AddressFile, + AddressFile->ReceiveDatagramHandler, + AddressFile->ReceiveDatagramHandlerContext); + DbgBreakPoint(); + } +#endif + // + // Set up the information field so we know + // how much to skip in it. + // + + IpxTransferReferenceAddressFile (AddressFile, AFREF_INDICATION, AFREF_RCV_DGRAM); + REQUEST_INFORMATION(Request) = IndicateBytesCopied; + + // + // Fall out of the if and continue via + // HandleDatagram... + // + + } + + } else { + + // + // No posted datagram, no handler; go to the next + // address file. + // + + continue; // next address file + + } + +HandleDatagram: + + // + // At this point, Request is set to the request + // that will hold for this address file, and + // REQUEST_INFORMATION() is the offset to start + // the transfer at. + // + + // + // First copy over the source address while it is handy. + // + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReturnDatagramInformation; + + if (DatagramInformation != NULL) { + + RtlCopyMemory( + DatagramInformation->RemoteAddress, + &SourceAddress, + (ULONG)DatagramInformation->RemoteAddressLength < SourceAddressLength ? + DatagramInformation->RemoteAddressLength : SourceAddressLength); + RtlCopyMemory( + DatagramInformation->Options, + &DatagramOptions, + (ULONG)DatagramInformation->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS) ? + DatagramInformation->OptionsLength : sizeof(IPX_DATAGRAM_OPTIONS)); + + } + + // + // Now check if this is the first request that will + // take the data, otherwise queue it up. + // + + if (RequestCount == 0) { + + // + // First one; we need to allocate a packet for the transfer. + // + + //if (Address->ReceivePacketInUse) { + if (InterlockedExchangeAdd(&Address->ReceivePacketInUse, 0) != 0) { + // + // Need a packet, check the pool. + // + + s = IpxPopReceivePacket (Device); + + if (s == NULL) { + + // + // None in pool, fail the request. + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + + } + + Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + ReceivePacket = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } else { + + // Address->ReceivePacketInUse = TRUE; + InterlockedIncrement(&Address->ReceivePacketInUse); + + ReceivePacket = PACKET(&Address->ReceivePacket); + Reserved = RECEIVE_RESERVED(&Address->ReceivePacket); + + } + + CTEAssert (IsListEmpty(&Reserved->Requests)); + + Reserved->SingleRequest = Request; + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + + ByteOffset = REQUEST_INFORMATION(Request) + LookaheadBufferOffset + IndicateOffset; + BytesToTransfer = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength; + + if (BytesToTransfer > (PacketSize - IndicateOffset)) { + BytesToTransfer = PacketSize - IndicateOffset; + } + + } else { + + if (RequestCount == 1) { + + // + // There is already one request. We need to + // allocate a buffer. + // + + s = IpxPopReceiveBuffer (Adapter); + + if (s == NULL) { + + // + // No buffers, fail the request. + // + // BUGBUG: Should we fail the transfer for the + // first request too? + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + } + + ReceiveBuffer = CONTAINING_RECORD(s, IPX_RECEIVE_BUFFER, PoolLinkage); + NdisBuffer = ReceiveBuffer->NdisBuffer; + + // + // Convert this to a queued multiple piece request. + // + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Reserved->SingleRequest)); + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = ReceiveBuffer; + + ByteOffset = LookaheadBufferOffset; + BytesToTransfer = PacketSize; + + } + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Request)); + + } + + // + // We are done setting up this address file's transfer, + // proceed to the next one. + // + + ++RequestCount; + + if (LastAddressFile) { + goto BreakWithoutLock; + } + + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + +BreakWithoutLock: + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + + // + // We can be transferring directly into a request's buffer, + // transferring into an intermediate buffer, or not + // receiving the packet at all. + // + + if (RequestCount > 0) { + + // + // If this is true, then ReceivePacket, Reserved, + // and NdisBuffer are all set up correctly. + // + + CTEAssert (ReceivePacket); + CTEAssert (Reserved == (PIPX_RECEIVE_RESERVED)(ReceivePacket->ProtocolReserved)); + + + NdisChainBufferAtFront(ReceivePacket, NdisBuffer); + + IPX_DEBUG (RECEIVE, ("Transfer into %lx, offset %d bytes %d\n", + NdisBuffer, ByteOffset, BytesToTransfer)); + + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("Loopback Copy from packet: %lx to packet: %lx\n", ReceivePacket, MacReceiveContext)); + + NdisCopyFromPacketToPacket( + ReceivePacket, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset - loopback packet + &BytesTransferred); // BytesCopied + + NdisStatus = NDIS_STATUS_SUCCESS; + + } else { + NdisTransferData( + &NdisStatus, + Adapter->NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + ReceivePacket, + &BytesTransferred); + } + + if (NdisStatus != NDIS_STATUS_PENDING) { + + IpxTransferDataComplete( + (NDIS_HANDLE)Adapter, + ReceivePacket, + NdisStatus, + BytesTransferred); + } + } + + + IpxDereferenceAddressSync (Address, AREF_RECEIVE); + +} /* IpxProcessDatagram */ + + + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + // + // Call the actual receive indication handler and indicate that this is not a + // chained receive + // + + return IpxReceiveIndicationNew ( + BindingContext, + ReceiveContext, // ReceiveContext + HeaderBuffer, + HeaderBufferSize, + LookaheadBuffer, + LookaheadBufferSize, + PacketSize, // PacketSize + NULL, // pMdl - non-NULL => chained receive. + NULL // pTdiClientCount - used in chained recv case to keep count of TDI clients + ); + +} + + +NDIS_STATUS +IpxReceiveIndicationNew( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + + pMdl - pointer to MDL chain if chained, NULL if this came from indication. + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + + IPX_DATAGRAM_OPTIONS DatagramOptions; + PADAPTER Adapter = (PADAPTER)BindingContext; + PBINDING Binding; + PDEVICE Device = IpxDevice; + PUCHAR Header = (PUCHAR)HeaderBuffer; + PUCHAR Lookahead = (PUCHAR)LookaheadBuffer; + ULONG PacketLength; + UINT IpxPacketSize; + ULONG Length802_3; + USHORT Saps; + ULONG DestinationNetwork; + ULONG SourceNetwork; + PUCHAR DestinationNode; + USHORT DestinationSocket; + ULONG IpxHeaderOffset; + PIPX_HEADER IpxHeader; + UINT i; + BOOLEAN IsBroadcast; + BOOLEAN IsLoopback = FALSE; +#if DBG + PUCHAR DestMacAddress; + ULONG ReceiveFlag; +#endif + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif _PNP_POWER + + // + // Reject packets that are too short to hold even the + // basic IPX header (this ignores any extra 802.2 etc. + // headers but is good enough because a runt will fail + // the IPX header packet length check). + // + + if (PacketSize < sizeof(IPX_HEADER)) { + return STATUS_SUCCESS; + } + + // + // If this is a loopback packet, no need to do figure out the + // MAC header. + // + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + +#ifdef _PNP_POWER + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, 1); + + if (!Binding) { + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); + + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, 0); +#else + if ((Binding = IpxDevice->Bindings[1]) == NULL) { + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + DatagramOptions.LocalTarget.NicId = 0; +#endif + + // + // Do this copy later, from the IpxHeader. + // + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->Adapter->MacInfo.MediumType == NdisMedium802_5) { + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + } + + // + // Ipx header starts at the top of the LookAheadBuffer + // + IpxHeaderOffset = 0; + + IPX_DEBUG (LOOPB, ("Loopback packet received: %lx\n", ReceiveContext)); + +#if DBG + DestMacAddress = DatagramOptions.LocalTarget.MacAddress; +#endif + + IsLoopback = TRUE; + goto Loopback; + } + +#ifdef _PNP_POWER + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); +#endif + + // + // The first step is to construct the 8-byte local + // target from the packet. We store it in the 9-byte + // datagram options, leaving one byte at the front + // for use by IpxProcessDatagram when indicating to + // its TDI clients. + // + +#if DBG + Binding = NULL; +#endif + + if (Adapter->MacInfo.MediumType == NdisMedium802_3) { + + // + // Try to figure out what the packet type is. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Header[12] < 0x06) { + + // + // An 802.3 header; check the next bytes. They may + // be E0/E0 (802.2), FFFF (raw 802.3) or A0/A0 (SNAP). + // + + Saps = *(UNALIGNED USHORT *)(Lookahead); + + if (Saps == 0xffff) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 0; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + + } else if (Saps == 0xe0e0) { + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 3; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 8; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + } + + goto NotValid802_3; + + } else { + + // + // It has an ethertype, see if it is ours. + // + + if (*(UNALIGNED USHORT *)(Header+12) == Adapter->BindSapNetworkOrder) { + + if (Adapter->MacInfo.MediumAsync) { + + *((ULONG UNALIGNED *)(&Binding)) = *((ULONG UNALIGNED *)(&Header[2])); + + CTEAssert(Binding != NULL); + + if ((Binding != NULL) && + (Binding->LineUp)) { + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + + // + // Check if this is a type 20 packet and + // we are disabling them on dialin lines -- we do + // this check here to avoid impacting the main + // indication path for LANs. + // + // The 0x02 bit of DisableDialinNetbios controls + // WAN->LAN packets, which we handle here. + // + + if ((!Binding->DialOutAsync) && + ((Device->DisableDialinNetbios & 0x02) != 0)) { + + IpxHeader = (PIPX_HEADER)Lookahead; // IpxHeaderOffset is 0 + if (IpxHeader->PacketType == 0x14) { +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + return STATUS_SUCCESS; + } + } + + goto Valid802_3; + } + goto NotValid802_3; + + } else if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_ETHERNET_II]) == NULL) { + goto NotValid802_3; + } + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + goto Valid802_3; + + } + } + + goto NotValid802_3; + +Valid802_3: + + if (Length802_3 > PacketSize) { + goto NotValid802_3; + } else if (Length802_3 < PacketSize) { + PacketSize = Length802_3; + if (LookaheadBufferSize > Length802_3) { + LookaheadBufferSize = Length802_3; + } + } + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+6, 6); +#if DBG + DestMacAddress = Header; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMedium802_5) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_5; + } + + IpxHeaderOffset = 3; + goto Valid802_5; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_5; + } + IpxHeaderOffset = 8; + goto Valid802_5; + } + } + + goto NotValid802_5; + +Valid802_5: +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+8, 6); + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + +#if DBG + DestMacAddress = Header+2; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMediumFddi) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 3; + goto ValidFddi; + } + + } else if (Saps == 0xffff) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 0; + goto ValidFddi; + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 8; + goto ValidFddi; + } + } + + goto NotValidFddi; + +ValidFddi: + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+7, 6); + +#if DBG + DestMacAddress = Header+1; +#endif + + + } else { + + // + // NdisMediumArcnet878_2 + // + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + if ((Header[2] == ARCNET_PROTOCOL_ID) && + ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) != NULL)) { + + IpxHeaderOffset = 0; + RtlZeroMemory (DatagramOptions.LocalTarget.MacAddress, 5); + DatagramOptions.LocalTarget.MacAddress[5] = Header[0]; + + } else { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+1, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + } + +#if DBG + DestMacAddress = Header+2; // BUGBUG Need to log less than six bytes +#endif + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + + // + // Make sure this didn't slip through. + // + + CTEAssert (Binding != NULL); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->NicId; +#endif + +Loopback: + + // + // Now that we have validated the header and constructed + // the local target, indicate the packet to the correct + // client. + // + + IpxHeader = (PIPX_HEADER)(Lookahead + IpxHeaderOffset); + + PacketLength = (IpxHeader->PacketLength[0] << 8) | IpxHeader->PacketLength[1]; + + IpxPacketSize = PacketSize - IpxHeaderOffset; + + if (PacketLength > IpxPacketSize) { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, DestMacAddress, DatagramOptions.LocalTarget.MacAddress, (USHORT)PacketSize, IpxHeader, IpxHeader+1); + } +#endif + IPX_DEBUG (BAD_PACKET, ("Packet len %d, IPX len %d\n", + PacketLength, IpxPacketSize)); + + return NDIS_STATUS_SUCCESS; + + } else if (PacketLength < IpxPacketSize) { + + IpxPacketSize = PacketLength; + if (LookaheadBufferSize > (PacketLength + IpxHeaderOffset)) { + LookaheadBufferSize = PacketLength + IpxHeaderOffset; + } + + } + + // + // Bug #33595 - (hotfixed in 3.51, checked into 4.0 beta2) + // Customer problem where NT allowed RIP/SAP to reply to an 802.5 functional address in the IPX source node. The source + // MAC address was proper in this case. We need to check for the case where if the packet's source network is the same + // as that of the binding it came on (=> did not come thru a router), then the SourceNodeAddress in the IPX header + // should be equal to the SourceAddress in the MAC header. + // + // This check is controlled through a registry value - VerifySourceAddress. + // In case of Arcnet, this check will not succeed. + // Also, for WAN, the node addresses will not match, so avoid check for those. + + // + // If the source network is 0, we drop it. Auto-detect frames should have matching node (MAC) addresses. + // Loopback packets dont have a valid header, so skip this test for them. + // + // BUGBUG: For loopback pkts, do all the processing above, so we can avoid all these checks for IsLoopback here. + // Also, to prevent the RtlCopyMemory into the localtarget above, try to use the MAC header to indicate the + // correct binding to us so we dont use the first one always. + // + // CAVEAT:: when using the MAC header as a binding pointer, ensure that we use the adapter corresp, to that binding + // to enque all the receive requests. currently we enqueue them onto the first bindings adapter. + // + if (((*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == Binding->LocalAddress.NetworkAddress) || + (*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == 0)) && + (!IPX_NODE_EQUAL (IpxHeader->SourceNode, DatagramOptions.LocalTarget.MacAddress)) && + Device->VerifySourceAddress && + !IsLoopback && + !Adapter->MacInfo.MediumAsync && + (Adapter->MacInfo.MediumType != NdisMediumArcnet878_2)) { + + IPX_DEBUG(BAD_PACKET, ("Local packet: Src MAC %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x ", + DatagramOptions.LocalTarget.MacAddress[0], + DatagramOptions.LocalTarget.MacAddress[1], + DatagramOptions.LocalTarget.MacAddress[2], + DatagramOptions.LocalTarget.MacAddress[3], + DatagramOptions.LocalTarget.MacAddress[4], + DatagramOptions.LocalTarget.MacAddress[5])); + + IPX_DEBUG(BAD_PACKET, ("IPX Src Node %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + +#ifdef IPX_PACKET_LOG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + + return NDIS_STATUS_SUCCESS; + } + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + // + // In order to have consistent local targets, copy over the target from the IpxHeader. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet copied the localtarget: %lx\n", IpxHeader->DestinationNode)); + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + *((UNALIGNED ULONG *)DatagramOptions.LocalTarget.MacAddress) = + *((UNALIGNED ULONG *)IpxHeader->DestinationNode); + + *((UNALIGNED USHORT *)(DatagramOptions.LocalTarget.MacAddress+4)) = + *((UNALIGNED USHORT *)(IpxHeader->DestinationNode+4)); + } + + ++Device->Statistics.PacketsReceived; + + if (DestinationSocket != RIP_SOCKET) { + + DestinationNetwork = *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork; + DestinationNode = IpxHeader->DestinationNode; + +RecheckPacket: + + if (Device->MultiCardZeroVirtual) { + + if ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + } else { + + if ((DestinationNetwork == Device->SourceAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Device->SourceAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + // + // We need to check for frames that are sent to the + // binding node and net, because if we have a virtual + // net we won't catch them in the check above. This + // will include any Netbios frames, since they don't + // use the virtual net. Doing the check like this will slow + // down netbios indications just a bit on a machine with + // a virtual network, but it saves a jump for other traffic + // vs. adding the check up there (the assumption is if we + // have a virtual net most traffic is NCP). + // + // Note that IsBroadcast is already set, so we don't have + // to do that. + // + + if ((Device->VirtualNetwork) && + ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0))) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + goto DestinationOk; + } else { + if (IsBroadcast && (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + } + } + + // + // If this was a loopback packet that was sent on the second binding (but showed back up on the first one), + // then the networknumbers will not match. Allow the receive on the first binding itself. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet forced on first binding: %lx\n", ReceiveContext)); + goto DestinationOk; + } + + // + // If we did not receive this packet, it might be because + // our network is still 0 and this packet was actually + // sent to the real network number. If so we try to + // update our local address, and if successful we + // re-check the packet. We don't insert if we are + // not done with auto detection, to avoid colliding + // with that. + // + // To avoid problems if we are a router, we only update + // on packets that are broadcast or sent to us. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + (Device->AutoDetectState == AUTO_DETECT_STATE_DONE) && + (DestinationNetwork != 0) && + (IsBroadcast || + IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress))) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + DestinationNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d reconfigured to network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + // + // Jump back and re-process the packet; we know + // we won't loop through here again because the + // binding's network is now non-zero. + // + + goto RecheckPacket; + + } + } + + + // + // The only frames that will not already have jumped to + // DestinationOk are those to or from the SAP socket, + // so we check for those. + // + + if ((*(USHORT UNALIGNED *)&IpxHeader->SourceSocket == SAP_SOCKET) || + (DestinationSocket == SAP_SOCKET)) { + +DestinationOk: + + // + // An IPX packet sent to us, or a SAP packet (which + // are not sent to the virtual address but still need + // to be indicated and not forwarded to RIP). + // + + if (DestinationSocket == NB_SOCKET) { +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_NB | IPX_PACKET_LOG_RCV_ALL; +#endif + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_NB].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_NB])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_NB, Adapter, Header, HeaderBufferSize); + } + + // + // We add HeaderBufferSize to the IpxHeaderOffset field since we do an NdisCopyFromPacketToPacket + // in IpxTransferData, which needs offset from the beginning of the packet. + // NdisTransferData adds the offset passed in to the beginning of the IPX packet. + // + (*Device->UpperDrivers[IDENTIFIER_NB].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_NB] = TRUE; + } + + // + // The router needs to see Netbios type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast)) { + goto RipIndication; + } + + } else if (IpxHeader->PacketType == SPX_PACKET_TYPE) { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_SPX | IPX_PACKET_LOG_RCV_ALL; +#endif + + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_SPX].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_SPX])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_SPX, Adapter, Header, HeaderBufferSize); + } + + (*Device->UpperDrivers[IDENTIFIER_SPX].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_SPX] = TRUE; + } + + } else { + + IPX_DEBUG (RECEIVE, ("Received packet type %d, length %d\n", + Binding->FrameType, + IpxPacketSize)); + IPX_DEBUG (RECEIVE, ("Source %lx %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(USHORT UNALIGNED *)&IpxHeader->SourceSocket, + IpxHeader->SourceNetwork[0], + IpxHeader->SourceNetwork[1], + IpxHeader->SourceNetwork[2], + IpxHeader->SourceNetwork[3], + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + IPX_DEBUG (RECEIVE, ("Destination %d %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + DestinationSocket, + IpxHeader->DestinationNetwork[0], + IpxHeader->DestinationNetwork[1], + IpxHeader->DestinationNetwork[2], + IpxHeader->DestinationNetwork[3], + IpxHeader->DestinationNode[0], + IpxHeader->DestinationNode[1], + IpxHeader->DestinationNode[2], + IpxHeader->DestinationNode[3], + IpxHeader->DestinationNode[4], + IpxHeader->DestinationNode[5])); + +#if DBG + if (IpxHeader->DestinationSocket == IpxPacketLogSocket) { + ReceiveFlag = IPX_PACKET_LOG_RCV_SOCKET | IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } else { + ReceiveFlag = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } +#endif + + // + // Fiddle with this if so in the general case + // the jump is not made (BUGBUG the compiler + // still rearranges it). + // + + if (Adapter->MacInfo.MediumType != NdisMedium802_5) { + +CallProcessDatagram: + // + // [SA] Returns a status now which needs to be returned to NDIS + // Also, MDL is passed in. + // We need to pass in the HeaderBufferSize too.... + // + IpxProcessDatagram( + Device, + Adapter, + Binding, + ReceiveContext, + &DatagramOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, // lookaheadbufferoffset + IpxPacketSize, + IsBroadcast, + pTdiClientCount, + HeaderBufferSize, + pMdl, + BindingContext); + + } else { + if (!IsLoopback) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + goto CallProcessDatagram; + } + + // + // The router needs to see type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast)) { + goto RipIndication; + } + } + + } else { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // We need to let non-type 20 broadcast frames go to RIP to allow for lan-specific + // broadcasts. For logon over IPX, this allows the logon request to get thru the WAN + // line. + // + // if ( !IsBroadcast ) { + +RipIndication:; + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_RIP, Adapter, Header, HeaderBufferSize); + } + + // + // We hide binding sets from the router, to avoid + // misordering packets which it routes. + // + + if (!IsLoopback && Binding->BindingSetMember) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN (Device->MaxBindings, Binding->MasterBinding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->MasterBinding->NicId; +#endif + } + + (*Device->UpperDrivers[IDENTIFIER_RIP].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_RIP] = TRUE; + } + // } + } + + } else { + + if ((Binding->ReceiveBroadcast) || + (!IPX_NODE_BROADCAST(IpxHeader->DestinationNode))) { + + SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork; + + // + // Sent to the RIP socket; check if this binding needs a + // network number. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + ((SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork) != 0)) { + + switch (Device->AutoDetectState) { + + case AUTO_DETECT_STATE_DONE: + + // + // We are done with auto-detect and running. + // Make sure this packet is useful. If the source + // MAC address and source IPX node are the same then + // it was not routed, and we also check that it is not + // an IPX broadcast (otherwise a misconfigured client + // might confuse us). + // + + if ((RtlEqualMemory( + IpxHeader->SourceNode, + DatagramOptions.LocalTarget.MacAddress, + 6)) && + (*(UNALIGNED ULONG *)(IpxHeader->DestinationNode) != 0xffffffff) && + (*(UNALIGNED USHORT *)(IpxHeader->DestinationNode+4) != 0xffff)) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + *(UNALIGNED LONG *)IpxHeader->SourceNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d is network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + } + } + + break; + + case AUTO_DETECT_STATE_RUNNING: + + // + // We are waiting for rip responses to figure out our + // network number. We count the responses that match + // and do not match our current value; when the non- + // matching number exceeds it we switch (to whatever + // this frame happens to have). Note that on the first + // non-zero response this will be the case and we will + // switch to that network. + // + // After auto-detect is done we call RipInsertLocalNetwork + // for whatever the current network is on each binding. + // + + if (SourceNetwork == Binding->TentativeNetworkAddress) { + + ++Binding->MatchingResponses; + + } else { + + ++Binding->NonMatchingResponses; + + if (Binding->NonMatchingResponses > Binding->MatchingResponses) { + + IPX_DEBUG (AUTO_DETECT, ("Switching to net %lx on %lx (%d - %d)\n", + REORDER_ULONG(SourceNetwork), + Binding, + Binding->NonMatchingResponses, + Binding->MatchingResponses)); + + Binding->TentativeNetworkAddress = SourceNetwork; + Binding->MatchingResponses = 1; + Binding->NonMatchingResponses = 0; + } + + } + + // + // If we are auto-detecting and we have just found + // a default, set this so that RIP stops trying + // to auto-detect on other nets. BUGBUG: Unless we + // are on a server doing multiple detects. + // + + if (Binding->DefaultAutoDetect) { + Adapter->DefaultAutoDetected = TRUE; + } + Adapter->AutoDetectResponse = TRUE; + + break; + + default: + + // + // We are still initializing, or are processing auto-detect + // responses, not the right time to start updating stuff. + // + + break; + + } + + } + + + // + // See if any packets are waiting for a RIP response. + // + + if (Device->RipPacketCount > 0) { + + RIP_PACKET UNALIGNED * RipPacket = (RIP_PACKET UNALIGNED *)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_RESPONSE) && + (RipPacket->NetworkEntry.NetworkNumber != 0xffffffff)) { + + RipProcessResponse( + Device, + &DatagramOptions.LocalTarget, + RipPacket); + } + } + + + // + // See if this is a RIP response for our virtual network + // and we are the only person who could respond to it. + // We also respond to general queries on WAN lines since + // we are the only machine on it. + // + + if (Device->RipResponder) { + + PRIP_PACKET RipPacket = + (PRIP_PACKET)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_REQUEST) && + ((RipPacket->NetworkEntry.NetworkNumber == Device->VirtualNetworkNumber) || + (Adapter->MacInfo.MediumAsync && (RipPacket->NetworkEntry.NetworkNumber == 0xffffffff)))) { + + // + // Update this so our response goes out correctly. + // + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + + RipSendResponse( + Binding, + (TDI_ADDRESS_IPX UNALIGNED *)(IpxHeader->SourceNetwork), + &DatagramOptions.LocalTarget); + } + } + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_RIP | IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // See if the RIP upper driver wants it too. + // + + goto RipIndication; + } + + } + + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + return NDIS_STATUS_SUCCESS; + + // + // These are the failure routines for the various media types. + // They only differ in the debug logging. + // + +NotValid802_3: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header, Header+6, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValid802_5: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+8, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValidFddi: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +NotValidLoopback: + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+1, Header+7, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + + return NDIS_STATUS_SUCCESS; + +} /* IpxReceiveIndication */ + + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + +Return Value: + + None + +--*/ + +{ + + PADAPTER Adapter = (PADAPTER)BindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PLIST_ENTRY linkage; + + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&Adapter->RequestCompletionQueue)) { + + linkage = IPX_REMOVE_HEAD_LIST( + &Adapter->RequestCompletionQueue, + Adapter->DeviceLock); + + if (!IPX_LIST_WAS_EMPTY (&Adapter->RequestCompletionQueue, linkage)) { + + Request = LIST_ENTRY_TO_REQUEST(linkage); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + IPX_DEBUG (RECEIVE, ("Completing RDG on %lx\n", AddressFile)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest(Request); + IpxFreeRequest(Adapter->Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_RCV_DGRAM); + + } else { + + // + // IPX_REMOVE_HEAD_LIST returned nothing, so don't + // bother looping back. + // + + break; + + } + + } + + // + // Unwind this loop for speed. + // + + if (IpxDevice->AnyUpperDriverBound) { + + PDEVICE Device = IpxDevice; + + if ((Device->UpperDriverBound[0]) && + (Device->ReceiveCompletePending[0])) { + + (*Device->UpperDrivers[0].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[0] = FALSE; + + } + + if ((Device->UpperDriverBound[1]) && + (Device->ReceiveCompletePending[1])) { + + (*Device->UpperDrivers[1].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[1] = FALSE; + + } + + if ((Device->UpperDriverBound[2]) && + (Device->ReceiveCompletePending[2])) { + + (*Device->UpperDrivers[2].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[2] = FALSE; + + } + + } + +} /* IpxReceiveComplete */ + + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ) + +/*++ + +Routine Description: + + This routine is called when we have decided that we now know + the network number for a binding which we previously thought + was zero. + +Arguments: + + Device - The IPX device. + + Binding - The binding being updated. + + Network - The new network number. + +Return Value: + + The status of the operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS Address; + ULONG CurrentHash; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Only binding set members should have these different, + // and they will not have a network of 0. + // + + Status = RipInsertLocalNetwork( + Network, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if (Status == STATUS_SUCCESS) { + + Binding->LocalAddress.NetworkAddress = Network; + + // + // Update the device address if we have no virtual net + // and there is one binding (!Device->MultiCardZeroVirtual) + // or this is the first binding, which is the one we + // appear to be if a) we have no virtual net defined and + // b) we are bound to multiple cards. + // +#ifdef _PNP_POWER + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + if (!Device->VirtualNetwork) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Network; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Network)); + } + + } + } +#else + if ((!Device->VirtualNetwork) && + ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1))) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own + // headers. When we indicate a line up on NIC ID + // 0 it knows to requery the local address. + // + // BUGBUG: Line up indication to RIP/NB?? + // + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + + IPX_LINE_INFO LineInfo; + LineInfo.LinkSpeed = Device->LinkSpeed; + LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + LineInfo.MacOptions = Device->MacOptions; + + (*Device->UpperDrivers[IDENTIFIER_SPX].LineUpHandler)( + 0, + &LineInfo, + Binding->Adapter->MacInfo.RealMediumType, + NULL); + + } + } +#endif + } else if (Status == STATUS_DUPLICATE_NAME) { + + // + // If it was a duplicate we still set the binding's local + // address to the value so we can detect binding sets. + // + + Binding->LocalAddress.NetworkAddress = Network; + + } + + return Status; + +} /* IpxUpdateBindingNetwork */ + + +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + Packet - contains the packet received as well as some mediaspecific info. + +Return Value: + + return of IpxReceiveIndicationNew(), + +--*/ +{ + UINT HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet); + UINT firstbufferLength, bufferLength; + PNDIS_BUFFER pFirstBuffer; + PUCHAR headerBuffer; + NTSTATUS ntStatus; + INT tdiClientCount = 0; + + // + // Query the number of buffers, the first MDL's descriptor and the packet length + // + NdisGetFirstBufferFromPacket(Packet, // packet + &pFirstBuffer, // first buffer descriptor + &headerBuffer, // ptr to the start of packet + &firstbufferLength,// length of the header+lookahead + &bufferLength); // length of the bytes in the buffers + + // + // ReceiveContext is the packet itself + // + + ntStatus = IpxReceiveIndicationNew ( + ProtocolBindingContext, + Packet, // ReceiveContext + headerBuffer, + HeaderBufferSize, + headerBuffer + HeaderBufferSize, // LookaheadBuffer + bufferLength - HeaderBufferSize, // LookaheadBufferSize + bufferLength - HeaderBufferSize, // PacketSize - since the whole packet is indicated + pFirstBuffer, // pMdl + &tdiClientCount // tdi client count + ); + + IPX_DEBUG(RECEIVE, ("IpxReceivePacket: Tdi Client Count is: %lx\n", tdiClientCount)); + + return tdiClientCount; +} /* IpxReceivePacket */ + + +#ifdef _PNP_POWER + +#if defined(_M_IX86) +_inline +#endif +BOOLEAN +IpxNewVirtualNetwork( + IN PDEVICE Device, + IN BOOLEAN NewVirtualNetwork + ) +/*++ + +Routine Description: + + If the virtualnetwork number changed, this function records this fact + in the device. + + Called with the BINDACCESSLOCK held. +Arguments: + + Device - Pointer to the Device. + + NewVirtualNetwork - boolean to indicate if the virtual net# changed. + +Return Value: + + BOOLEAN - to indicate whether SPX's reserved address was changed. + +--*/ +{ + NTSTATUS ntStatus; + UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + BOOLEAN ReservedAddrChanged = FALSE; + + if (Device->VirtualNetworkNumber) { + + if (NewVirtualNetwork) { + // + // If a new one appeared. + // + + ntStatus = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + NIC_ID_TO_BINDING(Device, 1)->Adapter->NdisBindingHandle, + 1); + + if (ntStatus != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (ntStatus == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + // + // If the number is non-zero now, a new one appeared + // + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + ReservedAddrChanged = TRUE; + + // + // If RIP is not bound, then this node is a RipResponder + // + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + Device->RipResponder = TRUE; + } + } + + } else { +NoVirtualNetwork: + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (Device->ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + if (NewVirtualNetwork) { + // + // The virtual network number disappeared this time + // + + // + // Remove the prev. net # from the RIP tables here + // + RipAdjustForBindingChange (0, 0, IpxBindingDeleted); + + // + // If we were a RipResponder, we are not anymore + // + if (Device->RipResponder) { + Device->RipResponder = FALSE; + } + } + + // + // Since there is not virtual network number, SPX's reserved address is + // the address of the first binding. This could have changed because of + // several reasons: if there was a WAN binding only earlier and this time + // a LAN binding appeared, or if the first LAN binding disappeared. Instead + // of checking for all these conditions, check if the Device's sourceaddress + // and that of the first mis-match. + // NB uses the address of the first device always and hence does not need + // this mechanism to determine if this is a reserved address change. + // + if (!RtlEqualMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket))) { + + RtlCopyMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + ReservedAddrChanged = TRUE; + } + } + + return ReservedAddrChanged; +} + + +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about a new + adapter in the machine. We are called here only if this adapter + is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING + + BindContext - context to represent this bind indication + + DeviceName - Name of the adapter that appeared (e.g. \Device\Lance1) + + SystemSpecific1/2 - Not used here + +Return Value: + + Status - NDIS_STATUS_SUCCESS + +--*/ +{ + NTSTATUS ntStatus; + PDEVICE Device = IpxDevice; + PADAPTER Adapter = NULL; + CONFIG Config; + UINT i; + ULONG Temp, SuccessfulOpens=0; + PBINDING Binding; + BINDING_CONFIG ConfigBinding; + ULONG ValidBindings; + USHORT AutoDetectReject; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN FirstDevice = FALSE; + BOOLEAN ReservedAddrChanged = FALSE; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + ConfigBinding.AdapterName = *DeviceName; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the virtual network number changed, record this fact. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + Device->VirtualNetworkNumber = Temp; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // For each FrameType and Network Number configured, initialize the + // FrameType array in the CONFIG_BINDING + // + ntStatus = IpxPnPGetAdapterParameters( + &Config, + DeviceName, + &ConfigBinding); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the adapter params: DeviceName: %lx\n", DeviceName->Buffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + IPX_DEBUG(PNP, ("ConfigBinding.FrameTypeCount: %lx\n", ConfigBinding.FrameTypeCount)); + + // + // Reset the auto-detect state to init so that if a receive occurs on this binding + // before we can place this binding in the device's binding array, we know of it. + // + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + // + // Register adapter with NDIS; query the various parameters; get the WAN line count + // if this is a WAN adapter. + // Allocate the bindings corresponding to this adapter + // + for (i = 0; i < ConfigBinding.FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. [BUGBUGZZ] not right now + // Adapter is NULL first time and is allocated then. In subsequent calls, + // it is not NULL and the bindings are hooked to this adapter. + + ntStatus = IpxBindToAdapter (Device, &ConfigBinding, &Adapter, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (ntStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + // + // If the status is STATUS_NOT_SUPPORTED, then this frametype mapped to a previously + // initialized one. In this case, remove this index fron the FrameType array so that + // when we try to update the binding array, we dont have duplicates. + // + if (ntStatus == STATUS_NOT_SUPPORTED) { + ULONG j; + + // + // Remove this frametype from the FrameType array. + // + for (j = i+1; j < ConfigBinding.FrameTypeCount; j++) { + ConfigBinding.FrameType[j-1] = ConfigBinding.FrameType[j]; + } + + --ConfigBinding.FrameTypeCount; + + // + // Decrement so we see the one just moved up. + // + --i; + +#if DBG + for (j = 0; j < ISN_FRAME_TYPE_MAX; j++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + j, ConfigBinding.FrameType[j], ConfigBinding.NetworkNumber[j], ConfigBinding.AutoDetect[j])); + } +#endif + continue; + } + + if (ntStatus != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding.AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + CTEAssert(Adapter); + + ++SuccessfulOpens; + + // + // Even for WAN adapters, the FrameTypeCount is set to 4. We only need to + // allocate one binding for WAN; the others come later. + // + if (Adapter->MacInfo.MediumAsync) { + break; + } + } + + if (SuccessfulOpens == 0) { + goto InitFailed; + } + + // + // Place all the bindings corresponding to this adapter in the binding array + // Also resolve binding sets for non-autodetect bindings. + // + + // + // Obtain lock to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IpxPnPUpdateBindingArray (Device, Adapter, &ConfigBinding); + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // If at least one card appeared here, set our state + // to open + // + // [BUGBUGZZ]: what if all these bindings are eliminated - then + // the state is not open... + // + if (Device->ValidBindings > 0) { + if (Device->State == DEVICE_STATE_LOADED) { + FirstDevice = TRUE; + Device->State = DEVICE_STATE_OPEN; + } + } + + // + // We don't do auto-detect/bindingsets for WAN lines: skip over. + // + if (Adapter->MacInfo.MediumAsync) { + goto jump_wan; + } + + // + // Auto-detect the network number. Update the results for only the + // bindings corresponding to this adapter + // + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + // Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + ntStatus = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (ntStatus == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + // + // Obtain exclusive access to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Note, here we go thru' only the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + Binding = Adapter->Bindings[i]; + + // + // Skip empty binding slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + ntStatus = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((ntStatus != STATUS_SUCCESS) && + (ntStatus != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + // ValidBindings = Device->BindingCount; + + ValidBindings = Device->ValidBindings; + + // [BUGBUGZZ] if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, &LockHandle1, &Device->RegistryPath); + + //} + + // + // Adjust all the indices by the number of AutoDetect bindings thrown away + // + // AutoDetectReject = (USHORT)(Device->BindingCount - ValidBindings); + + AutoDetectReject = (USHORT)(Device->ValidBindings - ValidBindings); + + Device->HighestLanNicId -= AutoDetectReject; + Device->HighestExternalNicId -= AutoDetectReject; + Device->HighestType20NicId -= AutoDetectReject; + Device->SapNicCount -= AutoDetectReject; + + Device->ValidBindings = (USHORT)ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This updates the Device->HighestExternalNicId + // and Device->HighestType20NicId, SapNicCount, HighestLanNicId + // + + // + // Do this only for the auto-detect bindings + // [BUGBUGZZ] check this + // + + //if (Device->AutoDetect) { + IpxResolveBindingSets (Device, Device->HighestExternalNicId); + //} + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +jump_wan: + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); + + // + // Enable this regardless of whether any of our clients enabled b'cast. + // NB always enables it, so we are fine. + // + // Since we dont increment the Broadcast count in the device, we will disable b'casts + // correctly if the count drops to 0. + // + // If the ISN clients appear before the adapters, they increment the BCount, but + // since the ValidBindings is 0, all works. Then, when the adapters appear, we enable + // the broadcasts here. + // + // If the adapters appear before the ISN clients, then the broadcast is enabled on + // the adapters here and the adapter's flag is set to indicate this, which will prevent + // any further calls to NDIS when the ISN clients force an IpxAddBroadcast. + // + Device->EnableBroadcastPending = TRUE; + IpxBroadcastOperation((PVOID)TRUE); + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // This function updates flags like RipResponder, MultiCardZeroVirtual, etc. + // If the VirtualNetwork number changed (NewVirtualNetwork is TRUE), it updates + // the Device structure and the RIP tables accordingly. + // It returns a boolean to indicate if SPX's reserved address changed. + // + ReservedAddrChanged = IpxNewVirtualNetwork(Device, NewVirtualNetwork); + + // + // Update the values once the auto-detect bindings have been thrown away... + // + IpxPnPUpdateDevice(Device); + + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + if (FirstDevice) { + UNICODE_STRING devicename; + + // + // Inform TDI clients about the open of our device object. + // + devicename.MaximumLength = (USHORT)Device->DeviceNameLength; + devicename.Length = (USHORT)Device->DeviceNameLength - sizeof(WCHAR); + devicename.Buffer = Device->DeviceName; + + if ((ntStatus = TdiRegisterDeviceObject( + &devicename, + &Device->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterDeviceObject failed: %lx", ntStatus)); + } + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform NB and TDI of all the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + // + // If a NULL binding or a binding set slave, dont inform NB about it. + // + if (!Binding || (Binding->NicId > Device->HighestExternalNicId)) { +#if DBG + if (Binding) { + IPX_DEBUG(PNP, ("Binding: %lx, Binding set slave\n", Binding)); + } +#endif + continue; + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Lock taken to check the UpperDriverBound flag. + // We already have the BindAccessLock at this point. + // + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // Ensure that we dont do it twice. + // + if (!Binding->IsnInformed[IDENTIFIER_NB]) { + + // + // Also, to ensure that the indications are done in the right order, + // check if the first card has been indicated yet. + // + if ((Binding->NicId != 1) && + !NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_NB]) { + + break; + } + + Binding->IsnInformed[IDENTIFIER_NB] = TRUE; + + if (Binding->NicId == 1) { + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + IpxPnPInfo.FirstORLastDevice = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + } + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB add: %lx\n", Binding)); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // Always true for SPX + // + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + // + // Inform SPX - the network/node address is the Virtual one if it exists + // else the address of the first binding + // + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to SPX add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + + // + // Not the first device - inform if the reserved address changed. + // + if (ReservedAddrChanged) { + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + IPX_DEBUG(PNP, ("Reserved addr changed; SPX not told of first one yet\n")); + } + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("PnP to SPX add (res. addr change): %lx\n", Binding)); + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +InitFailed: + *Status = NDIS_STATUS_SUCCESS; + return; + +} /* IpxBindAdapter */ + + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about the removal + of an existing adapter from the machine. We are called here only if + this adapter is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING. + + ProtocolBindingContext - the adapter that got removed. + + UnbindContext - context to represent this bind indication. + +Return Value: + + Void - return thru' Status above. + +--*/ +{ + NTSTATUS ntStatus; + PADAPTER Adapter=(PADAPTER)ProtocolBindingContext; + CONFIG Config; + PBINDING Binding; + PDEVICE Device=IpxDevice; + ULONG i, Temp; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN NBReservedAddrChanged = FALSE; + BOOLEAN SPXInformed = FALSE; + IPX_PNP_INFO IpxPnPInfo; + PBINDING newMasterBinding; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the VirtualNetwork number changed, record it. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // If the WAN adapter disappeared, we can simply remove all the WAN bindings since + // all of them correspond to this single WAN adapter. Since we tell NB only about + // the first one of these, we need to indicate removal of only one binding to NB. + // + if (Adapter->MacInfo.MediumAsync) { + USHORT wanLineCount = (USHORT)Adapter->WanNicIdCount; + + CTEAssert(wanLineCount == (Device->HighestExternalNicId - Device->HighestLanNicId)); + + // + // If no more bindings remain, tell upper driver of the same. + // We go back to the loaded state. + // + + if ((Device->ValidBindings - wanLineCount) == 0) { + IpxPnPInfo.FirstORLastDevice = TRUE; + Device->State = DEVICE_STATE_LOADED; + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + + // + // DeRegister this address with the TDI clients. + // + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Give the PnP indication to indicate the deletion only if it was + // added before. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB delete: %lx\n", Binding)); + } +#if DBG + else { + DbgPrint("WAN adapter id: %lx not indicated to NB\n", Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Inform SPX only if this is the last device. + // + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (IpxPnPInfo.FirstORLastDevice && Binding->IsnInformed[IDENTIFIER_SPX]) { + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Device->HighestLanNicId == 0); + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + } + + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Now remove these WAN bindings from the array. Move all the Slave bindings + // up to where the WAN bindings were. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + for (i = Device->HighestLanNicId+1; i <= Device->HighestExternalNicId; i++) { + // + // Unbind from the adapter - if it is not referenced by any other thread, it will + // be deleted at this point. + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(NIC_ID_TO_BINDING_NO_ILOCK(Device, i)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // Move the slave binding here. + // + INSERT_BINDING(Device, i, NIC_ID_TO_BINDING_NO_ILOCK(Device, i+wanLineCount)); + } + + /* + RtlCopyMemory( Device->Bindings[Device->HighestLanNicId+1], + Device->Bindings[Device->HighestExternalNicId+1], + (Device->ValidBindings - Device->HighestExternalNicId) * sizeof(PBIND_ARRAY_ELEM)); + */ + + // + // Update the indices + // + Device->HighestExternalNicId -= wanLineCount; + Device->ValidBindings -= wanLineCount; + Device->BindingCount -= wanLineCount; + Device->SapNicCount = Device->HighestType20NicId = Device->HighestLanNicId; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + CTEAssert(Device->HighestLanNicId == Device->HighestExternalNicId); + + } else { + // + // LAN adapter disappeared. + // + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + // + // For each binding corresponding to this adapter, inform NB only + // if the binding addition was indicated. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + if (!Binding) { + continue; + } + + // + // We cannot receive on this binding anymore + // + Adapter->Bindings[i] = NULL; + + // + // If this was a slave binding, dont inform of the deletion. + // Just remove the binding from the binding array and the bindingset list. + // + + if (Binding->NicId > Device->HighestExternalNicId) { + PBINDING MasterBinding, tempBinding; + + CTEAssert(Binding->BindingSetMember); + CTEAssert(Binding->CurrentSendBinding == NULL); + + // + // Traverse the bindingset list and remove this binding from there. + // + tempBinding = MasterBinding = Binding->MasterBinding; + + while (tempBinding->NextBinding != MasterBinding) { + if (tempBinding->NextBinding == Binding) { + tempBinding->NextBinding = tempBinding->NextBinding->NextBinding; + break; + } + tempBinding = tempBinding->NextBinding; + } + + // + // If no more slaves, this is no longer a bindingset. + // + if (MasterBinding->NextBinding == MasterBinding) { + MasterBinding->BindingSetMember = FALSE; + MasterBinding->CurrentSendBinding = NULL; + MasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Slave binding: %lx removed, no master: %lx\n", Binding, MasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", Binding->NicId, MasterBinding->NicId)); + + // + // Null out the Slave binding. + // + INSERT_BINDING(Device, Binding->NicId, NULL); + + --Device->ValidBindings; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + + continue; + } + + // + // If this was the last binding, go back to loaded state and shut down the RIP timers. + // + if (Device->ValidBindings == 1) { + CTEAssert(Device->HighestExternalNicId == 1); + CTEAssert(Device->HighestLanNicId == 1); + CTEAssert(Device->SapNicCount == 1); + CTEAssert(Device->HighestType20NicId == 1); + + Device->State = DEVICE_STATE_LOADED; + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // If this was a master binding, promote a slave binding to master. + // + if (Binding->BindingSetMember) { + + CTEAssert(Binding->CurrentSendBinding); + CTEAssert(Binding->MasterBinding == Binding); + + // + // Promote the next slave to Master. + // + newMasterBinding = Binding->NextBinding; + INSERT_BINDING(Device, Binding->NicId, newMasterBinding); + newMasterBinding->CurrentSendBinding = newMasterBinding; + newMasterBinding->MasterBinding = newMasterBinding; + + // + // If this is the only binding remaining out of its set, + // it is no longer part of a set. + // + if (newMasterBinding->NextBinding == Binding) { + newMasterBinding->NextBinding = newMasterBinding->CurrentSendBinding = NULL; + newMasterBinding->BindingSetMember = FALSE; + newMasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Master binding: %lx removed, no master: %lx\n", Binding, newMasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (newMasterBinding->NicId, Binding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", newMasterBinding->NicId, Binding->NicId)); + + // + // Register slave's address with the TDI clients. + // + CTEAssert(!newMasterBinding->TdiRegistrationHandle); + + RtlCopyMemory ( Device->TdiRegistrationAddress->Address, + &newMasterBinding->LocalAddress, + sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &newMasterBinding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Null out the slave binding + // + INSERT_BINDING(Device, newMasterBinding->NicId, NULL); + + newMasterBinding->NicId = Binding->NicId; + + IPX_DEBUG(PNP, ("Promoted a master binding: %lx, old master: %lx\n", newMasterBinding, Binding)); + } else { + + ULONG j; + + // + // Remove the binding from the array + // + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + for (j = Binding->NicId+1; j <= Device->HighestExternalNicId; j++) { + INSERT_BINDING(Device, j-1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + --NIC_ID_TO_BINDING_NO_ILOCK(Device, j)->NicId; + } + + INSERT_BINDING(Device, Device->HighestExternalNicId, NULL); + + --Device->HighestExternalNicId; + --Device->HighestLanNicId; + --Device->HighestType20NicId; + --Device->SapNicCount; + } + + --Device->ValidBindings; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + // + // If this is the first binding, NB's reserved will change. + // When we inform SPX of an address change later, we dont have + // this binding to know if this binding was indicated to SPX earlier. + // So, set SPXInformed, which is used later to determine if an address + // change is to be indicated to SPX later. + // + // Since NB is informed of all adapters, we inform of the reserved address + // change to NB if the new Binding (now at NicId 1) was indicated earlier. + // + if (Binding->NicId == 1) { + NBReservedAddrChanged = TRUE; + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + SPXInformed = TRUE; + } + } + + CTEAssert(Binding->TdiRegistrationHandle); + + // + // DeRegister this address with the TDI clients. + // + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate its deletion to NB. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete LAN device: %lx\n", Binding)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // If this was a Master, indicate the addition of the (promoted) slave + // + if (Binding->BindingSetMember) { + IpxPnPInfo.NetworkAddress = newMasterBinding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, newMasterBinding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, newMasterBinding->NicId); + + // + // In this case, we set the ReservedAddrChanged bit here itself so dont need + // to indicate a separate address changed. + // + IpxPnPInfo.NewReservedAddress = (NBReservedAddrChanged) ? TRUE : FALSE; + NBReservedAddrChanged = FALSE; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: add slave device: NicId: %lx\n", Binding->NicId)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + newMasterBinding->IsnInformed[IDENTIFIER_NB] = TRUE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Last device - inform SPX if it is bound and this device was added earlier. + // + if (IpxPnPInfo.FirstORLastDevice) { + IPX_DEBUG(PNP, ("Last device - inform SPX\n")); + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: last LAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + // + // Unbind from the adapter so it can be deleted + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + + // + // Update the Device and RIP tables if this is not the last device. + // If the reserved address changed, inform NB and SPX of this change. + // + if (!IpxPnPInfo.FirstORLastDevice) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1); + + if (IpxNewVirtualNetwork(Device, NewVirtualNetwork)) { + + IPX_DEBUG(PNP, ("SPX's reserved address changed\n")); + + // + // SPX's reserved address changed + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // If this binding's addition was indicated earlier, indicate change of address. + // + if (SPXInformed) { + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + + IPX_DEBUG(PNP, ("Inform SPX: reserved address changed\n")); + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } else { + + // + // Set the first binding's flag so that when this binding goes away, we remember + // to inform SPX of this device's removal. + // + + IPX_DEBUG(PNP, ("Transfer SPX informed flag to NicId: %lx\n", Binding->NicId)); + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + } + + if (NBReservedAddrChanged) { + // + // NB's reserved address changed. + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate the change of reserved address. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: reserved address changed\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + // + // Re-calculate the values of datagram sizes in the Device. + // + IpxPnPUpdateDevice(Device); + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); +} /* IpxUnbindAdapter */ + + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + ReceivedPacket - The packet received + + MediaSpecificInformation - Used for media such as Irda, wireless, etc. Not used here. + + HeaderBufferSize - Size of the MAC header + +Return Value: + + return of IpxReceiveIndicationNew(), + +--*/ +{ +} /* IpxTranslate */ + +#endif _PNP_POWER + + diff --git a/private/ntos/tdi/isnp/ipx/internal.c b/private/ntos/tdi/isnp/ipx/internal.c new file mode 100644 index 000000000..f11790158 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/internal.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + internal.c + +Abstract: + + This module contains the code to handle the internal + binding of the upper drivers to IPX. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 25-August-1995 + Bug Fixes - tagged [SA] +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to bind to IPX. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + PIPX_INTERNAL_BIND_INPUT BindInput; + PIPX_INTERNAL_BIND_OUTPUT BindOutput; + PIPX_INTERNAL_BIND_RIP_OUTPUT BindRipOutput; + CTELockHandle LockHandle; + PIPX_NIC_DATA NicData; + PBINDING Binding, LastRealBinding; + PADAPTER Adapter; + ULONG Identifier; + ULONG BindOutputSize; + BOOLEAN BroadcastEnable; + UINT i; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(IPX_INTERNAL_BIND_INPUT) - sizeof(ULONG))) { + + IPX_DEBUG (BIND, ("Bind received, bad input length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.InputBufferLength, + sizeof (IPX_INTERNAL_BIND_INPUT))); + return STATUS_INVALID_PARAMETER; + + } + + BindInput = (PIPX_INTERNAL_BIND_INPUT)(Irp->AssociatedIrp.SystemBuffer); + + if (BindInput->Identifier >= UPPER_DRIVER_COUNT) { + IPX_DEBUG (BIND, ("Bind received, bad id %d\n", BindInput->Identifier)); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (BIND, ("Bind received from id %d (%s)\n", + BindInput->Identifier, + IdStrings[BindInput->Identifier])); + +#ifdef _PNP_POWER +// RIP gives us version == 1 whereas the others give us 2 (ISN_VERSION). +// [BUGBUGZZ] - have RIP change? +// + if (BindInput->Identifier == IDENTIFIER_RIP) { + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } else { + if (BindInput->Version != ISN_VERSION) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } + +#else + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } +#endif + + if (BindInput->Identifier != IDENTIFIER_RIP) { + BindOutputSize = sizeof(IPX_INTERNAL_BIND_OUTPUT); + } else { + BindOutputSize = FIELD_OFFSET (IPX_INTERNAL_BIND_RIP_OUTPUT, NicInfoBuffer.NicData[0]) + + (MIN (Device->MaxBindings, Device->HighestExternalNicId) * sizeof(IPX_NIC_DATA)); + } + + Irp->IoStatus.Information = BindOutputSize; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + BindOutputSize) { + + IPX_DEBUG (BIND, ("Bind: bad output length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.OutputBufferLength, + BindOutputSize)); + + // + // Fail this request with BUFFER_TOO_SMALL. Since the + // I/O system may not copy the status block back to + // the user's status block, do that here so that + // he gets IoStatus.Information. + // + + try { + *Irp->UserIosb = Irp->IoStatus; + } except(EXCEPTION_EXECUTE_HANDLER) { + NOTHING; + } + + return STATUS_BUFFER_TOO_SMALL; + } + + // + // We have verified the length, make sure we are not + // already bound. + // + + Identifier = BindInput->Identifier; + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[Identifier]) { + IPX_DEBUG (BIND, ("Bind: already bound\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return STATUS_REQUEST_NOT_ACCEPTED; + } + + { + LARGE_INTEGER ControlChId; + + CCID_FROM_REQUEST(ControlChId, Irp); + + IPX_DEBUG (BIND, ("Control ChId: (%d, %d) for Id: %d\n", ControlChId.HighPart, ControlChId.LowPart, Identifier)); + Device->UpperDriverControlChannel[Identifier].QuadPart = ControlChId.QuadPart; + } + + RtlCopyMemory( + &Device->UpperDrivers[Identifier], + BindInput, + sizeof (IPX_INTERNAL_BIND_INPUT) + ); + + BroadcastEnable = BindInput->BroadcastEnable; + + // + // Now construct the output buffer. + // + + if (Identifier != IDENTIFIER_RIP) { + + BindOutput = (PIPX_INTERNAL_BIND_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindOutput->Version = 1; + + // + // Tell netbios our first binding's net/node instead of the + // virtual one. + // +#ifdef _PNP_POWER +// +// Fill the fields in only if the adapters have already appeared +// Else, set NodeNumber to 0 so NB/SPX know of it. +// + if ((*(UNALIGNED USHORT *)(Device->SourceAddress.NodeAddress+4) != 0) || + (*(UNALIGNED ULONG *)Device->SourceAddress.NodeAddress != 0)) { + + IPX_DEBUG(BIND, ("Device already opened\n")); + CTEAssert(Device->ValidBindings); + + if (Identifier == IDENTIFIER_SPX) { + + // + // For SPX, inform directly. + // + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IpxPnPIsnIndicate((PVOID)Identifier); + + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + } else { + // + // For NB, queue a work item which will go thru' the adapters list and + // inform the upper drivers about each of them. + // + ExInitializeWorkItem( + &Device->PnPIndicationsQueueItem, + IpxPnPIsnIndicate, + (PVOID)Identifier); + ExQueueWorkItem(&Device->PnPIndicationsQueueItem, DelayedWorkQueue); + } + + } else { + IPX_DEBUG(BIND, ("Device not open\n")); + *((UNALIGNED ULONG *)BindOutput->Node) = 0; + *((UNALIGNED USHORT *)(BindOutput->Node+4)) = 0; + RtlZeroMemory(&BindOutput->LineInfo, sizeof(BindOutput->LineInfo)); + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindOutput->IncludedHeaderOffset = MAC_HEADER_SIZE; // (USHORT)Device->IncludedHeaderOffset; + + BindOutput->SendHandler = IpxSendFramePreFwd; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; + +#else + if ((Identifier == IDENTIFIER_NB) && + (Device->VirtualNetwork)) { + RtlCopyMemory(BindOutput->Node, Device->Bindings[1]->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->Bindings[1]->LocalAddress.NetworkAddress; + } else { + + RtlCopyMemory(BindOutput->Node, Device->SourceAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->SourceAddress.NetworkAddress; + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + + BindOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindOutput->LineInfo.LinkSpeed = Device->LinkSpeed; + BindOutput->LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + BindOutput->LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + BindOutput->LineInfo.MacOptions = Device->MacOptions; + + BindOutput->SendHandler = IpxSendFrame; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; +#endif + BindOutput->TransferDataHandler = IpxTransferData; + } else { + // + // Set this so we stop RIPping for our virtual network (if + // we have one). + // + + Device->RipResponder = FALSE; + + // + // See if he wants a single wan network number. + // + + if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IPX_INTERNAL_BIND_INPUT)) || + ((BindInput->RipParameters & IPX_RIP_PARAM_GLOBAL_NETWORK) == 0)) { + + Device->WanGlobalNetworkNumber = FALSE; + Device->SapNicCount = Device->HighestExternalNicId; + + } else { + + Device->WanGlobalNetworkNumber = TRUE; + + } + + BindRipOutput = (PIPX_INTERNAL_BIND_RIP_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindRipOutput->Version = 1; + BindRipOutput->MaximumNicCount = MIN (Device->MaxBindings, Device->HighestExternalNicId) + 1; + + BindRipOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindRipOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindRipOutput->SendHandler = IpxSendFrame; + + BindRipOutput->SegmentCount = Device->SegmentCount; + BindRipOutput->SegmentLocks = Device->SegmentLocks; + + BindRipOutput->GetSegmentHandler = RipGetSegment; + BindRipOutput->GetRouteHandler = RipGetRoute; + BindRipOutput->AddRouteHandler = RipAddRoute; + BindRipOutput->DeleteRouteHandler = RipDeleteRoute; + BindRipOutput->GetFirstRouteHandler = RipGetFirstRoute; + BindRipOutput->GetNextRouteHandler = RipGetNextRoute; + + BindRipOutput->IncrementWanInactivityHandler = IpxInternalIncrementWanInactivity; + BindRipOutput->QueryWanInactivityHandler = IpxInternalQueryWanInactivity; + + BindRipOutput->TransferDataHandler = IpxTransferData; + + BindRipOutput->NicInfoBuffer.NicCount = (USHORT)MIN (Device->MaxBindings, Device->HighestExternalNicId); + BindRipOutput->NicInfoBuffer.VirtualNicId = 0; + if (Device->VirtualNetwork || Device->MultiCardZeroVirtual) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = Device->SourceAddress.NetworkAddress; + } else if (Device->DedicatedRouter) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = 0x0; + } + + NicData = &BindRipOutput->NicInfoBuffer.NicData[0]; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + // + // NULL bindings are WAN bindings, so we return the + // information from the last non-NULL binding found, + // which will be the first one on this adapter. + // Otherwise we save this as the last non-NULL one. + // + + if (Binding == NULL) { + Binding = LastRealBinding; + } else { + LastRealBinding = Binding; + } + + Adapter = Binding->Adapter; + NicData->NicId = i; + RtlCopyMemory (NicData->Node, Binding->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)NicData->Network = Binding->LocalAddress.NetworkAddress; + NicData->LineInfo.LinkSpeed = Binding->MediumSpeed; + NicData->LineInfo.MaximumPacketSize = + Binding->MaxLookaheadData + sizeof(IPX_HEADER); + NicData->LineInfo.MaximumSendSize = + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + NicData->LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + NicData->DeviceType = Adapter->MacInfo.RealMediumType; + NicData->EnableWanRouter = Adapter->EnableWanRouter; + + ++NicData; + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + if (BroadcastEnable) { + IpxAddBroadcast (Device); + } + + Device->UpperDriverBound[Identifier] = TRUE; + Device->AnyUpperDriverBound = TRUE; + CTEFreeLock (&Device->Lock, LockHandle); + + return STATUS_SUCCESS; + +} /* IpxInternalBind */ + + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to unbind from IPX. It does this by closing the + control channel on which the bind ioctl was submitted. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + + IPX_DEBUG (BIND, ("Unbind received from id %d (%s)\n", + Identifier, + IdStrings[Identifier])); + + CTEGetLock (&Device->Lock, &LockHandle); + + if (!Device->UpperDriverBound[Identifier]) { + CTEFreeLock (&Device->Lock, LockHandle); + IPX_DEBUG (BIND, ("No existing binding\n")); + return STATUS_SUCCESS; + } + + Device->UpperDriverBound[Identifier] = FALSE; + Device->AnyUpperDriverBound = (BOOLEAN) + (Device->UpperDriverBound[IDENTIFIER_RIP] || + Device->UpperDriverBound[IDENTIFIER_SPX] || + Device->UpperDriverBound[IDENTIFIER_NB]); + + if (Device->UpperDrivers[Identifier].BroadcastEnable) { + IpxRemoveBroadcast (Device); + } + +#ifdef _PNP_POWER + if (Device->ValidBindings > 0) { + // + // If SPX went away, reset the IsnIndicate flag in the first binding + // + if (Identifier == IDENTIFIER_SPX) { + CTEAssert(NIC_ID_TO_BINDING(Device, 1)); + + if (NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("SPX unbound: IsnInformed turned off\n")); + } + } + + // + // If NB went away, reset all the Binding's flags + // + if (Identifier == IDENTIFIER_SPX) { + PBINDING Binding; + UINT i; + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i < Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + if (Binding && Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("NB unbound: IsnInformed off for NicId: %lx\n", i)); + } + } + } + } +#endif + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // BUGBUG: Ensure that no calls are made to bogus + // handlers. + // + + return STATUS_SUCCESS; + +} /* IpxInternalUnbind */ + + +VOID +IpxInternalFindRoute ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to submit + requests to find a remote network, which is contained in + FindRouteRequest->Network. FindRouteRequest->Identifier must + contain the identifier of the upper driver. + + This request is always asynchronous and is completed by + a call to the FindRouteComplete handler of the upper driver. + + NOTE: As a currently unspecified extension to this call, + we returns the tick and hop counts as two USHORTs in the + PVOID Reserved2 structure of the request. + +Arguments: + + FindRouteRequest - Describes the request and contains + storage for IPX to use while processing it. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + ULONG Segment; + TDI_ADDRESS_IPX TempAddress; + PBINDING Binding, MasterBinding; + NTSTATUS Status; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // First see if we have a route to this network in our + // table. + // + + TempAddress.NetworkAddress = *(UNALIGNED ULONG *)(FindRouteRequest->Network); + // + // [SA] Bug #15094 Copy over the Node address so it can be used in WAN cases + // + + // RtlZeroMemory (TempAddress.NodeAddress, 6); + + *((UNALIGNED ULONG *)TempAddress.NodeAddress) = *((UNALIGNED ULONG *)FindRouteRequest->Node); + *((UNALIGNED USHORT *)(TempAddress.NodeAddress+4)) = *((UNALIGNED USHORT *)(FindRouteRequest->Node+4)); + + Segment = RipGetSegment(FindRouteRequest->Network); +#ifdef _PNP_POWER + // + // Since we maintain the order of locks as Bind > Device > RIP table + // Get the lock up-front. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + IPX_BEGIN_SYNC (&SyncContext); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + CTEAssert ((sizeof(USHORT)*2) <= sizeof(PVOID)); + + Status = RipGetLocalTarget( + Segment, + &TempAddress, + FindRouteRequest->Type, + &FindRouteRequest->LocalTarget, + (PUSHORT)&FindRouteRequest->Reserved2); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this find route request for completion when the + // RIP response arrives. + // + + CTEAssert (FindRouteRequest->Type != IPX_FIND_ROUTE_NO_RIP); // should never pend + + InsertTailList( + &Device->Segments[Segment].FindWaitingForRoute, + &FindRouteRequest->Linkage); + + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + if (Status != STATUS_PENDING) { + + if (Status == STATUS_SUCCESS && FindRouteRequest->LocalTarget.NicId) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &FindRouteRequest->LocalTarget.NicHandle); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&FindRouteRequest->LocalTarget, Binding->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[FindRouteRequest->LocalTarget.NicId]; + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FindRouteRequest->LocalTarget.NicId = Binding->NicId; + + } +#endif + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + (BOOLEAN)((Status == STATUS_SUCCESS) ? TRUE : FALSE)); + + } + +} /* IpxInternalFindRoute */ + + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to query + information from us. + +Arguments: + + InternalQueryType - Identifies the type of the query. + + NicId - The ID to query, if needed + + Buffer - Input or output buffer for the query. + + BufferLength - The length of the buffer. + + BufferLengthNeeded - If the buffer is too short, this returns + the length needed. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding; + BOOLEAN BindingNeeded; + ULONG LengthNeeded; + PIPX_LINE_INFO LineInfo; + PUSHORT MaximumNicId; + PULONG ReceiveBufferSpace; + TDI_ADDRESS_IPX UNALIGNED * IpxAddress; + IPX_SOURCE_ROUTING_INFO UNALIGNED * SourceRoutingInfo; + ULONG SourceRoutingLength; + UINT MaxUserData; + PDEVICE Device = IpxDevice; +#ifdef _PNP_POWER + USHORT NicId = NicHandle->NicId; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // First verify the parameters. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_LINE_INFO); + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + BindingNeeded = FALSE; + LengthNeeded = sizeof(USHORT); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + BindingNeeded = FALSE; // for now we don't need it + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(ULONG); + break; + + case IPX_QUERY_IPX_ADDRESS: + + if ((NicId == 0) && + (BufferLength >= sizeof(TDI_ADDRESS_IPX))) { + + RtlCopyMemory (Buffer, &Device->SourceAddress, sizeof(TDI_ADDRESS_IPX)); + return STATUS_SUCCESS; + + } + + BindingNeeded = TRUE; + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_SOURCE_ROUTING_INFO); + break; + +#ifdef _PNP_POWER + // + // These are moved down from NB/SPX to IPX. LengthNeeded is set to 0 + // so we dont return BUFFER_TOO_SMALL here; we assume here that + // Bufferlength is also 0. + // Buffer is actually the IRP here. + // + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + + BindingNeeded = FALSE; + LengthNeeded = 0; + break; +#endif + default: + + return STATUS_NOT_SUPPORTED; + + } + + + if (LengthNeeded > BufferLength) { + if (BufferLengthNeeded != NULL) { + *BufferLengthNeeded = LengthNeeded; + } + return STATUS_BUFFER_TOO_SMALL; + } + + if (BindingNeeded) { + + if (NicId == 0) { + NicId = 1; + } + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding == NULL) || + (!Binding->LineUp)) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + return STATUS_INVALID_PARAMETER; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = IpxDevice->Bindings[NicId]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + return STATUS_INVALID_PARAMETER; + } +#endif + } + + + // + // Now return the data. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + LineInfo = (PIPX_LINE_INFO)Buffer; + LineInfo->LinkSpeed = Binding->MediumSpeed; + LineInfo->MaximumPacketSize = Binding->MaxLookaheadData + sizeof(IPX_HEADER); + LineInfo->MaximumSendSize = Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + LineInfo->MacOptions = Binding->Adapter->MacInfo.MacOptions; + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestExternalNicId); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + IpxAddress = (TDI_ADDRESS_IPX UNALIGNED *)Buffer; + if (!IpxIsAddressLocal(IpxAddress)) { + return STATUS_NO_SUCH_DEVICE; + } + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + ReceiveBufferSpace = (PULONG)Buffer; + *ReceiveBufferSpace = Binding->Adapter->ReceiveBufferSpace; + break; + + case IPX_QUERY_IPX_ADDRESS: + + RtlCopyMemory (Buffer, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + SourceRoutingInfo = (IPX_SOURCE_ROUTING_INFO UNALIGNED *)Buffer; + + MacLookupSourceRouting( + SourceRoutingInfo->Identifier, + Binding, + SourceRoutingInfo->RemoteAddress, + SourceRoutingInfo->SourceRouting, + &SourceRoutingLength); + + // + // Reverse the direction of the source routing since it + // is returned in the outgoing order. + // + + if (SourceRoutingLength > 0) { + SourceRoutingInfo->SourceRouting[0] &= 0x7f; + } + SourceRoutingInfo->SourceRoutingLength = (USHORT)SourceRoutingLength; + + MacReturnMaxDataSize( + &Binding->Adapter->MacInfo, + SourceRoutingInfo->SourceRouting, + SourceRoutingLength, + Binding->MaxSendPacketSize, + &MaxUserData); + + // + // MaxUserData does not include the MAC header but does include + // any extra 802.2 etc. headers, so we adjust for that to get the + // size starting at the IPX header. + // + + SourceRoutingInfo->MaximumSendSize = + MaxUserData - + (Binding->DefHeaderSize - Binding->Adapter->MacInfo.MinHeaderLength); + + break; + + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestType20NicId); + break; + +#ifdef _PNP_POWER + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + // + // Call the TDI query equivalent here. + // + return IpxTdiQueryInformation(Device, (PREQUEST)Buffer); + +#endif + } + +#ifdef _PNP_POWER + // + // If Binding was needed earlier, it was referenced, deref it now. + // + if (BindingNeeded) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + + // + // If we haven't returned failure by now, succeed. + // + + return STATUS_SUCCESS; + +} /* IpxInternalQuery */ + + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER +// RIP not converted yet... +// + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to increment + the inactivity counter on a wan binding. This is done every + minute. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + None. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // + // [BUGBUGZZ] Change to NIC_HANDLE_TO_BINDING later. Not done yet since RIP not changed to + // use NICHANDLE instead of NicId + // + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } +#endif + +} /* IpxInternalIncrementWanInactivity */ + + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to query + the inactivity counter on a wan binding. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + The inactivity counter for this binding. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // Binding = NIC_HANDLE_TO_BINDING(IpxDevice, &NicHandle); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + return Binding->WanInactivityCounter; + + } else { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + CTEAssert (FALSE); + return 0; + + } + +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + return Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + return 0; + + } +#endif + +} /* IpxInternalQueryWanInactivity */ + +#ifdef _PNP_POWER + +VOID +IpxPnPIsnIndicate( + IN PVOID Param +) + +/*++ + +Routine Description: + + This routine goes through the list of adapters and informs (thru' PnP indications) + the ISN drivers bound to IPX about any new adapters that have appeared before the + bind took place. + + This is queued as a work item in the InternalBind routine. + +Arguments: + + Param - the upper driver identifier. + +Return Value: + + None. + +--*/ +{ + ULONG Identifier = (ULONG)Param; + PDEVICE Device=IpxDevice; + ULONG i; + PBINDING Binding; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + // + // Set up the LineInfo struct. + // + + // + // BUGBUG: Do we give Binding-specific information here? + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + switch(Identifier) { + case IDENTIFIER_NB: + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform about all the adapters + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + + if (!Binding) { + continue; + } + + // + // We could have informed the upper driver from IpxBindAdapter + // + if (!Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = TRUE; + + // + // Inform NB - the reserved network/node address is always that of the first + // binding + // + if (i==1) { + IpxPnPInfo.FirstORLastDevice = TRUE; + IpxPnPInfo.NewReservedAddress = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, (USHORT)i); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to NB add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + break; + + case IDENTIFIER_SPX: + // + // For SPX this is called directly, with the IsnInformed flag appropriately set. + // This is done so that the IsnInformed flag cannot be changed under + // us by the BindAdapter routine. + // +#if 0 + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; +#endif + IpxPnPInfo.FirstORLastDevice = TRUE; + // + // Inform of the reserved address only + // + if (Device->VirtualNetwork) { + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + IpxPnPInfo.NetworkAddress = NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IpxPnPInfo.NewReservedAddress = TRUE; + + // IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to SPX add: %lx\n", NIC_ID_TO_BINDING(Device, 1))); +#if 0 + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + + } +} +#endif diff --git a/private/ntos/tdi/isnp/ipx/ipxprocs.h b/private/ntos/tdi/isnp/ipx/ipxprocs.h new file mode 100644 index 000000000..abfcf6ec3 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ipxprocs.h @@ -0,0 +1,1525 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxprocs.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + 1. Added new functions - IpxReceivePacket, IpxReceiveIndicationNew + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define IpxPrint0(fmt) DbgPrint(fmt) +#define IpxPrint1(fmt,v0) DbgPrint(fmt,v0) +#define IpxPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define IpxPrint0(fmt) +#define IpxPrint1(fmt,v0) +#define IpxPrint2(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define IPX_PACKET_LOG 1 +#endif + +#ifdef IPX_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _IPX_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER IpxHeader; + UCHAR Data[14]; +} IPX_PACKET_LOG_ENTRY, *PIPX_PACKET_LOG_ENTRY; + +#define IPX_PACKET_LOG_LENGTH 128 +extern ULONG IpxPacketLogDebug; +extern USHORT IpxPacketLogSocket; +EXTERNAL_LOCK(IpxPacketLogLock); +extern IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd; + +// +// Bit fields in IpxPacketLogDebug +// + +#define IPX_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to IpxPacketLogSocket +#define IPX_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-IPX) + +#define IPX_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from IpxPacketLogSocket + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (IpxPacketLogDebug & (_Bit)) + +#else // IPX_PACKET_LOG + +#define IpxLogPacket(_MacHeader,_Length,_IpxHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // IPX_PACKET_LOG + +#ifdef _PNP_POWER +// +// In load-only PnP, references are not needed on adapters. This should be changed +// to actually take the reference post 4.0. +// +// BUGBUG: Revisit Post 4.0 - Keep the actual instructions around for ease of activation later. +// +#define IpxReferenceAdapter(_adapter) + // InterlockedIncrement(&(_adapter)->ReferenceCount) + +#define IpxDereferenceAdapter(_adapter) +/* + if (InterlockedDecrement(&(_adapter)->ReferenceCount) == 0) {\ + IpxCloseNdis(_adapter); \ + IpxDestroyAdapter(_adapter);\ + }\ +*/ +#endif + +// +// In load-only PnP case, we dont need the references on bindings. All such references +// have been changed to this macro. +// +#define IpxReferenceBinding1(_Binding, _Type) + +#define IpxDereferenceBinding1(_Binding, _Type) + +#if DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefBinding (_Binding) + +#define IpxDereferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefDevice (_Device) + +#define IpxDereferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefDevice (_Device) + + +#define IpxReferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddress (_Address) + +#define IpxReferenceAddressLock(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressLock (_Address) + +#define IpxDereferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressSync (_Address) + + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFile (_AddressFile) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileLock (_AddressFile) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileSync (_AddressFile) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFile (_AddressFile) + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFileSync (_AddressFile) + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &IpxGlobalInterlock); \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &IpxGlobalInterlock); + +#else // DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + InterlockedIncrement(&(_Binding)->ReferenceCount) + +#define IpxDereferenceBinding(_Binding, _Type) \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define IpxDereferenceDevice(_Device, _Type) \ + IpxDerefDevice (_Device) + +#define IpxReferenceAddress(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxDereferenceAddress(_Address, _Type) \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + IpxDerefAddressSync (_Address) + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG( \ + &(_AddressFile)->ReferenceCount, \ + 1, \ + (_AddressFile)->AddressLock) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// This routine compares two node addresses. +// + +#define IPX_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define IPX_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + +// +// This routine does an ordered compare of two node addresses. It +// can handle the first address having the source-routing bit on. +// + +#define IPX_NODE_COMPARE(_A,_B,_R) \ + if ((*(_R) = (*(UNALIGNED SHORT *)(((PUCHAR)(_A))+4) - *(UNALIGNED SHORT *)(((PUCHAR)(_B))+4))) == 0) { \ + *(_R) = ((*(UNALIGNED LONG *)((PUCHAR)(_A)) & 0xffffff7f) - *(UNALIGNED LONG *)((PUCHAR)(_B))); \ + } + + + +// +// Routines in action.c +// + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ); + + +// +// Routines in adapter.c +// + +VOID +IpxRefBinding( + IN PBINDING Binding + ); + +VOID +IpxDerefBinding( + IN PBINDING Binding + ); + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ); + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ); + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ); + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ); + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ); +#endif + +// +// Routines in address.c +// + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ); + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ); + +#else + +#define IpxBuildTdiAddress(_AddressBuffer,_Network,_Node,_Socket) { \ + TA_IPX_ADDRESS UNALIGNED * _IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)(_AddressBuffer); \ + _IpxAddress->TAAddressCount = 1; \ + _IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); \ + _IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; \ + _IpxAddress->Address[0].Address[0].NetworkAddress = (_Network); \ + _IpxAddress->Address[0].Address[0].Socket = (_Socket); \ + RtlCopyMemory(_IpxAddress->Address[0].Address[0].NodeAddress, (_Node), 6); \ +} + +#endif + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ); + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +IpxRefAddress( + IN PADDRESS Address + ); + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +IpxDerefAddress( + IN PADDRESS Address + ); + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ); + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +#endif + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ); + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in device.c +// + +VOID +IpxRefDevice( + IN PDEVICE Device + ); + +VOID +IpxDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ); + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ); +#endif + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +#ifdef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN CTELockHandle *LockHandle1, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN PADAPTER *AdapterPtr, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxPnPToLoad(); + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ); + +#endif _PNP_POWER + +// +// Routines in event.c +// + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ); + + +// +// Routines in ind.c +// + +// +// [CH] Added these two functions +// +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ); + +NDIS_STATUS +IpxReceiveIndicationNew( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ); + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ); + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ); + + +// +// Routines in internal.c +// + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ); + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ); + +VOID +IpxInternalFindRoute( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ); + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +#ifdef _PNP_POWER +VOID +IpxPnPIsnIndicate( + IN PVOID Param +); +#endif + +// +// Routines in ndis.c +// + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ); + +VOID +IpxDeregisterProtocol( + VOID + ); + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ); + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ); + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ); + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ); + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ); + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ); + + +#ifdef _PNP_POWER +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ); + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ); + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ); +#endif // _PNP_POWER + +// +// Routines in mac.c +// + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ); + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ); + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ); + +#ifdef _PNP_POWER +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); +#endif + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ); + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR NextRouter[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ); + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ); + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ); + + +// +// Routines in packet.c +// + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); +#endif + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); +#endif + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ); + +#if BACK_FILL +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ); +#endif + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ); + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ); + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + IN PDEVICE Device + ); + +#if BACK_FILL +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + IN PDEVICE Device + ); +#endif + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ); + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ); + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ); + + + +// +// Routines in query.c +// + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in receive.c +// + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ); + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ); + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in rip.c +// + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ); + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ); + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ); + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ); + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ); + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ); + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ); + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ); + +UINT +RipGetSegment( + IN UCHAR Network[4] + ); + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ); + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ); + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ); + +VOID +RipDropRemoteEntries( + VOID + ); + + +// +// Routines in send.c +// + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ); + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ); +#else +#define IpxConstructHeader(_Header,_PacketLength,_PacketType,_RemoteAddress,_LocalAddress) { \ + PIPX_HEADER _IpxHeader = (PIPX_HEADER)(_Header); \ + _IpxHeader->CheckSum = 0xffff; \ + _IpxHeader->PacketLength[0] = (UCHAR)((_PacketLength) / 256); \ + _IpxHeader->PacketLength[1] = (UCHAR)((_PacketLength) % 256); \ + _IpxHeader->TransportControl = 0; \ + _IpxHeader->PacketType = (_PacketType); \ + RtlCopyMemory(_IpxHeader->DestinationNetwork, (PVOID)(_RemoteAddress), 12); \ + RtlCopyMemory(_IpxHeader->SourceNetwork, (_LocalAddress), 12); \ +} +#endif + +// +// Routines in loopback.c +// + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ); + +VOID +IpxInitLoopback(); + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ); + diff --git a/private/ntos/tdi/isnp/ipx/ipxtypes.h b/private/ntos/tdi/isnp/ipx/ipxtypes.h new file mode 100644 index 000000000..0cc788a8f --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ipxtypes.h @@ -0,0 +1,1999 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxtypes.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _IPX_SEND_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN SendInProgress; // used in an NdisSend + BOOLEAN OwnedByAddress; // packet is owned by an address + UCHAR DestinationType; // one of DEF, BCAST, MCAST + struct _IPX_PADDING_BUFFER * PaddingBuffer; // if one was allocated + PNDIS_BUFFER PreviousTail; // if padding buffer was appended +#ifdef _PNP_POWER + IPX_LOCAL_TARGET LocalTarget; + USHORT CurrentNicId; // current binding being tried for net 0 sends + ULONG PacketLength; // length that comes into IpxSendFrame initially + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY WaitLinkage; // when on WaitingForRoute/WaitingRipPackets +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + + // + // The next fields are used differently depending on whether + // the packet is being used for a datagram send or a rip request. + // + + union { + struct { + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on + USHORT CurrentNicId; // current binding being tried for net 0 sends + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends + BOOLEAN OutgoingSap; // packet is sent from the SAP socket + } SR_DG; + struct { + ULONG Network; // net we are looking for + USHORT CurrentNicId; // current binding being tried + UCHAR RetryCount; // number of times sent; 0xfe = response, 0xff = down + BOOLEAN RouteFound; // network has been found + USHORT SendTime; // timer expirations when sent. + BOOLEAN NoIdAdvance; // don't advance CurrentNicId this time. + } SR_RIP; + } u; + + PUCHAR Header; // points to the MAC/IPX header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header; +#if BACK_FILL + BOOLEAN BackFill; // 1 if we are using SMB's extended header + PNDIS_BUFFER IpxHeader; // Place holder for our IpxHeader + PNDIS_BUFFER MacHeader; // Place holder for our mac header + PVOID MappedSystemVa; + PVOID ByteOffset; + LONG UserLength; +#endif +} IPX_SEND_RESERVED, *PIPX_SEND_RESERVED; + +// +// Values for the DestinationType field. +// + +#define DESTINATION_DEF 1 +#define DESTINATION_BCAST 2 +#define DESTINATION_MCAST 3 + +// +// Used to indicate to IpxReceiveIndication that this is a loopback packet +// Assumption: Ndis cannot return this as the NdisBindingHandle value since +// that is a pointer (our pointers shd in kernel space, if not in Nonpaged pool). +// +#define IPX_LOOPBACK_COOKIE 0x00460007 + +// +// MIN/MAX macros +// +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#ifdef _PNP_POWER + +// +// In order to avoid a lock to read a value, this is used. +// As long as the final value has made it to _b by the time +// the check is made, this works fine. +// + +#define ASSIGN_LOOP(_a, _b) \ + do { \ + _a = _b; \ + } while ( _a != _b ); + +// +// Gets the value of a Ulong (possibly a pointer) by adding 0 in an interlocked manner. +// This relies on the fact that the return of the ExchangeAdd will be the value prior to +// addition. Since the value added is 0, the final value stays the same. +// +#define GET_VALUE(x) \ + InterlockedExchangeAdd((PULONG)&(x), 0) + +#define SET_VALUE(x,y) \ + InterlockedExchange((PLONG)&(x), (LONG)(y)) + +/* +PBINDING +NIC_ID_TO_BINDING ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// We need to ensure that the binding array pointer is valid hence use the interlocked operation. +// Also, the binding pointer read out of the array should be valid. Since the bindings are never +// freed (IPX maintains a pool of bindings), the pointer thus retrieved will always point to +// memory that belongs to us, which in the worst case could point to a re-claimed binding block. +// +// BUGBUGZZ: we can eliminate the second interlock if we always ensure that the bindings in an array +// dont change i.e. when we move around bindings, do them in a copy and make that the master (thru' +// a single ulong exchange). +// +// A problem that still remains here is that even if we get a valid (IPX owned non-paged) ptr out of +// the array, we still cannot atomically get a ref on the binding +// We might need those locks after all.... (revisit post SUR when the delete is enabled). +// +#define NIC_ID_TO_BINDING(_device, _nicid) \ + ((PBINDING)GET_VALUE( ((PBIND_ARRAY_ELEM) GET_VALUE( (_device)->Bindings) )[_nicid].Binding )) + +/* +PBINDING +NIC_ID_TO_BINDING_NO_ILOCK ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// No interlocked operations are used here to get to the binding. This is used in the PnP add/delete +// adapter paths on the assumption that NDIS will serialize the addition/deletion of cards. [JammelH: 5/15/96] +// +#define NIC_ID_TO_BINDING_NO_ILOCK(_device, _nicid) \ + ((_device)->Bindings[_nicid].Binding) + +/* +VOID +INSERT_BINDING( + IN PDEVICE _device, + IN USHORT _nicid, + IN PBINDING _binding + ) +*/ +// +// We dont do a get_value for the first arg of the macro since we are the writer and +// this value cannot change from under us here (NDIS will not give us two PnP Add adapter +// indications simultaneously). +// +#define INSERT_BINDING(_device, _nicid, _binding) \ + SET_VALUE((_device)->Bindings[_nicid].Binding, (_binding)); + +/* +VOID +SET_VERSION( + IN PDEVICE _device, + IN USHORT _nicid + ) +*/ +#define SET_VERSION(_device, _nicid) \ + SET_VALUE((_device)->Bindings[_nicid].Version, ++(_device)->BindingVersionNumber); + +/* +PBINDING +NIC_HANDLE_TO_BINDING ( + IN PDEVICE _device, + IN PNIC_HANDLE _nichandle, + ); +*/ +#ifdef _PNP_LATER +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + (((_nichandle)->Signature == IPX_BINDING_SIGNATURE) && \ + ((_nichandle)->Version == (_device)->Bindings[(_nichandle)->NicId].Version)) ? \ + (_device)->Bindings[(_nichandle)->NicId].Binding : NULL; +#else + +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + NIC_ID_TO_BINDING(_device, (_nichandle)->NicId); +#endif + +/* +VOID +FILL_LOCAL_TARGET( + IN PLOCAL_TARGET _localtarget, + IN USHORT _nicid + ) +*/ + +#define FILL_LOCAL_TARGET(_localtarget, _nicid) \ + NIC_HANDLE_FROM_NIC((_localtarget)->NicHandle, _nicid) + +#ifdef _PNP_LATER +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = _nic; \ + _nichandle.Signature = IPX_BINDING_SIGNATURE; \ + if (_nic == 0) { \ + _nichandle.Version = 0; \ + } else { \ + _nichandle.Version = IpxDevice->Bindings[_nic].Version; \ + } + +#else + +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = _nic; + +#endif + +#define NIC_FROM_LOCAL_TARGET(_localtarget) \ + (_localtarget)->NicHandle.NicId + +#endif _PNP_POWER + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _IPX_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + BOOLEAN OwnedByAddress; // packet is owned by an address +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + PREQUEST SingleRequest; // if transfer is for one only + struct _IPX_RECEIVE_BUFFER * ReceiveBuffer; // if transfer is for multiple requests + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY Requests; // waiting on this transfer +} IPX_RECEIVE_RESERVED, *PIPX_RECEIVE_RESERVED; + +// +// The amount of data we need in our standard header, rounded up +// to the next longword bounday. +// +// [BUGBUGZZ] Make this declaration in one place +// +#define PACKET_HEADER_SIZE (MAC_HEADER_SIZE + IPX_HEADER_SIZE + RIP_PACKET_SIZE) + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +// #define IPX_OWN_PACKETS 1 + +#define IpxAllocateSendPacket(_Device,_SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_SendPacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define IpxAllocateReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_ReceivePacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#ifdef IPX_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _IPX_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_SEND_RESERVED)]; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_RECEIVE_RESERVED)]; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define IpxFreeSendPacket(_Device,_Packet) + +#define IpxFreeReceivePacket(_Device,_Packet) + +#else // IPX_OWN_PACKETS + +typedef struct _IPX_SEND_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define IpxAllocateSingleSendPacket(_Device,_SendPacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_SendPacket)->PoolHandle,1,sizeof(IPX_SEND_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, (_SendPacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxAllocateSingleReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_ReceivePacket)->PoolHandle,1,sizeof(IPX_RECEIVE_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, (_ReceivePacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxFreeSingleSendPacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ +} +#define IpxFreeSingleReceivePacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ +} + +#define IpxFreeSendPacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#define IpxFreeReceivePacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#endif // IPX_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PIPX_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PIPX_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + +// +// This is the structure that contains a receive buffer for +// datagrams that are going to multiple recipients. +// + +typedef struct _IPX_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#ifdef IPX_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + PUCHAR Data; // the actual data +} IPX_RECEIVE_BUFFER, *PIPX_RECEIVE_BUFFER; + + +// +// This is the structure that contains a padding buffer for +// padding ethernet frames out to an even number of bytes. +// + +typedef struct _IPX_PADDING_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + UCHAR Data[1]; // the actual pad data +} IPX_PADDING_BUFFER, *PIPX_PADDING_BUFFER; + +#ifdef IPX_OWN_PACKETS + +typedef struct _IPX_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_SEND_PACKET Packets[1]; +} IPX_SEND_POOL, *PIPX_SEND_POOL; + +typedef struct _IPX_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_RECEIVE_PACKET Packets[1]; +} IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +#else + +typedef struct _IPX_PACKET_POOL { + LIST_ENTRY Linkage; + PUCHAR Header; + NDIS_HANDLE PoolHandle; +} IPX_PACKET_POOL, *PIPX_PACKET_POOL; + +typedef IPX_PACKET_POOL IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +typedef IPX_PACKET_POOL IPX_SEND_POOL, *PIPX_SEND_POOL; + +#endif // IPX_OWN_PACKETS + +typedef struct _IPX_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; + IPX_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} IPX_RECEIVE_BUFFER_POOL, *PIPX_RECEIVE_BUFFER_POOL; + +// +// Number of upper drivers we support. +// + +#define UPPER_DRIVER_COUNT 3 + + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_RIP 4 +#define MEMORY_SOURCE_ROUTE 5 +#define MEMORY_BINDING 6 +#define MEMORY_QUERY 7 + +#define MEMORY_MAX 8 + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(IpxMemoryInterlock); +extern MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +#endif + + +// +// This defines the reasons we delete rip entries for a binding. +// + +typedef enum _IPX_BINDING_CHANGE_TYPE { + IpxBindingDeleted, + IpxBindingMoved, + IpxBindingDown +} IPX_BINDING_CHANGE_TYPE, *PIPX_BINDING_CHANGE_TYPE; + + +// +// This structure contains information about a single +// source routing entry. +// + +typedef struct _SOURCE_ROUTE { + + struct _SOURCE_ROUTE * Next; // next in hash list + + UCHAR MacAddress[6]; // remote MAC address + UCHAR TimeSinceUsed; // timer expirations since last used + UCHAR SourceRoutingLength; // length of the data + + UCHAR SourceRouting[1]; // source routing data, stored as received in + +} SOURCE_ROUTE, *PSOURCE_ROUTE; + +#define SOURCE_ROUTE_SIZE(_SourceRoutingLength) \ + (FIELD_OFFSET(SOURCE_ROUTE, SourceRouting[0]) + (_SourceRoutingLength)) + +#define SOURCE_ROUTE_HASH_SIZE 16 + +// +// ULONG +// MacSourceRoutingHash( +// IN PUCHAR MacAddress +// ) +// +// /*++ +// +// Routine Description: +// +// This routine returns a hash value based on the MAC address +// that is pointed to. It will be between 0 and SOURCE_ROUTE_HASH_SIZE. +// +// Arguments: +// +// MacAddress - The MAC address. NOTE: The source-routing bit may +// or may not be on in the first byte, this routine will handle +// that. +// +// Return Value: +// +// The hash value. +// +// --*/ +// + +#define MacSourceRoutingHash(_MacAddress) \ + ((ULONG)((_MacAddress)[5] % SOURCE_ROUTE_HASH_SIZE)) + + + +// +// this structure describes a single NDIS adapter that IPX is +// bound to. +// + +struct _DEVICE; + +typedef struct _ADAPTER { + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IAD1" +#endif + +#ifdef _PNP_POWER + ULONG ReferenceCount; +#endif + + ULONG BindingCount; // number bound to this adapter + + // + // Handle returned by the NDIS wrapper after we bind to it. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The queue of (currently receive only) requests waiting to complete. + // + + LIST_ENTRY RequestCompletionQueue; + + // + // IPX header normal offsets for directed and + // broadcast/multicast frames. + // + + ULONG DefHeaderSizes[ISN_FRAME_TYPE_MAX]; + ULONG BcMcHeaderSizes[ISN_FRAME_TYPE_MAX]; + + // + // List of buffers to be used for transfers. + // + + ULONG AllocatedReceiveBuffers; + LIST_ENTRY ReceiveBufferPoolList; + SLIST_HEADER ReceiveBufferList; + + // + // List of ethernet padding buffers. + // + + ULONG AllocatedPaddingBuffers; + SINGLE_LIST_ENTRY PaddingBufferList; + + struct _BINDING * Bindings[ISN_FRAME_TYPE_MAX]; // the binding for each frame type. + + // + // TRUE if broadcast reception is enabled on this adapter. + // + + BOOLEAN BroadcastEnabled; + + // + // TRUE if we have enabled an auto-detected frame type + // on this adapter -- used to prevent multiple ones. + // + + BOOLEAN AutoDetectFound; + + // + // TRUE if we got a response to at least one of our + // auto-detect frames. + // + + BOOLEAN AutoDetectResponse; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // For WAN adapters, we support multiple bindings per + // adapter, all with the same frame type. For them we + // demultiplex using the local mac address. This stores + // the range of device NIC IDs associated with this + // particular address. + // + + USHORT FirstWanNicId; + USHORT LastWanNicId; + ULONG WanNicIdCount; + + // + // This is based on the configuration. + // + + USHORT BindSap; // usually 0x8137 + USHORT BindSapNetworkOrder; // usually 0x3781 + BOOLEAN SourceRouting; + BOOLEAN EnableFunctionalAddress; + BOOLEAN EnableWanRouter; + ULONG ConfigMaxPacketSize; + + // + // TRUE if the tree is empty, so we can check quickly. + // + + BOOLEAN SourceRoutingEmpty[IDENTIFIER_TOTAL]; + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR AdapterName; + ULONG AdapterNameLength; + + struct _DEVICE * Device; + + CTELock Lock; + CTELock * DeviceLock; + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // The value of Device->SourceRoutingTime the last time + // we checked the list for timeouts (this is so we can + // tell in the timeout code when two bindings point to the + // same adapter). + // + + CHAR LastSourceRoutingTime; + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + NDIS_STATUS OpenErrorStatus; // if Status is NDIS_STATUS_OPEN_FAILED. + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + NDIS_INFORMATION MacInfo; + + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + ULONG ReceiveBufferSpace; // as queried from the card + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + + // + // The source routing tree for each of the identifiers + // + + PSOURCE_ROUTE SourceRoutingHeads[IDENTIFIER_TOTAL][SOURCE_ROUTE_HASH_SIZE]; + +} ADAPTER, * PADAPTER; + +#define ASSERT_ADAPTER(_Adapter) \ + CTEAssert (((_Adapter)->Type == IPX_ADAPTER_SIGNATURE) && ((_Adapter)->Size == sizeof(ADAPTER))) + + +// +// These are the media and frame type specific MAC header +// constructors that we call in the main TDI send path. +// + +typedef NDIS_STATUS +(*IPX_SEND_FRAME_HANDLER) ( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + + +#define BREF_BOUND 1 +#ifdef _PNP_POWER +#define BREF_DEVICE_ACCESS 2 +#define BREF_ADAPTER_ACCESS 3 +#endif +#define BREF_TOTAL 4 + +typedef struct _BINDING { + +#if DBG + ULONG RefTypes[BREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IBI1" +#endif + + ULONG ReferenceCount; + +#ifdef _PNP_POWER + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +#endif + + // + // Adapter this binding is on. + // + + PADAPTER Adapter; + + // + // ID identifying us to the system (will be the index + // in Device->Bindings[]). + // + + USHORT NicId; + + // + // For LANs these will be the same as the adapter's, for WANs + // they change on line up indications. + // + + ULONG MaxSendPacketSize; + ULONG MediumSpeed; // in units of 100 bytes/sec + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // This is used for WAN lines, all sends go to this address + // which is given on line up. + // + + HARDWARE_ADDRESS RemoteMacAddress; + + // + // For WAN lines, holds the remote address indicated to us + // in the IPXCP_CONFIGURATION structure -- this is used to + // select a binding to send to when WanGlobalNetworkNumber + // is TRUE. + // + + UCHAR WanRemoteNode[6]; + + // + // TRUE if this binding was set up to allow auto-detection, + // instead of being configured explicitly in the registry. + // + + BOOLEAN AutoDetect; + + // + // TRUE if this binding was set up for auto-detection AND + // was the default in the registry. + // + + BOOLEAN DefaultAutoDetect; + + // + // During auto-detect when we are processing responses from + // various networks, these keep track of how many responses + // we have received that match the current guess at the + // network number, and how many don't (the current guess + // is stored in TentativeNetworkAddress). + // + + USHORT MatchingResponses; + USHORT NonMatchingResponses; + + // + // During auto-detect, stores the current guess at the + // network number. + // + + ULONG TentativeNetworkAddress; + + // + // TRUE if this binding is part of a binding set. + // + + BOOLEAN BindingSetMember; + + // + // TRUE if this binding should receive broadcasts (this + // rotates through the members of a binding set). + // + + BOOLEAN ReceiveBroadcast; + + // + // TRUE for WAN lines if we are up. + // + + BOOLEAN LineUp; + + // + // TRUE if this is a WAN line and is dialout. + // + + BOOLEAN DialOutAsync; + + union { + + // + // Used when a binding is active, if it is a member + // of a binding set. + // + + struct { + + // + // Used to link members of a binding set in a circular list. + // NULL for non-set members. + // + + struct _BINDING * NextBinding; + + // + // If this binding is a master of a binding set, this points + // to the binding to use for the next send. For other members + // of a binding set it is NULL. We use this to determine + // if a binding is a master or not. + // + + struct _BINDING * CurrentSendBinding; + + // + // For binding set members, points to the master binding + // (if this is the master it points to itself). + // + + struct _BINDING * MasterBinding; + + }; + + // + // This is used when we are first binding to adapters, + // and the device's Bindings array is not yet allocated. + // + + LIST_ENTRY InitialLinkage; + + }; + + // + // Used by rip to keep track of unused wan lines. + // + + ULONG WanInactivityCounter; + + // + // Our local address, we don't use the socket but we keep + // it here so we can do quick copies. It contains the + // real network that we are bound to and our node + // address on that net (typically the adapter's MAC + // address but it will change for WANs). + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_FRAME_HANDLER SendFrameHandler; + + struct _DEVICE * Device; + + CTELock * DeviceLock; + + ULONG DefHeaderSize; // IPX header offset for directed frames + ULONG BcMcHeaderSize; // IPX header offset for broadcast/multicast + + ULONG AnnouncedMaxDatagramSize; // what we advertise -- assumes worst-case SR + ULONG RealMaxDatagramSize; // what will really break the card + ULONG MaxLookaheadData; + + // + // Configuration parameters. We overlay all of them except + // FrameType over the worker thread item we use to delay + // deletion -- all the others are not needed once the + // binding is up. Some of the config parameters are stored + // in the adapter, these are the ones that are modified + // per-binding. + // + + ULONG FrameType; + union { + struct { + ULONG ConfiguredNetworkNumber; + BOOLEAN AllRouteDirected; + BOOLEAN AllRouteBroadcast; + BOOLEAN AllRouteMulticast; + }; + WORK_QUEUE_ITEM WanDelayedQueueItem; + }; + +#ifdef _PNP_POWER + // + // Indicates whether this binding was indicated to the ISN driver + // + BOOLEAN IsnInformed[UPPER_DRIVER_COUNT]; + + // + // Keeps the NetAddressRegistrationHandle. + // + HANDLE TdiRegistrationHandle; +#endif +} BINDING, * PBINDING; + + +#ifdef _PNP_POWER +typedef struct _IPX_BINDING_POOL { + LIST_ENTRY Linkage; + UINT BindingCount; + BINDING Bindings[1]; +} IPX_BINDING_POOL, *PIPX_BINDING_POOL; +#endif + +// +// This structure defines the control structure for a single +// router table segment. +// + +typedef struct _ROUTER_SEGMENT { + LIST_ENTRY WaitingForRoute; // packets waiting for a route in this segment + LIST_ENTRY FindWaitingForRoute; // find route requests waiting for a route in this segment + LIST_ENTRY WaitingLocalTarget; // QUERY_IPX_LOCAL_TARGETs waiting for a route in this segment + LIST_ENTRY WaitingReripNetnum; // MIPX_RERIPNETNUMs waiting for a route in this segment + LIST_ENTRY Entries; + PLIST_ENTRY EnumerateLocation; +} ROUTER_SEGMENT, *PROUTER_SEGMENT; + + +// +// Number of buckets in the address hash table. This is +// a multiple of 2 so hashing is quick. +// + +#define IPX_ADDRESS_HASH_COUNT 16 + +// +// Routine to convert a socket to a hash index. We use the +// high bits because it is stored reversed. +// + +#define IPX_HASH_SOCKET(_S) ((((_S) & 0xff00) >> 8) % IPX_ADDRESS_HASH_COUNT) + +// +// This macro gets the socket hash right out of the IPX header. +// + +#define IPX_DEST_SOCKET_HASH(_IpxHeader) (((PUCHAR)&(_IpxHeader)->DestinationSocket)[1] % IPX_ADDRESS_HASH_COUNT) + + +// +// This structure defines the per-device structure for IPX +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_SR_TIMER 4 +#define DREF_RIP_TIMER 5 +#define DREF_LONG_TIMER 6 +#define DREF_RIP_PACKET 7 +#define DREF_ADDRESS_NOTIFY 8 +#define DREF_LINE_CHANGE 9 + +#define DREF_TOTAL 12 + +#ifdef _PNP_POWER +// +// Pre-allocated binding array size +// +#define MAX_BINDINGS 50 +#endif _PNP_POWER + +#ifdef _PNP_POWER +// +// Our new binding array is composed of the following binding +// array element +// +typedef struct _BIND_ARRAY_ELEM { + PBINDING Binding; + ULONG Version; +} BIND_ARRAY_ELEM, *PBIND_ARRAY_ELEM; + +#endif _PNP_POWER + +typedef struct _DEVICE { + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + CTELock Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + + // + // These are temporary versions of these counters, during + // timer expiration we update the real ones. + // + + ULONG TempDatagramBytesSent; + ULONG TempDatagramsSent; + ULONG TempDatagramBytesReceived; + ULONG TempDatagramsReceived; + + // + // Configuration parameters. + // + + BOOLEAN EthernetPadToEven; + BOOLEAN SingleNetworkActive; + BOOLEAN DisableDialoutSap; + + // + // TRUE if we have multiple cards but a virtual network of 0. + // + + BOOLEAN MultiCardZeroVirtual; + + CTELock Lock; + + // + // Lock to access the sequenced lists in the device. + // + CTELock SListsLock; + + LONG ReferenceCount; // activity count/this provider. + +#ifdef _PNP_POWER + + // + // Lock used to control the access to a binding (either from the + // binding array in the device or from the binding array in the + // adapter. + // + CTELock BindAccessLock; + + // + // Registry Path for use when PnP adapters appear. + // + PWSTR RegistryPathBuffer; + + UNICODE_STRING RegistryPath; + + // + // Binding array has the Version number too + // + PBIND_ARRAY_ELEM Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + + // + // Monotonically increasing version number kept in bindings. + // Hopefully this will not wrap around... + // + ULONG BindingVersionNumber; +#else + // + // During init we hold all bindings in a queue, but after we + // know the approximate number we allocate an array. + // + + union { + LIST_ENTRY InitialBindingList; // only used during init. + struct { + PBINDING * Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + }; + }; +#endif _PNP_POWER + + + // + // ValidBindings is the number of bindings in the array which may + // be valid (they are lan bindings or down wan binding placeholders). + // It will be less than BindingCount by the number of auto-detect + // bindings that are thrown away. HighestExternalNicId is ValidBindings + // minus any binding set slaves which are moved to the end of the + // array. SapNicCount is like HighestExternalNicId except that + // if WanGlobalNetworkNumber is TRUE it will count all WAN bindings + // as one. HighestExternalType20NicId is like HighestExternalNicId + // except it stops when all the remaining bindings are down wan + // lines, or dialin wan lines if DisableDialinNetbios bit 1 is on. + // + + USHORT ValidBindings; + USHORT HighestExternalNicId; + USHORT SapNicCount; + USHORT HighestType20NicId; +#ifdef _PNP_POWER + // + // Keeps track of the last LAN binding's position in the binding array + // + USHORT HighestLanNicId; + + // + // This keeps track of the current size of the binding array + // + USHORT MaxBindings; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + +#if BACK_FILL + LIST_ENTRY GlobalBackFillPacketList; +#endif + + // + // Action requests from SAP waiting for an adapter status to change. + // + + LIST_ENTRY AddressNotifyQueue; + + // + // Action requests from nwrdr waiting for the WAN line + // to go up/down. + // + + LIST_ENTRY LineChangeQueue; + + // + // All packet pools are chained on these lists. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + + +#if BACK_FILL + LIST_ENTRY BackFillPoolList; + SLIST_HEADER BackFillPacketList; +#endif + +#ifdef _PNP_POWER + LIST_ENTRY BindingPoolList; + SLIST_HEADER BindingList; +#endif + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + PIPX_PADDING_BUFFER PaddingBuffer; + + UCHAR State; + + UCHAR FrameTypeDefault; + + // + // This holds state if SingleNetworkActive is TRUE. If + // it is TRUE then WAN nets are active; if it is FALSE + // then LAN nets are active. + // + + BOOLEAN ActiveNetworkWan; + + // + // TRUE if we have a virtual network. + // + + BOOLEAN VirtualNetwork; + + // + // If we are set up for SingleNetworkActive, we may have + // to start our broadcast of net 0 frames somewhere other + // than NIC ID 1, so that we don't send to the wrong type. + // + + USHORT FirstLanNicId; + USHORT FirstWanNicId; + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many of various resources have been allocated. + // + + ULONG AllocatedDatagrams; + ULONG AllocatedReceivePackets; + ULONG AllocatedPaddingBuffers; + + // + // Other configuration parameters. + // + + ULONG InitDatagrams; + ULONG MaxDatagrams; + ULONG RipAgeTime; + ULONG RipCount; + ULONG RipTimeout; + ULONG RipUsageTime; + ULONG SourceRouteUsageTime; + USHORT SocketStart; + USHORT SocketEnd; + ULONG SocketUniqueness; + ULONG VirtualNetworkNumber; + ULONG EthernetExtraPadding; + BOOLEAN DedicatedRouter; + BOOLEAN VirtualNetworkOptional; + UCHAR DisableDialinNetbios; + + // + // These are currently not read from the registry. + // + + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG MaxReceivePackets; + ULONG MaxReceiveBuffers; + +#ifdef _PNP_POWER + ULONG MaxPoolBindings; + ULONG AllocatedBindings; + ULONG InitBindings; +#endif + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + LARGE_INTEGER ControlChannelIdentifier; + + // + // This registry parameter controls whether IPX checks (and discards) + // packets with mismatched Source addresses in the receive path. + // + BOOLEAN VerifySourceAddress; + + // + // Where the current socket allocation is. + // + USHORT CurrentSocket; + + // + // Number of segments in the RIP database. + // + + ULONG SegmentCount; + + // + // Points to an array of locks for the RIP database (these + // are stored outside of the ROUTER_SEGMENT so the array + // can be exposed to the RIP upper driver as one piece). + // + + CTELock *SegmentLocks; + + // + // Points to an array of ROUTER_SEGMENT fields for + // various RIP control fields. + // + + ROUTER_SEGMENT *Segments; + + // + // Queue of RIP packets waiting to be sent. + // + + LIST_ENTRY WaitingRipPackets; + ULONG RipPacketCount; + + // + // Timer that keeps RIP requests RIP_GRANULARITY ms apart. + // + + BOOLEAN RipShortTimerActive; + USHORT RipSendTime; + CTETimer RipShortTimer; + + // + // Timer that runs to age out unused rip entries (if the + // router is not bound) and re-rip every so often for + // active entries. + // + + CTETimer RipLongTimer; + + // + // This controls the source routing timeout code. + // + + BOOLEAN SourceRoutingUsed; // TRUE if any 802.5 bindings exist. + CHAR SourceRoutingTime; // incremented each time timer fires. + CTETimer SourceRoutingTimer; // runs every minute. + + // + // These are the merging of the binding values. + // + + ULONG LinkSpeed; + ULONG MacOptions; + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // A pre-allocated header containing our node and network, + // plus an unused socket (so the structure is a known size + // for easy copying). + // + + TDI_ADDRESS_IPX SourceAddress; + + // + // The following field is an array of list heads of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabases[IPX_ADDRESS_HASH_COUNT]; // list of defined transport addresses. + + // + // Holds the last address we looked up. + // + + PVOID LastAddress; + + NDIS_HANDLE NdisBufferPoolHandle; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + + // + // Information.MaxDatagramSize is the minimum size we can + // send to all bindings assuming worst-case source routing; + // this is the value that won't break any network drivers. + // + + ULONG RealMaxDatagramSize; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // Indicates whether each upper driver is bound + // (Netbios = 0, SPX = 1, RIP = 2). + // + + BOOLEAN UpperDriverBound[UPPER_DRIVER_COUNT]; + + // + // TRUE if any driver is bound. + // + + BOOLEAN AnyUpperDriverBound; + + // + // Whether a receive complete should be indicated to + // this upper driver. + // + + BOOLEAN ReceiveCompletePending[UPPER_DRIVER_COUNT]; + + // + // Control channel identifier for each of the upper + // drivers' bindings. + // + + LARGE_INTEGER UpperDriverControlChannel[UPPER_DRIVER_COUNT]; + + // + // Entry points and other information for each of the + // upper drivers. + // + + IPX_INTERNAL_BIND_INPUT UpperDrivers[UPPER_DRIVER_COUNT]; + + // + // How many upper drivers want broadcast enabled. + // + + ULONG EnableBroadcastCount; + + // + // Indicates if an enable broadcast operation is in + // progress. + // + + BOOLEAN EnableBroadcastPending; + + // + // Indicates if a disable broadcast operation is in + // progress. + // + + BOOLEAN DisableBroadcastPending; + + // + // Indicates if the current operation should be + // reversed when it is finished. + // + + BOOLEAN ReverseBroadcastOperation; + + // + // TRUE if RIP wants a single network number for all WANs + // + + BOOLEAN WanGlobalNetworkNumber; + + // + // If WanGlobalNetworkNumber is TRUE, then this holds the + // actual value of the network number, once we know it. + // + + ULONG GlobalWanNetwork; + + // + // Set to TRUE if WanGlobalNetworkNumber is TRUE and we + // have already completed a queued notify from SAP. In + // this case GlobalWanNetwork will be set correctly. + // + + BOOLEAN GlobalNetworkIndicated; + + // + // TRUE if we need to act as a RIP announcer/responder + // for our virtual net. + // + + BOOLEAN RipResponder; + + // + // TRUE if we have already logged an error because someone + // sent a SAP response but we have multiple cards with no + // virtual network. + // + + BOOLEAN SapWarningLogged; + + // + // Used to queue up a worker thread to perform + // broadcast operations. + // + + WORK_QUEUE_ITEM BroadcastOperationQueueItem; + +#ifdef _PNP_POWER + // + // Used to queue up a worker thread to perform + // PnP indications to upper drivers. + // + + WORK_QUEUE_ITEM PnPIndicationsQueueItem; +#endif + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + + // + // Counters for most of the statistics that IPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + + // + // This is TRUE if we have any adapters where we are + // auto-detecting the frame type. + // + + BOOLEAN AutoDetect; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // Our state during auto-detect. After we are done this + // will stay at AutoDetectDone; + // + + UCHAR AutoDetectState; + + // + // If we are auto-detecting, this event is used to stall + // our initialization code while we do auto-detection -- + // this is so we have a constant view of the world once + // we return from DriverEntry. + // + + KEVENT AutoDetectEvent; + + // + // Counters for "active" time. + // + + LARGE_INTEGER IpxStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // Points back to the system device object. + // + + PDEVICE_OBJECT DeviceObject; + +#ifdef _PNP_POWER + // + // Used to store the Tdi registration handle for deviceobject notifications. + // + HANDLE TdiRegistrationHandle; + + // + // Used to store the TA_ADDRESS which is indicated up to Tdi clients as adapters appear. + // + PTA_ADDRESS TdiRegistrationAddress; +#endif + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; + ULONG DeviceNameLength; + +} DEVICE, * PDEVICE; + + +extern PDEVICE IpxDevice; +extern PIPX_PADDING_BUFFER IpxPaddingBuffer; +#if DBG +EXTERNAL_LOCK(IpxGlobalInterlock); +#endif + + +// +// device state definitions +// + +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 + +#ifdef _PNP_POWER + +// +// New state which comes between CLOSED and OPEN. At this state, +// there are no adapters in the system and so no network activity +// is possible. +// +#define DEVICE_STATE_LOADED 0x03 +#endif _PNP_POWER + +// +// This is the state of our auto-detect if we do it. +// + +#define AUTO_DETECT_STATE_INIT 0x00 // still initializing the device +#define AUTO_DETECT_STATE_RUNNING 0x01 // sent ffffffff query, waiting for responses +#define AUTO_DETECT_STATE_PROCESSING 0x02 // processing the responses +#define AUTO_DETECT_STATE_DONE 0x03 // detection is done, IPX is active + + + +#define IPX_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + CTELock * AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST Request; // the request used for open or close + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + // + // + // TRUE if ExtendedAddressing, ReceiveIpxHeader, + // FilterOnPacketType, or ReceiveFlagAddressing is TRUE. + // + + BOOLEAN SpecialReceiveProcessing; + + // + // The remote address of a send datagram includes the + // packet type. and on a receive datagram includes + // the packet type AND a flags byte indicating information + // about the frame (was it broadcast, was it sent from + // this machine). + // + + BOOLEAN ExtendedAddressing; + + // + // TRUE if the address on a receive datagram includes + // the packet type and a flags byte (like ExtendedAddressing), + // but on send the address is normal (no packet type). + // + + BOOLEAN ReceiveFlagsAddressing; + + // + // Is the IPX header received with the data. + // + + BOOLEAN ReceiveIpxHeader; + + // + // The packet type to use if it is unspecified in the send. + // + + UCHAR DefaultPacketType; + + // + // TRUE if packet type filtering is enabled. + // + + BOOLEAN FilterOnPacketType; + + // + // The packet type to filter on. + // + + UCHAR FilteredType; + + // + // Does this address file want broadcast packets. + // + + BOOLEAN EnableBroadcast; + + // + // This is set to TRUE if this is the SAP socket -- we + // put this under SpecialReceiveProcessing to avoid + // hitting the main path. + // + + BOOLEAN IsSapSocket; + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + // + // [CH] Added the chained receive handlers. + // + + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredChainedReceiveDatagramHandler; + BOOLEAN RegisteredErrorHandler; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + PTDI_IND_CHAINED_RECEIVE_DATAGRAM ChainedReceiveDatagramHandler; + PVOID ChainedReceiveDatagramHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + +/* ULONGs to allow for Interlocked operations. + + BOOLEAN SendPacketInUse; // put these after so header is aligned. + + BOOLEAN ReceivePacketInUse; +#if BACK_FILL + BOOLEAN BackFillPacketInUse; +#endif +*/ + + ULONG SendPacketInUse; // put these after so header is aligned. + + ULONG ReceivePacketInUse; +#if BACK_FILL + ULONG BackFillPacketInUse; +#endif + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + CTELock Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + USHORT Socket; // the socket this address corresponds to. + USHORT SendSourceSocket; // used for sends; may be == Socket or 0 + + // + // The following fields are used to maintain state about this address. + // + + BOOLEAN Stopping; + ULONG Flags; // attributes of the address. + struct _DEVICE *Device; // device context to which we are attached. + CTELock * DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // Holds our source address, used for construcing datagrams + // quickly. + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_PACKET SendPacket; + IPX_RECEIVE_PACKET ReceivePacket; + +#if BACK_FILL + IPX_SEND_PACKET BackFillPacket; +#endif + + + UCHAR SendPacketHeader[IPX_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying IpxDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +#define ADDRESS_FLAGS_STOPPING 0x00000001 + +// +// In order to increase the range of ControlChannelIds, we have a large integer to represent +// monotonically increasing ControlChannelIdentifiers. This large integer is packed into the +// 6 Bytes as follows: +// +// REQUEST_OPEN_CONTEXT(_Request) - 4 bytes +// Upper 2 bytes of REQUEST_OPEN_TYPE(_Request) - 2 bytes +// +// IPX_CC_MASK is used to mask out the upper 2 bytes of the OPEN_TYPE. +// MAX_CCID is 2^48. +// +#define IPX_CC_MASK 0x0000ffff + +#define MAX_CCID 0xffffffffffff + +#define CCID_FROM_REQUEST(_ccid, _Request) \ + (_ccid).LowPart = (ULONG)(REQUEST_OPEN_CONTEXT(_Request)); \ + (_ccid).HighPart = ((ULONG)(REQUEST_OPEN_TYPE(_Request)) >> 16); + diff --git a/private/ntos/tdi/isnp/ipx/isnipx.h b/private/ntos/tdi/isnp/ipx/isnipx.h new file mode 100644 index 000000000..df947d439 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/isnipx.h @@ -0,0 +1,531 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnipx.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#ifndef _ISNIPX_ +#define _ISNIPX_ + +#define MAC_HEADER_SIZE ((IPX_MAXIMUM_MAC + 3) & ~3) +#define RIP_PACKET_SIZE ((sizeof(RIP_PACKET) + 3) & ~3) +#define IPX_HEADER_SIZE ((sizeof(IPX_HEADER) + 3) & ~3) + +// +// Frame type definitions +// + +#define ISN_FRAME_TYPE_ETHERNET_II 0 +#define ISN_FRAME_TYPE_802_3 1 +#define ISN_FRAME_TYPE_802_2 2 +#define ISN_FRAME_TYPE_SNAP 3 +#define ISN_FRAME_TYPE_ARCNET 4 // we ignore this +#define ISN_FRAME_TYPE_MAX 4 // of the four standard ones + +#define ISN_FRAME_TYPE_AUTO 0xff + + +// +// This defines the size of the maximum MAC header required +// (token-ring: MAC 14 bytes, RI 18 bytes, LLC 3 bytes, SNAP 5 bytes). +// + +#define IPX_MAXIMUM_MAC 40 + +// +// This is an internal identifier used for RIP query packets. +// + +#define IDENTIFIER_RIP_INTERNAL 4 + +// +// This is an internal identifier used for RIP response packets. +// + +#define IDENTIFIER_RIP_RESPONSE 5 + + +// +// This is the total number of "real" identifiers. +// + +#define IDENTIFIER_TOTAL 4 + + +// +// Some definitions (in the correct on-the-wire order). +// + +#define RIP_PACKET_TYPE 0x01 +#define RIP_SOCKET 0x5304 +#define RIP_REQUEST 0x0100 +#define RIP_RESPONSE 0x0200 +#define RIP_DOWN 0x8200 // use high bit to indicate it + +#define SAP_PACKET_TYPE 0x04 +#define SAP_SOCKET 0x5204 + +#define SPX_PACKET_TYPE 0x05 + +#define NB_SOCKET 0x5504 + + +#include + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of a RIP network entry. +// + +typedef struct _RIP_NETWORK_ENTRY { + ULONG NetworkNumber; + USHORT HopCount; + USHORT TickCount; +} RIP_NETWORK_ENTRY, *PRIP_NETWORK_ENTRY; + +// +// Definition of a single entry rip packet. +// + +typedef struct _RIP_PACKET { + USHORT Operation; + RIP_NETWORK_ENTRY NetworkEntry; +} RIP_PACKET, *PRIP_PACKET; + +#include + + +#define IPX_DEVICE_SIGNATURE 0x1401 +#define IPX_ADAPTER_SIGNATURE 0x1402 +#define IPX_BINDING_SIGNATURE 0x1403 +#define IPX_ADDRESS_SIGNATURE 0x1404 +#define IPX_ADDRESSFILE_SIGNATURE 0x1405 + +#define IPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// Defined granularity of RIP timeouts in milliseconds +// + +#define RIP_GRANULARITY 55 + + +// +// The default number of segments in the RIP table. +// + +#define RIP_SEGMENTS 7 + + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#if DBG + +extern ULONG IpxDebug; +extern ULONG IpxMemoryDebug; + +#define IPX_MEMORY_LOG_SIZE 128 +extern UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +extern PUCHAR IpxDebugMemoryLoc; +extern PUCHAR IpxDebugMemoryEnd; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define IPX_DEBUG(_Flag, _Print) { \ + if (IpxDebug & (IPX_DEBUG_ ## _Flag)) { \ + DbgPrint ("IPX: "); \ + DbgPrint _Print; \ + } \ + if (IpxMemoryDebug & (IPX_DEBUG_ ## _Flag)) { \ + IpxDebugMemoryLog _Print; \ + } \ +} + +#else + +#define IPX_DEBUG(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + + +// +// PREQUEST +// IpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define IpxAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// IpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define IpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// VOID +// REQUEST_OPEN_CONTEXT_AND_PARAMS( +// IN PREQUEST Request +// OUT PVOID * OpenContext, +// OUT PTDI_REQUEST_KERNEL * Parameters +// ); +// +// Simultaneously returns the open context and the parameters +// for a request (this is an optimization since the send +// datagram code needs them both). +// + +#define REQUEST_OPEN_CONTEXT_AND_PARAMS(_Request,_OpenContext,_Parameters) { \ + PIO_STACK_LOCATION _IrpSp = IoGetCurrentIrpStackLocation(_Request); \ + *(_OpenContext) = _IrpSp->FileObject->FsContext; \ + *(_Parameters) = (PTDI_REQUEST_KERNEL)(&_IrpSp->Parameters); \ +} + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// IpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define IpxCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + + +#define IPX_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define IPX_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define IPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +#define IPX_DEFINE_SYNC_CONTEXT(_SyncContext) +#define IPX_BEGIN_SYNC(_SyncContext) +#define IPX_END_SYNC(_SyncContext) + +#define IPX_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; +#define IPX_DEFINE_LOCK_HANDLE_PARAM(_LockHandle) CTELockHandle _LockHandle; + +#define IPX_GET_LOCK(_Lock, _LockHandle) \ + CTEGetLock(_Lock, _LockHandle) + +#define IPX_FREE_LOCK(_Lock, _LockHandle) \ + CTEFreeLock(_Lock, _LockHandle) + +#define IPX_GET_LOCK1(_Lock, _LockHandle) + +#define IPX_FREE_LOCK1(_Lock, _LockHandle) + +#define IPX_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, _Lock) +#define IPX_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define IPX_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, _Lock) +#define IPX_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, _Lock) + +#define IPX_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntrySList(_Queue, _Lock) +#define IPX_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntrySList(_Queue, _Entry, _Lock) + +// +// This macro adds a ULONG to a LARGE_INTEGER. +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define IPX_DEBUG_DEVICE 0x00000001 +#define IPX_DEBUG_ADAPTER 0x00000002 +#define IPX_DEBUG_ADDRESS 0x00000004 +#define IPX_DEBUG_SEND 0x00000008 +#define IPX_DEBUG_NDIS 0x00000010 +#define IPX_DEBUG_RECEIVE 0x00000020 +#define IPX_DEBUG_CONFIG 0x00000040 +#define IPX_DEBUG_PACKET 0x00000080 +#define IPX_DEBUG_RIP 0x00000100 +#define IPX_DEBUG_BIND 0x00000200 +#define IPX_DEBUG_ACTION 0x00000400 +#define IPX_DEBUG_BAD_PACKET 0x00000800 +#define IPX_DEBUG_SOURCE_ROUTE 0x00001000 +#define IPX_DEBUG_WAN 0x00002000 +#define IPX_DEBUG_AUTO_DETECT 0x00004000 + +#ifdef _PNP_POWER +#define IPX_DEBUG_PNP 0x00008000 +#endif + +#define IPX_DEBUG_LOOPB 0x00010000 + +#endif diff --git a/private/ntos/tdi/isnp/ipx/loopback.c b/private/ntos/tdi/isnp/ipx/loopback.c new file mode 100644 index 000000000..be44bae5b --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/loopback.c @@ -0,0 +1,280 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + loopback.c + +Abstract: + + This module contains the routines to implement loopback + +Author: + + Sanjay Anand (SanjayAn) 2/6/96 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Global lock to control access to the Loopback queue +// +DEFINE_LOCK_STRUCTURE(LoopLock) + +// +// Head and tail of the Loopback queue +// +PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL; +PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL; + +CTEEvent LoopXmitEvent; +BOOLEAN LoopXmitRtnRunning = 0; + +// +// MaximumPacket sized buffer to hold the lookahead data. +// +// ZZBUGBUG: In PnP this value can change +// +// PUCHAR LookaheadBuffer=NULL; +#define LOOP_LOOKAHEAD_SIZE 128 + sizeof(IPX_HEADER) + 8 + 34 + + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ) +/*++ + +Routine Description: + + Does the actual loopback. + +Arguments: + + Event - Pointer to event structure. + + Context - Pointer to ZZ + +Return Value: + + None. + +--*/ +{ + PNDIS_PACKET Packet; // Pointer to packet being transmitted + PNDIS_BUFFER Buffer; // Current NDIS buffer being processed. + ULONG TotalLength; // Total length of send. + ULONG LookaheadLength; // Bytes in lookahead. + ULONG Copied; // Bytes copied so far. + PUCHAR CopyPtr; // Pointer to buffer being copied into. + PUCHAR SrcPtr; // Pointer to buffer being copied from. + ULONG SrcLength; // Length of src buffer. + BOOLEAN Rcvd = FALSE; + PIPX_SEND_RESERVED Reserved; + ULONG MacSize; + PNDIS_PACKET *PacketPtr; + UCHAR LookaheadBuffer[LOOP_LOOKAHEAD_SIZE]; + + IPX_DEFINE_LOCK_HANDLE(Handle) + + KIRQL OldIrql; + + CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + // + // Raise IRQL so we can acquire locks at DPC level in the receive code. + // Also to be able to ReceiveIndicate at DPC + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + IPX_GET_LOCK(&LoopLock, &Handle); + + if (LoopXmitRtnRunning) { + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } + + LoopXmitRtnRunning = 1; + + for (;;) { + + // + // Get the next packet from the list. + // + Packet = LoopXmitHead; + + if (Packet != (PNDIS_PACKET)NULL) { + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + LoopXmitHead = (PNDIS_PACKET)(Reserved->PaddingBuffer); + IPX_FREE_LOCK(&LoopLock, Handle); + } else { // Nothing left to do. + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + break; + } + + // + // We use the PaddingBuffer section as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_DEBUG(LOOPB, ("Packet: %lx\n", Packet)); + + NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength); + + NdisQueryBuffer(Buffer, NULL, &MacSize); + + IPX_DEBUG(LOOPB, ("Buffer: %lx Totalpktlen: %lx MacSize: %lx\n", Buffer, TotalLength, MacSize)); + + LookaheadLength = MIN(LOOP_LOOKAHEAD_SIZE, TotalLength); + Copied = 0; + CopyPtr = LookaheadBuffer; + while (Copied < LookaheadLength) { + ULONG ThisCopy; // Bytes to copy this time. + +#ifdef DBG + if (!Buffer) { + DbgBreakPoint(); + IPX_GET_LOCK(&LoopLock, &Handle); + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } +#endif + + NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength); + ThisCopy = MIN(SrcLength, LookaheadLength - Copied); + CTEMemCopy(CopyPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + CopyPtr += ThisCopy; + NdisGetNextBuffer(Buffer, &Buffer); + } + + Rcvd = TRUE; + +#ifdef BACK_FILL + // + // For Backfill packets, the MAC header is not yet set up; for others, it is the size + // of the first MDL (17). + // + if ((Reserved->Identifier == IDENTIFIER_IPX) && + (Reserved->BackFill)) { + MacSize = 0; + } +#endif + IpxReceiveIndication( (NDIS_HANDLE)IPX_LOOPBACK_COOKIE, // BindingContext + Packet, // ReceiveContext + (MacSize) ? LookaheadBuffer : NULL, // HeaderBuffer + MacSize, // HeaderBufferSize + LookaheadBuffer+MacSize, // LookAheadBuffer + LookaheadLength-MacSize, // LookAheadBufferSize + TotalLength-MacSize); // PacketSize + + IpxSendComplete(Context, Packet, NDIS_STATUS_SUCCESS); + + // + // Give other threads a chance to run. + // + KeLowerIrql(OldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + IPX_GET_LOCK(&LoopLock, &Handle); + } + + if (Rcvd) { + IpxReceiveComplete(Context); + } + + KeLowerIrql(OldIrql); +} + + +VOID +IpxInitLoopback() +/*++ + +Routine Description: + + Initializes various loopback structures. + +Arguments: + +Return Value: + + None. + +--*/ +{ + CTEInitLock(&LoopLock); + CTEInitEvent(&LoopXmitEvent, IpxDoLoopback); + return; +} + + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Enqueues a packet to the loopbackQ + +Arguments: + + Packet - The packet to be enqueued. + + Context - Pointer to the adapter corresp to the first binding. + +Return Value: + + None. + +--*/ +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // We use the PaddingBuffer as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_GET_LOCK(&LoopLock, &LockHandle); + + // + // LoopbackQ is empty + // + if (LoopXmitHead == (PNDIS_PACKET)NULL) { + LoopXmitHead = Packet; + } else { + Reserved = (PIPX_SEND_RESERVED)(LoopXmitTail->ProtocolReserved); + (PNDIS_PACKET)(Reserved->PaddingBuffer) = Packet; + } + LoopXmitTail = Packet; + + IPX_DEBUG(LOOPB, ("Enqued packet: %lx, Reserved: %lx\n", Packet, Reserved)); + + // + // If this routine is not already running, schedule it as a work item. + // + if (!LoopXmitRtnRunning) { + CTEScheduleEvent(&LoopXmitEvent, Context); + } + + IPX_FREE_LOCK(&LoopLock, LockHandle); +} diff --git a/private/ntos/tdi/isnp/ipx/mac.c b/private/ntos/tdi/isnp/ipx/mac.c new file mode 100644 index 000000000..93f9e8a89 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mac.c @@ -0,0 +1,3793 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the IPX transport. + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define TR_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit +#define TR_DEFAULT_LENGTH 0x70 // default for outgoing +#define TR_MAX_SIZE_MASK 0x70 + +#define TR_PREAMBLE_AC 0x10 +#define TR_PREAMBLE_FC 0x40 + +#define FDDI_HEADER_BYTE 0x57 + + +static UCHAR AllRouteSourceRouting[2] = { 0x82, TR_DEFAULT_LENGTH }; +static UCHAR SingleRouteSourceRouting[2] = { 0xc2, TR_DEFAULT_LENGTH }; + +#define ROUTE_EQUAL(_A,_B) { \ + (*(UNALIGNED USHORT *)(_A) == *(UNALIGNED USHORT *)(_B)) \ +} + +// +// For back-fillable packets, chains the back-fill space as a MAC header +// to the packet and sets the header pointer. +// + +// +// BUGBUG: We dont need to test for IDENTIFIER_IPX since it will always be +// true for the mediumframe specific send handlers. +// +#define BACK_FILL_HEADER(_header, _reserved, _headerlength, _packet) \ + if ((_reserved)->Identifier == IDENTIFIER_IPX) { \ + if((_reserved)->BackFill) { \ + CTEAssert ((_reserved)->HeaderBuffer); \ + CTEAssert ((_reserved)->HeaderBuffer->MdlFlags & MDL_NETWORK_HEADER); \ + _header = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->MappedSystemVa = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->ByteOffset -= _headerlength; \ + NdisChainBufferAtFront(_packet,(PNDIS_BUFFER)(_reserved)->HeaderBuffer); \ + } \ + } + +// +// In case of back-fillable packets, the adjusted length should include +// the prev. bytecount of the headerbuffer. +// +#define BACK_FILL_ADJUST_BUFFER_LENGTH(_reserved, _headerlength) \ + if((_reserved)->BackFill){ \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength+(_reserved)->HeaderBuffer->ByteCount); \ + IPX_DEBUG(SEND,("mac user mdl %x\n", (_reserved)->HeaderBuffer)); \ + } else { \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength); \ + } + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + +#ifndef _PNP_POWER + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,MacInitializeMacInfo) +#endif + +#endif + + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ) + +/*++ + +Routine Description: + + Fills in the binding info based on the adapter's MacInfo + and the frame type of the binding. + +Arguments: + + Binding - The newly created binding. + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + ULONG MaxUserData; + + Binding->DefHeaderSize = Adapter->DefHeaderSizes[Binding->FrameType]; + Binding->BcMcHeaderSize = Adapter->BcMcHeaderSizes[Binding->FrameType]; + + MacReturnMaxDataSize( + &Adapter->MacInfo, + NULL, + 0, + Binding->MaxSendPacketSize, + &MaxUserData); + + Binding->MaxLookaheadData = + Adapter->MaxReceivePacketSize - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->AnnouncedMaxDatagramSize = + MaxUserData - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->RealMaxDatagramSize = + Binding->MaxSendPacketSize - + Adapter->MacInfo.MaxHeaderLength - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + +} /* MacInitializeBindingInfo */ + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + case NdisMedium802_5: + MacInfo->SourceRouting = TRUE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x80; + MacInfo->MaxHeaderLength = 32; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_5; + break; + case NdisMediumFddi: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 13; + MacInfo->MinHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + break; + case NdisMediumArcnet878_2: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x00; + MacInfo->MaxHeaderLength = 3; + MacInfo->MinHeaderLength = 3; + MacInfo->MediumType = NdisMediumArcnet878_2; + break; + case NdisMediumWan: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = TRUE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + default: + CTEAssert(FALSE); + } + MacInfo->RealMediumType = MacType; + +} /* MacInitializeMacInfo */ + + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ) + +/*++ + +Routine Description: + + Maps the specified frame type to a value which is + valid for the medium. + +Arguments: + + MacType - The MAC type we wish to map for. + + FrameType - The frame type in question. + + MappedFrameType - Returns the mapped frame type. + +Return Value: + +--*/ + +{ + switch (MacType) { + + // + // Ethernet accepts all values, the default is 802.2. + // + + case NdisMedium802_3: + if (FrameType >= ISN_FRAME_TYPE_MAX) { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } else { + *MappedFrameType = FrameType; + } + break; + + // + // Token-ring supports SNAP and 802.2 only. + // + + case NdisMedium802_5: + if (FrameType == ISN_FRAME_TYPE_SNAP) { + *MappedFrameType = ISN_FRAME_TYPE_SNAP; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // FDDI supports SNAP, 802.2, and 802.3 only. + // + + case NdisMediumFddi: + if ((FrameType == ISN_FRAME_TYPE_SNAP) || (FrameType == ISN_FRAME_TYPE_802_3)) { + *MappedFrameType = FrameType; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // On arcnet there is only one frame type, use 802.3 + // (it doesn't matter what we use). + // + + case NdisMediumArcnet878_2: + *MappedFrameType = ISN_FRAME_TYPE_802_3; + break; + + // + // WAN uses ethernet II because it includes the ethertype. + // + + case NdisMediumWan: + *MappedFrameType = ISN_FRAME_TYPE_ETHERNET_II; + break; + + default: + CTEAssert(FALSE); + } + +} /* MacMapFrameType */ + +// +// BUGBUG -- use symbols instead of hardcoded values for mac header lengths +// --pradeepb +// + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all headers + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & TR_MAX_SIZE_MASK) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + +#if 0 + if (DeviceMaxFrameSize < 608) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 576; + } +#endif + // + // bug # 6192. There is no point in assuming the worst. It only + // leads to lower throughput. Packets can get dropped by an + // an intermediate router for both cases (this one and the one + // above where 576 is chosen). In the above case, they will + // get dropped if two ethernet machines are communicating via + // a token ring. In this case, they will if two token ring + // machines with a frame size > max ethernet frame size are + // going over an ethernet. To fix the packet drop case, one + // should adjust the MaxPktSize Parameter of the card. + // + *MaxFrameSize = DeviceMaxFrameSize - 32; + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + case NdisMediumArcnet878_2: + + // + // For Arcnet, we always have a 3-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 3; + break; + + } + +} /* MacReturnMaxDataSize */ + +#if 0 + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: THIS FUNCTION IS REAL HACKY AND NEEDS TO BE WORKED AT. WE CAN + Improve the instruction count here - pradeepb + +--*/ + +{ + PNDIS_BUFFER SecondBuffer = NULL; + PUCHAR SecondBufferData; + UINT SecondBufferLength; + USHORT SourceSocket; + PNDIS_BUFFER ThirdBuffer = NULL; + PUCHAR ThirdBufferData; + UINT ThirdBufferLength; + + UNREFERENCED_PARAMETER (PacketLength); + + // + // First get the source socket. + // + +#if 0 + // + // Only time IncludedHeaderLength is less than the offset of + // SourceSocket in IPX header is when it 0 (from rip) + // + if (IncludedHeaderLength <= FIELD_OFFSET (IPX_HEADER, SourceSocket)) { +#endif + + // + // Get the second buffer in the packet (the ipx header is always in + // the second buffer - pradeepb. + // + // In this case + // there must be a second buffer or the packet is too + // short, so we don't check for NULL. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (SecondBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + SourceSocket = *(UNALIGNED USHORT *) + (&SecondBufferData[FIELD_OFFSET(IPX_HEADER, SourceSocket) - IncludedHeaderLength]); + +#if 0 + } +else { + + SourceSocket = IpxHeader->SourceSocket; + } +#endif + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + +#if 0 + // + // We assume the connection control flag and data stream type + // are in the same buffer. + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + } +#endif + + ConnectionControlFlag = *(SecondBufferData + (sizeof(IPX_HEADER) - IncludedHeaderLength)); + DataStreamType = *(SecondBufferData + (sizeof(IPX_HEADER) + 1 - IncludedHeaderLength)); + + +#if 0 + } else { + + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + } + +#endif + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // This would be from the rip driver, if IncludedHeaderLength is + // 0. -- pradeepb + // + // At this point, we assume that total data length is in + // the same buffer as the others. + // + // + // real hacky way of doing things. One should be using + // FIELD_OFFSET(NB_SESSION, TotalDataLength) instead of 8. + // -- pradeepb + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + TotalDataLength = *(USHORT UNALIGNED *)(SecondBufferData + (sizeof(IPX_HEADER) + 8 - IncludedHeaderLength)); + } else { + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + } + + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. + // + + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // if from rip + // + if (IncludedHeaderLength <= sizeof(IPX_HEADER) + 1) { + + + // + // Get the second buffer + // +#if 0 + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + ThirdBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&ThirdBufferData, &ThirdBufferLength); + + } +#endif + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; + + } else { + + // + // will we ever come here - pradeepb? + // + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; +#if 0 + KeepAliveSignature = ((PUCHAR)(IpxHeader+1))[1]; +#endif + + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + + } + + } + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ +#endif + + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: Improve the instruction count here - pradeepb + +--*/ + +{ + USHORT SourceSocket; + PNDIS_BUFFER DataBuffer = NULL; + PUCHAR DataBufferData; + UINT DataBufferLength; + + + // + // First get the source socket. + // + SourceSocket = IpxHeader->SourceSocket; + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + + // + // ConnectionControlFlag and DataStreamType will always follow + // IpxHeader + // + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // TotalDataLength is in the same buffer. + // + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + + // + // No need to update the WAN activity counter + // + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. It can be from rip or from + // NCP on this machine + // + // NOTE: We cannot come here for an SMB packet - [IsaacHe - 12/15]. + // + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // Get the client data buffer + // + NdisQueryPacket(Packet, NULL, NULL, &DataBuffer, NULL); + + // + // If the included header length is 0, it is from rip + // + if (IncludedHeaderLength == 0) { + + // + // Get the second buffer in the packet. The second buffer + // contains the IPX header + other stuff + // + DataBuffer = NDIS_BUFFER_LINKAGE(DataBuffer); + } else { + // + // Get the third buffer in the packet. + // + DataBuffer = NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(DataBuffer)); + } + + NdisQueryBuffer (DataBuffer, (PVOID *)&DataBufferData, &DataBufferLength); + CTEAssert(DataBufferData); + + if (IncludedHeaderLength == 0) { + KeepAliveSignature = DataBufferData[sizeof(IPX_HEADER) + 1]; + } else { + KeepAliveSignature = DataBufferData[1]; + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + } + } + + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ + +#if DBG +ULONG IpxPadCount = 0; +#endif + +#ifdef _PNP_POWER + +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called by NB/SPX to send a frame. + +Arguments: + + LocalTarget - The local target of the send - NB will have the LocalTarget in the Send_Reserved part + of the packet; SPX will not now, but will later. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + Return of IpxSendFrame + + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + PDEVICE Device = IpxDevice; + PIPX_HEADER TempHeader; + + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + + // + // Set this now, will change later + // + Reserved->CurrentNicId = 0; + + // + // Copy the LocalTarget into the send reserved area of the packet. + // + Reserved->LocalTarget = *LocalTarget; + + // + // If the NicId in the handle is 0, then this could be a send + // over all NICs in the case of NB/SPX. + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == 0) { + CTEAssert(Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX); + + // + // Check the destination network in the IPX header. If this is 0, + // then we need to iterate the send over all NICs. + // + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + if ((*(UNALIGNED ULONG *)(TempHeader->DestinationNetwork) == 0) && + ((IPX_NODE_BROADCAST(TempHeader->DestinationNode)) || + (Reserved->Identifier == IDENTIFIER_SPX))) { + + // + // Start with the first NIC + // BUGBUG: Search for the first NIC + // + + IPX_DEBUG(SEND, ("Iteration over NICs started, reserved: %lx\n", Reserved)); + Reserved->CurrentNicId = 1; + Reserved->Net0SendSucceeded = FALSE; + + FILL_LOCAL_TARGET(&Reserved->LocalTarget, 1); + Reserved->PacketLength = PacketLength; + } else { + // + // If this is on the loopback adapter (Nic 0), queue it on the LoopbackQueue; + // if the LoopbackRtn is not started, start it now. + // + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Recalculate packet counts here. + // + + // + // Assume an 802_3802_2 header and use that length. + // + + // + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, 17); + NdisRecalculatePacketCounts (Packet); + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + return IpxSendFrame ( + &Reserved->LocalTarget, + Packet, + PacketLength, + IncludedHeaderLength); + + } else { + return IpxSendFrame ( + LocalTarget, + Packet, + PacketLength, + IncludedHeaderLength); + } +} +#endif + + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + BUGBUG: Check that Binding is not NULL. + +Arguments: + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PDEVICE Device = IpxDevice; + PUCHAR Header; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + ULONG TwoBytes; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + ULONG HeaderLength=0; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + UCHAR SourceRoutingIdentifier; + ULONG HeaderSizeRequired; + PIPX_HEADER TempHeader; + USHORT PktLength; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + // + // Get the lock on the binding array + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &LocalTarget->NicHandle); + + if (Binding == NULL) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG(PNP, ("Invalid NIC handle: %lx\n", LocalTarget->NicHandle)); + // + // [BUGBUGZZ] Return a unique error that NB/SPX see and re-query the NicId. + // + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + Adapter = Binding->Adapter; + + IpxReferenceAdapter(Adapter); + + // + // Release the lock + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + + // + // If this is on the loopback adapter (Nic 0), queue it on the LoopbackQueue; + // if the LoopbackRtn is not started, start it now. + // + if (LocalTarget->NicId == 0) { + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Assume an 802_3802_2 header and use that length. + // + + // + // Adjust the MAC header's length to the right value + // + // NdisAdjustBufferLength (HeaderBuffer, 17); + + NdisRecalculatePacketCounts (Packet); + + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding == NULL) { + return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? + } + Adapter = Binding->Adapter; +#endif _PNP_POWER + + // + // For IPX and other protocols that are guaranteed to have allocated + // the header from non-paged pool, use the buffer directly. For others, + // query the packet for the pointer to the MDL. + // + if (Reserved->Identifier >= IDENTIFIER_IPX) { + HeaderBuffer = Reserved->HeaderBuffer; + Header = Reserved->Header; + + } else { + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + } + + CTEAssert (Reserved->PaddingBuffer == NULL); + + // + // First move the packet around if needed. + // + + if (Reserved->Identifier < IDENTIFIER_IPX) { + + // + // Only RIP will have IncludedHeaderLength as 0. I don't know + // why we have the comment about RIP inside this if statement. + // + if (IncludedHeaderLength > 0) { + + // + // Spx can handle a virtual net as long as it is + // not 0. Netbios always needs to use the real address. + // We need to hack the ipx source address for packets + // which are sent by spx if we have a fake virtual + // net, and packets sent by netbios unless we are + // bound to only one card. + // + + // + // We handle binding sets as follows, based on who + // sent the frame to us: + // + // RIP: Since we only tell RIP about the masters at + // bind time, and hide slaves on indications, it should + // never be sending on a slave binding. Since RIP knows + // the real net and node of every binding we don't + // need to modify the packet at all. + // + // NB: For broadcasts we want to put the first card's + // address in the IPX source but round-robin the + // actual sends over all cards (broadcasts shouldn't + // be passed in with a slave's NIC ID). For directed + // packets, which may come in on a slave, we should + // put the slave's address in the IPX source. + // + // SPX: SPX does not send broadcasts. For directed + // frames we want to use the slave's net and node + // in the IPX source. + // + + if (Reserved->Identifier == IDENTIFIER_NB) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + // + // Get the packet length from the ipx header. Compare with + // the max. allowed datagram size. + // + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + PktLength = ((TempHeader->PacketLength[0] << 8) | + (TempHeader->PacketLength[1])); + +// +// BUGBUG - Not the most efficient way to do this. NWLNKNB should do this. +// Doing it in ipx means doing it for all packets (even those sent on +// connections). Will remove this later when nwlnknb change has been +// tested. +// + + + if (PktLength > (Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + PktLength, + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))); + +#ifdef _PNP_POWER + // + // Dereference the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_INVALID_BUFFER_SIZE; + } + + if (Device->ValidBindings > 1) { + + + // + // Store this now, since even if we round-robin the + // actual send we want the binding set master's net + // and node in the IPX source address. + // + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->BindingSetMember) { + + if (IPX_NODE_BROADCAST(LocalTarget->MacAddress)) { + + // + // This is a broadcast, so we round-robin the + // sends through the binding set. + // +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + Adapter = Binding->Adapter; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxReferenceAdapter(Adapter); +#endif + } + + } + } + + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + + // + // Need to update this if we have multiple cards but + // a zero virtual net. + // + + if (Device->MultiCardZeroVirtual) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + } + + } else { + + // + // For a rip packet it should not be in a binding set, + // or if it is it should be the master. + // +#if DBG + CTEAssert ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding)); +#endif + } + + +#if 0 + // + // There is a header included, we need to adjust it. + // The header will be at Device->IncludedHeaderOffset. + // + + if (LocalTarget->MacAddress[0] & Adapter->MacInfo.BroadcastMask) { + HeaderSizeRequired = Adapter->BcMcHeaderSizes[Binding->FrameType]; + } else { + HeaderSizeRequired = Adapter->DefHeaderSizes[Binding->FrameType]; + } + + if (HeaderSizeRequired != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + &Header[HeaderSizeRequired], + &Header[Device->IncludedHeaderOffset], + IncludedHeaderLength); + } +#endif + } + } + + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + + if (!Binding->LineUp) { + // + // Bug #17273 return proper error message + // + // return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_NETWORK_UNREACHABLE; + } + + if (Adapter->MacInfo.MediumAsync) { + + IPX_HEADER UNALIGNED * IpxHeader; + PNDIS_BUFFER IpxNdisBuff; + UINT IpxHeaderLen; + +#if 0 + // + // The header should have been moved here. + // + + CTEAssert(Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] == + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]); + + + IpxHeader = (IPX_HEADER UNALIGNED *) + (&Header[Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]]); +#endif + // + // The Ipx header is always the second ndis buffer in the mdl + // chain. Get it and then query the va of the same. + // + IpxNdisBuff = NDIS_BUFFER_LINKAGE(HeaderBuffer); + NdisQueryBuffer (IpxNdisBuff, (PVOID *)&IpxHeader, &IpxHeaderLen); +// IpxHeader = (IPX_HEADER UNALIGNED *) (&Header[MAC_HEADER_SIZE]); + + // + // If this is a type 20 name frame from Netbios and we are + // on a dialin WAN line, drop it if configured to. + // + // The 0x01 bit of DisableDialinNetbios controls + // internal->WAN packets, which we handle here. + // + // + + // + // SS# 33592: In case of iterative sends, the IncludedHeaderLength is not set properly + // since we dont keep track of the length that came in the first time (we track the PacketLength + // however). The included length field is used here for checking for NB_NAME_FRAMES, but elsewhere + // used only to distinguish between whether RIP or NB/SPX sent the packet (IncludedHeaderLen ==0 for RIP) + // The ideal solution here is to do way with this field altogether, but for the beta we will just use the + // PacketLength field for comparison here since we are assured that this will be equal to the InclHeaderLen + // for any type 0x14 packet that comes down from NB. + // + // BUGBUGZZ: Remove the IncludedHeaderLength field. + // + if ((!Binding->DialOutAsync) && + (Reserved->Identifier == IDENTIFIER_NB) && + // (IncludedHeaderLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + (PacketLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + ((Device->DisableDialinNetbios & 0x01) != 0) && + (IpxHeader->PacketType == 0x14)) { +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_SUCCESS; + } + + + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact sap and ncp + // packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + IpxHeader, + IncludedHeaderLength, + Packet, + PacketLength); + + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + + } else { + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + } + + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + + case ISN_FRAME_TYPE_802_2: + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_802_3: + TwoBytes = PacketLength; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_ETHERNET_II: + TwoBytes = Adapter->BindSap; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_SNAP: + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + //BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + // + // Pad odd-length packets if needed. + // + + if ((((PacketLength + HeaderLength) & 1) != 0) && + (Device->EthernetPadToEven) && + (!Adapter->MacInfo.MediumAsync)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + if ( CurBuffer == HeaderBuffer ) { + BufferLength++; // Just bump this length + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + if (TwoBytes != Adapter->BindSap) { + CTEAssert(TwoBytes & 1); + TwoBytes += 1; + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + } + +#if DBG + ++IpxPadCount; +#endif + } + + break; + + case NdisMedium802_5: + + if (Reserved->Identifier >= IDENTIFIER_IPX) { + + DestinationType = Reserved->DestinationType; + SourceRoutingIdentifier = IDENTIFIER_IPX; + + } else { + + if (LocalTarget->MacAddress[0] & 0x80) { + if (*(UNALIGNED ULONG *)(&LocalTarget->MacAddress[2]) != 0xffffffff) { + DestinationType = DESTINATION_MCAST; + } else { + DestinationType = DESTINATION_BCAST; + } + } else { + DestinationType = DESTINATION_DEF; + } + SourceRoutingIdentifier = Reserved->Identifier; + + } + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + SourceRoutingIdentifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Binding->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_802_3: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_SNAP: + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + + break; + + case NdisMediumFddi: + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_3: + HeaderLength = 13; + break; + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + HeaderLength = 16; + break; + case ISN_FRAME_TYPE_SNAP: + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + HeaderLength = 21; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + case NdisMediumArcnet878_2: + + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Binding->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + + // + // Binding->FrameType is not used. + // + + HeaderLength = 3; +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + } + + // + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, BufferLength); + NdisRecalculatePacketCounts (Packet); + +#if 0 + { + PMDL mdl; + mdl = (PMDL)NDIS_BUFFER_LINKAGE(HeaderBuffer); + if (mdl) { + + KdPrint(("**Bytecount %x %x\n",mdl->ByteCount, mdl)); + if ((LONG)mdl->ByteCount < 0) { + DbgBreakPoint(); + } + } + } +#endif + +#if DBG + { + ULONG SendFlag; + ULONG Temp; + PNDIS_BUFFER FirstPacketBuffer; + PNDIS_BUFFER SecondPacketBuffer; + IPX_HEADER DumpHeader; + UCHAR DumpData[14]; + + NdisQueryPacket (Packet, NULL, NULL, &FirstPacketBuffer, NULL); + SecondPacketBuffer = NDIS_BUFFER_LINKAGE(FirstPacketBuffer); + TdiCopyMdlToBuffer(SecondPacketBuffer, 0, &DumpHeader, 0, sizeof(IPX_HEADER), &Temp); + if (Reserved->Identifier == IDENTIFIER_NB) { + SendFlag = IPX_PACKET_LOG_SEND_NB; + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + SendFlag = IPX_PACKET_LOG_SEND_SPX; + } else if (Reserved->Identifier == IDENTIFIER_RIP) { + SendFlag = IPX_PACKET_LOG_SEND_RIP; + } else { + if (DumpHeader.SourceSocket == IpxPacketLogSocket) { + SendFlag = IPX_PACKET_LOG_SEND_SOCKET | IPX_PACKET_LOG_SEND_OTHER; + } else { + SendFlag = IPX_PACKET_LOG_SEND_OTHER; + } + } + +#if 0 + if (PACKET_LOG(SendFlag)) { + + TdiCopyMdlToBuffer(SecondPacketBuffer, sizeof(IPX_HEADER), &DumpData, 0, 14, &Temp); + + IpxLogPacket( + TRUE, + LocalTarget->MacAddress, + Binding->LocalMacAddress.Address, + (USHORT)PacketLength, + &DumpHeader, + DumpData); + + } +#endif + } +#endif + + ++Device->Statistics.PacketsSent; + + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + +#ifdef _PNP_POWER + if (Status != STATUS_PENDING) { + + if (Reserved->PaddingBuffer) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } + + // + // If this was an NB/SPX packet, and there was an + // iterative send going on, then call the SendComplete + // handler. + // + if ((Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX) && + (Reserved->CurrentNicId)) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + Status); + + Status = STATUS_PENDING; + } + } +#else + if ((Status != STATUS_PENDING) && + (Reserved->PaddingBuffer)) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } +#endif + +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + + return Status; + +} /* IpxSendFrame */ + + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + + // + // BUGBUG: Remove the IncludedHeaderLength parameter from here + // +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + LONG HeaderLength; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + IPX_DEBUG(SEND,("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++PacketLength; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(PacketLength / 256); + Header[13] = (UCHAR)(PacketLength % 256); + + //NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_3 */ + + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17, Packet); + IPX_DEBUG(SEND, ("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); + IPX_DEBUG(SEND, ("packet=%x, usermdl %x\n",Packet,Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0 ) { +#if BACK_FILL + if (0) { +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 17); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 17); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_2 */ + + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + +#if DBG + ++IpxPadCount; +#endif + + } + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3EthernetII */ + + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 22); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 22); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 22); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3Snap */ + + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17, Packet); +#endif BACK_FILL + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + + //PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5802_2 */ + + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22, Packet); +#endif BACK_FILL + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + } + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5Snap */ + + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 13, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 13); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 13); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 13); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_3 */ + + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 16, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 16); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 16); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 16); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_2 */ + + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 21, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 21); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 21); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 21); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiSnap */ + + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMARCNET878_2 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 3, Packet); +#endif BACK_FILL + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Adapter->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 3); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 3); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 3); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiArcnet878_2 */ + + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMWAN FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(IpxDevice, NIC_FROM_LOCAL_TARGET(LocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + +#else + PBINDING Binding = IpxDevice->Bindings[LocalTarget->NicId]; +#endif _PNP_POWER + + if (Binding->LineUp) { + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + + // + // Call UpdateWanInactivity only if this is not a backfill packet, since + // SMB server does not do KeepAlives. In case, of backfilled packets, reset + // the counter regardless. + // + if (!Reserved->BackFill) { + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); + } else { + Binding->WanInactivityCounter = 0; + } + +#else + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact netbios packets + // and rip packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); +#endif BACK_FILL + + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return Status; + + } else { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_NETWORK_UNREACHABLE; + } + +} /* IpxSendFrameWanEthernetII */ + + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called when a valid IPX frame is received from + a remote. It gives the source routing database a change to + update itself to include information about this remote. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Adapter - The adapter the frame was received on. + + MacHeader - The MAC header of the received frame. + + MacHeaderLength - The length of the MAC header. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + CTEAssert ((Database >= 0) && (Database <= 3)); + + // + // If this adapter is configured for no source routing, don't + // need to do anything. + // + + if (!Adapter->SourceRouting) { + return; + } + + // + // See if this source routing is relevant. We don't + // care about two-byte source routing since that + // indicates it did not cross a router. If there + // is nothing in the database, then don't add + // this if it is minimal (if it is not, we need + // to add it so we will find it on sending). + // + + if ((Adapter->SourceRoutingEmpty[Database]) && + (MacHeaderLength <= 16)) { + return; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + // + // Try to find this address in the database. + // + + Hash = MacSourceRoutingHash (MacHeader+8); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacHeader+8, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. If the data is the + // same as what we have, update the time since used to + // prevent aging. + // + + if ((Current->SourceRoutingLength == MacHeaderLength-14) && + (RtlEqualMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength-14))) { + + Current->TimeSinceUsed = 0; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + } + + } + + // + // Not found, insert a new node at the front of the list. + // + + Current = (PSOURCE_ROUTE)IpxAllocateMemory (SOURCE_ROUTE_SIZE(MacHeaderLength-14), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + if (Current == (PSOURCE_ROUTE)NULL) { + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + } + + Current->Next = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current; + + Adapter->SourceRoutingEmpty[Database] = FALSE; + + RtlCopyMemory (Current->MacAddress, MacHeader+8, 6); + Current->MacAddress[0] &= 0x7f; + Current->SourceRoutingLength = (UCHAR)(MacHeaderLength - 14); + RtlCopyMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength - 14); + + Current->TimeSinceUsed = 0; + + IPX_DEBUG (SOURCE_ROUTE, ("Adding source route %lx for %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + Current, Current->MacAddress[0], Current->MacAddress[1], + Current->MacAddress[2], Current->MacAddress[3], + Current->MacAddress[4], Current->MacAddress[5])); + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacUpdateSourceRouting */ + + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR MacAddress[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine looks up a target address in the adapter's + source routing database to see if source routing information + needs to be added to the frame. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Binding - The binding the frame is being sent on. + + MacAddress - The destination address. + + SourceRouting - Buffer to hold the returned source routing info. + + SourceRoutingLength - The returned source routing length. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // If this adapter is configured for no source routing, don't + // insert any. + // + + if (!Adapter->SourceRouting) { + *SourceRoutingLength = 0; + return; + } + + // + // See if source routing has not been important so far. + // + // BUGBUG: This is wrong because we may be sending a directed + // packet to somebody on the other side of a router, without + // ever having received a routed packet. We fix this for the + // moment by only setting SourceRoutingEmpty for netbios + // which uses broadcasts for discovery. + // + + if (Adapter->SourceRoutingEmpty[Database]) { + *SourceRoutingLength = 0; + return; + } + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. + // + + if (Current->SourceRoutingLength <= 2) { + *SourceRoutingLength = 0; + } else { + RtlCopyMemory (SourceRouting, Current->SourceRouting, Current->SourceRoutingLength); + SourceRouting[0] = (SourceRouting[0] & TR_LENGTH_MASK); + SourceRouting[1] = (SourceRouting[1] ^ TR_DIRECTION_MASK); + *SourceRoutingLength = Current->SourceRoutingLength; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + // + // We did not find this node, use the default. + // + + if (Binding->AllRouteDirected) { + RtlCopyMemory (SourceRouting, AllRouteSourceRouting, 2); + } else { + RtlCopyMemory (SourceRouting, SingleRouteSourceRouting, 2); + } + *SourceRoutingLength = 2; + +} /* MacLookupSourceRouting */ + + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the source routing timer expires. + It is called every minute. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PADAPTER Adapter; + PBINDING Binding; + PSOURCE_ROUTE Current, OldCurrent, Previous; + UINT i, j, k; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + +#ifdef _PNP_POWER + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + // + // Get a lock on the access path. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + ++Device->SourceRoutingTime; + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + ++Device->SourceRoutingTime; + + for (i = 1; i <= Device->ValidBindings; i++) { + + if (Binding = Device->Bindings[i]) { +#endif _PNP_POWER + + Adapter = Binding->Adapter; + + if (Adapter->LastSourceRoutingTime != Device->SourceRoutingTime) { + + // + // We need to scan this adapter's source routing + // tree for stale routes. To simplify the scan we + // only delete entries that have at least one + // child that is NULL. + // + + Adapter->LastSourceRoutingTime = Device->SourceRoutingTime; + + for (j = 0; j < IDENTIFIER_TOTAL; j++) { + + for (k = 0; k < SOURCE_ROUTE_HASH_SIZE; k++) { + + if (Adapter->SourceRoutingHeads[j][k] == (PSOURCE_ROUTE)NULL) { + continue; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + Current = Adapter->SourceRoutingHeads[j][k]; + Previous = (PSOURCE_ROUTE)NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + ++Current->TimeSinceUsed; + + if (Current->TimeSinceUsed >= Device->SourceRouteUsageTime) { + + // + // A stale entry needs to be aged. + // + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[j][k] = Current->Next; + } + + OldCurrent = Current; + Current = Current->Next; + + IPX_DEBUG (SOURCE_ROUTE, ("Aging out source-route entry %lx\n", OldCurrent)); + IpxFreeMemory (OldCurrent, SOURCE_ROUTE_SIZE (OldCurrent->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } else { + + Previous = Current; + Current = Current->Next; + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + } // for loop through the database's hash list + + } // for loop through the adapter's four databases + + } // if adapter's database needs to be checked + + } // if binding exists + + } // for loop through every binding + } + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Now restart the timer unless we should not (which means + // we are being unloaded). + // + + if (Device->SourceRoutingUsed) { + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + + } else { + + IpxDereferenceDevice (Device, DREF_SR_TIMER); + } + +} /* MacSourceRoutingTimeout */ + + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + MAC address should be removed. + +Arguments: + + Binding - The binding to modify. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + + PSOURCE_ROUTE Current, Previous; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + ULONG Database; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through to find the matching entry in each database. + // + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Previous = NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + } + + IPX_DEBUG (SOURCE_ROUTE, ("IPXROUTE freeing source-route entry %lx\n", Current)); + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + break; + + } else { + + Previous = Current; + Current = Current->Next; + + } + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingRemove */ + + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + binding should be cleared entirely. + +Arguments: + + Binding - The binding to be cleared. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Database, Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through and remove every entry in the database. + // + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } + } + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingClear */ + + + diff --git a/private/ntos/tdi/isnp/ipx/mac.h b/private/ntos/tdi/isnp/ipx/mac.h new file mode 100644 index 000000000..a88e77ecd --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mac.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Revision History: + +--*/ + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the mac.c routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _NDIS_INFORMATION { + + NDIS_MEDIUM MediumType; + NDIS_MEDIUM RealMediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + UCHAR BroadcastMask; + ULONG CopyLookahead; + ULONG MacOptions; + ULONG MinHeaderLength; + ULONG MaxHeaderLength; + +} NDIS_INFORMATION, * PNDIS_INFORMATION; + + +#define TR_SOURCE_ROUTE_FLAG 0x80 + +#define ARCNET_PROTOCOL_ID 0xFA + diff --git a/private/ntos/tdi/isnp/ipx/mp/makefile b/private/ntos/tdi/isnp/ipx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf b/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isnp/ipx/mp/sources b/private/ntos/tdi/isnp/ipx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/ipx/ndis.c b/private/ntos/tdi/isnp/ipx/ndis.c new file mode 100644 index 000000000..14066a786 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ndis.c @@ -0,0 +1,2204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ndis.c + +Abstract: + + This module contains code which implements the routines used to + initialize the IPX <-> NDIS interface, as well as most of the + interface routines. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + 1. Added the ReceivePacketHandler to the ProtChars. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + + Tony Bell (TonyBe) 10-Dec-1995 + Changes to support new NdisWan Lineup. + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ); + +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxRegisterProtocol) +#pragma alloc_text(INIT,IpxInitializeNdis) +#endif +#endif + + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + NameString - The name of the transport. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + + // + // Set up the characteristics of this protocol + // +#if NDIS40 + ProtChars.MajorNdisVersion = 4; + + ProtChars.ReceivePacketHandler = IpxReceivePacket; +#else + ProtChars.MajorNdisVersion = 3; +#endif + ProtChars.MinorNdisVersion = 0; + + ProtChars.Name = *NameString; + + ProtChars.OpenAdapterCompleteHandler = IpxOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = IpxCloseAdapterComplete; + ProtChars.ResetCompleteHandler = IpxResetComplete; + ProtChars.RequestCompleteHandler = IpxRequestComplete; + + ProtChars.SendCompleteHandler = IpxSendComplete; + ProtChars.TransferDataCompleteHandler = IpxTransferDataComplete; + + ProtChars.ReceiveHandler = IpxReceiveIndication; + ProtChars.ReceiveCompleteHandler = IpxReceiveComplete; + ProtChars.StatusHandler = IpxStatus; + ProtChars.StatusCompleteHandler = IpxStatusComplete; + +#ifdef _PNP_POWER + ProtChars.BindAdapterHandler = IpxBindAdapter; + ProtChars.UnbindAdapterHandler = IpxUnbindAdapter; + ProtChars.TranslateHandler = IpxTranslate; +#endif // _PNP_POWER + + NdisRegisterProtocol ( + &ndisStatus, + &IpxNdisProtocolHandle, + &ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; + +} /* IpxRegisterProtocol */ + + +VOID +IpxDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (IpxNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + IpxNdisProtocolHandle); + IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + } + +} /* IpxDeregisterProtocol */ + + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + Adapter - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + Adapter->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx failed %lx\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid, + NdisStatus)); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + + } else { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx succeeded\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid)); + } + + return NdisStatus; + +} /* IpxSubmitNdisRequest */ + + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + Adapter - Structure describing this binding. + + ConfigAdapter - Configuration information for this binding. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM IpxSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumArcnet878_2, NdisMediumWan }; + UINT SelectedMedium; + NDIS_REQUEST IpxRequest; + ULONG MinimumLookahead; + UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x81, 0x37 }; + UCHAR FunctionalAddress[4] = { 0x00, 0x80, 0x00, 0x00 }; + ULONG WanHeaderFormat = NdisWanHeaderEthernet; + NDIS_OID IpxOid; + ULONG MacOptions; + ULONG PacketFilter; + PNDIS_STRING AdapterString = &ConfigBinding->AdapterName; + + // + // Initialize this adapter for IPX use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + Adapter->NdisBindingHandle = NULL; + + OpenErrorStatus = 0; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &Adapter->NdisBindingHandle, + &SelectedMedium, + IpxSupportedMedia, + sizeof (IpxSupportedMedia) / sizeof(NDIS_MEDIUM), + IpxNdisProtocolHandle, + (NDIS_HANDLE)Adapter, + &ConfigBinding->AdapterName, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + OpenErrorStatus = Adapter->OpenErrorStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("Open %ws failed %lx\n", ConfigBinding->AdapterName.Buffer, NdisStatus)); + + IpxWriteGeneralErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 1, + &OpenErrorStatus); + return STATUS_INSUFFICIENT_RESOURCES; + + } else { + + IPX_DEBUG (NDIS, ("Open %ws succeeded\n", ConfigBinding->AdapterName.Buffer)); + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + IpxSupportedMedia[SelectedMedium], + &Adapter->MacInfo); + + + switch (Adapter->MacInfo.RealMediumType) { + + case NdisMedium802_3: + + IpxOid = OID_802_3_CURRENT_ADDRESS; + break; + + case NdisMedium802_5: + + IpxOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + IpxOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + case NdisMediumArcnet878_2: + + IpxOid = OID_ARCNET_CURRENT_ADDRESS; + break; + + case NdisMediumWan: + + IpxOid = OID_WAN_CURRENT_ADDRESS; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = IpxOid; + + if (IpxOid != OID_ARCNET_CURRENT_ADDRESS) { + + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = Adapter->LocalMacAddress.Address; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + } else { + + // + // We take the arcnet single-byte address and right-justify + // it in a field of zeros. + // + + RtlZeroMemory (Adapter->LocalMacAddress.Address, 5); + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->LocalMacAddress.Address[5]; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 1; + + } + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the maximum packet sizes. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxReceivePacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxSendPacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Query the receive buffer space. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_RECEIVE_BUFFER_SPACE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->ReceiveBufferSpace); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now set the minimum lookahead size. The value we choose + // here is the 128 needed for TDI indications, plus the size + // of the IPX header, plus the largest extra header possible + // (a SNAP header, 8 bytes), plus the largest higher-level + // header (I think it is a Netbios datagram, 34 bytes). + // + // BETABUGBUG: Adapt this based on higher-level bindings and + // configured frame types. + // + + MinimumLookahead = 128 + sizeof(IPX_HEADER) + 8 + 34; + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MediumSpeed); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // For wan, specify our protocol ID and header format. + // We don't query the medium subtype because we don't + // case (since we require ethernet emulation). + // + + if (Adapter->MacInfo.MediumAsync) { + + if (Adapter->BindSap != 0x8137) { + *(UNALIGNED USHORT *)(&WanProtocolId[4]) = Adapter->BindSapNetworkOrder; + } + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Now query the line count. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_LINE_COUNT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->WanNicIdCount; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + Adapter->WanNicIdCount = 1; + } + + if (Adapter->WanNicIdCount == 0) { + + IPX_DEBUG (NDIS, ("OID_WAN_LINE_COUNT returned 0 lines\n")); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_QUERY_OID_FAILED, + NDIS_STATUS_INVALID_DATA, + AdapterString->Buffer, + OID_WAN_LINE_COUNT); + + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + + } + } + + + // + // For 802.5 adapter's configured that way, we enable the + // functional address (C0-00-00-80-00-00). + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->EnableFunctionalAddress)) { + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = FunctionalAddress; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Now query the MAC's optional characteristics. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Adapter->MacInfo.CopyLookahead = + ((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0; + Adapter->MacInfo.MacOptions = MacOptions; + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMedium802_5: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMediumFddi: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + break; + + case NdisMediumArcnet878_2: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + break; + + } + + // + // BUGBUG: If functional filtering is set, set the address + // for the appropriate binding. + // + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumFddi: + case NdisMedium802_5: + case NdisMediumArcnet878_2: + + // + // If we have a virtual network number we need to receive + // broadcasts (either the router will be bound in which + // case we want them, or we need to respond to rip requests + // ourselves). + // + + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + + if (Adapter->Device->VirtualNetworkNumber != 0) { + + Adapter->BroadcastEnabled = TRUE; + Adapter->Device->EnableBroadcastCount = 1; + PacketFilter |= NDIS_PACKET_TYPE_BROADCAST; + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter |= NDIS_PACKET_TYPE_FUNCTIONAL; + } + + } else { + + Adapter->BroadcastEnabled = FALSE; + Adapter->Device->EnableBroadcastCount = 0; + + } + + break; + + default: + + CTEAssert (FALSE); + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* IpxInitializeNdis */ + + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when another reason for enabling + broadcast reception is added. If it is the first, then + reception on the card is enabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + ++Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 1) { + + // + // Broadcasts should be enabled. + // + + if (!Device->EnableBroadcastPending) { + + if (Device->DisableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxAddBroadcast */ + + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when a reason for enabling + broadcast reception is removed. If it is the last, then + reception on the card is disabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + --Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 0) { + + // + // Broadcasts should be disabled. + // + + if (!Device->DisableBroadcastPending) { + + if (Device->EnableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxRemoveBroadcast */ + + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine is used to change whether broadcast reception + is enabled or disabled. It performs the requested operation + on every adapter bound to by IPX. + + This routine is called by a worker thread queued when a + bind/unbind operation changes the broadcast state. + +Arguments: + + Parameter - TRUE if broadcasts should be enabled, FALSE + if they should be disabled. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + BOOLEAN Enable = (BOOLEAN)Parameter; + UINT i; + PBINDING Binding; + PADAPTER Adapter; + ULONG PacketFilter; + NDIS_REQUEST IpxRequest; + NDIS_STRING AdapterName; + CTELockHandle LockHandle; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); +#else + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; +#endif + if (Binding == NULL) { + continue; + } + + Adapter = Binding->Adapter; + if (Adapter->BroadcastEnabled == Enable) { + continue; + } + + if (Enable) { + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_FUNCTIONAL); + } else { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST); + } + } else { + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + AdapterName.Buffer = Adapter->AdapterName; + AdapterName.Length = (USHORT)Adapter->AdapterNameLength; + AdapterName.MaximumLength = (USHORT)(Adapter->AdapterNameLength + sizeof(WCHAR)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + (VOID)IpxSubmitNdisRequest (Adapter, &IpxRequest, &AdapterName); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + Adapter->BroadcastEnabled = Enable; + + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Enable) { + + CTEAssert (Device->EnableBroadcastPending); + Device->EnableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } else { + + CTEAssert (Device->DisableBroadcastPending); + Device->DisableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } + + CTEFreeLock (&Device->Lock, LockHandle); + +}/* IpxBroadcastOperation */ + + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in IpxInitializeNdis. + It is written so that it can be called from within IpxInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (Adapter->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + Adapter->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + +#if 0 + if (Adapter->SendPacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->SendPacketPoolHandle); + } + + if (Adapter->ReceivePacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->ReceivePacketPoolHandle); + } + + if (Adapter->NdisBufferPoolHandle != NULL) { + NdisFreeBufferPool (Adapter->NdisBufferPoolHandle); + } +#endif + +} /* IpxCloseNdis */ + + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + Adapter->OpenErrorStatus = OpenErrorStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxOpenAdapterComplete */ + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxCloseAdapterComplete */ + + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + +} /* IpxResetComplete */ + + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxRequestComplete */ + + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PADAPTER Adapter, TmpAdapter; + + PNDIS_WAN_LINE_UP LineUp; + PNDIS_WAN_LINE_DOWN LineDown; + PIPXCP_CONFIGURATION Configuration; // contains ipx net and node + + BOOLEAN UpdateLineUp; + PBINDING Binding, TmpBinding; + PDEVICE Device; + PADDRESS Address; + ULONG CurrentHash; + PIPX_ROUTE_ENTRY RouteEntry; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + PIPX_ADDRESS_DATA IpxAddressData; + PREQUEST Request; + UINT BufferLength; + IPX_LINE_INFO LineInfo; + ULONG Segment; + ULONG LinkSpeed; + PLIST_ENTRY p; + NTSTATUS Status; + UINT i, j; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + NTSTATUS ntStatus; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + Adapter = (PADAPTER)NdisBindingContext; + + IpxReferenceAdapter(Adapter); +#else + Adapter = (PADAPTER)NdisBindingContext; +#endif + + Device = Adapter->Device; + + switch (NdisStatus) { + + case NDIS_STATUS_WAN_LINE_UP: + + + // + // If the line is already up, then we are just getting + // a change in line conditions, and the IPXCP_CONFIGURATION + // information is not included. If it turns out we need + // all the info, we check the size again later. + // + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_UP)) { + IPX_DEBUG (WAN, ("Line up, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_UP))); +#ifdef _PNP_POWER + goto error_no_lock; +#else + return; +#endif + } + + LineUp = (PNDIS_WAN_LINE_UP)StatusBuffer; + + // + // We scan through the adapter's NIC ID range looking + // for an active binding with the same remote address. + // + + UpdateLineUp = FALSE; + + // + // See if this is a new lineup or not + // + *((ULONG UNALIGNED *)(&Binding)) = + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Binding != NULL) { + UpdateLineUp = TRUE; + } + + if (LineUp->ProtocolType != Adapter->BindSap) { + IPX_DEBUG (WAN, ("Line up, wrong protocol type %lx\n", LineUp->ProtocolType)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; +#else + return; +#endif + } + + Configuration = (PIPXCP_CONFIGURATION)LineUp->ProtocolBuffer; + + // + // PNP_POWER - We hold the exclusive lock to the binding array (thru both the device and adapter) + // and the reference to the adapter at this point. + // + + // + // If this line was previously down, create a new binding + // if needed. + // + + if (!UpdateLineUp) { + + // + // We look for a binding that is allocated but down, if + // we can't find that then we look for any empty spot in + // the adapter's NIC ID range and allocate a binding in it. + // Since we always allocate this way, the allocated + // bindings are all clumped at the beginning and once + // we find a NULL spot we know there are no more + // allocated ones. + // + // We keep track of the first binding on this adapter + // in TmpBinding in case we need config info from it. + // + + TmpBinding = NULL; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (i = Adapter->FirstWanNicId; + i <= Adapter->LastWanNicId; + i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if (TmpBinding == NULL) { + TmpBinding = Binding; + } + + if ((Binding == NULL) || + (!Binding->LineUp)) { + break; + } + } + + if (i > Adapter->LastWanNicId) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, no WAN binding available\n")); + return; + } + + if (Binding == NULL) { + + // + // We need to allocate one. + // + + CTEAssert (TmpBinding != NULL); + +#ifdef _PNP_POWER + // + // CreateBinding does an InterLockedPop with the DeviceLock. + // So, release the lock here. + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + Status = IpxCreateBinding( + Device, + NULL, + 0, + Adapter->AdapterName, + &Binding); + + if (Status != STATUS_SUCCESS) { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + goto error_no_lock; +#else + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + return; +#endif + } + +#ifdef _PNP_POWER + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + // + // Binding->AllRouteXXX doesn't matter for WAN. + // + + Binding->FrameType = ISN_FRAME_TYPE_ETHERNET_II; + Binding->SendFrameHandler = IpxSendFrameWanEthernetII; + ++Adapter->BindingCount; + Binding->Adapter = Adapter; + + Binding->NicId = i; +#ifdef _PNP_POWER + INSERT_BINDING(Device, i, Binding); +#else + Device->Bindings[i] = Binding; +#endif + + // + // Other fields are filled in below. + // + + } + + // + // This is not an update, so note that the line is active. + // + + Binding->LineUp = TRUE; + + if (Configuration->ConnectionClient == 1) { + Binding->DialOutAsync = TRUE; + } else { + Binding->DialOutAsync = FALSE; + } + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + if ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0)) { + + Device->HighestType20NicId = i; + } + } + + // + // We could error out below, trying to insert this network number. In RipShortTimeout + // we dont check for LineUp when calculating the tick counts; set this before the insert + // attempt. + // + Binding->MediumSpeed = LineUp->LinkSpeed; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Add a router entry for this net if there is no router. + // We want the number of ticks for a 576-byte frame, + // given the link speed in 100 bps units, so we calculate + // as: + // + // seconds 18.21 ticks 4608 bits + // --------------------- * ----------- * --------- + // link_speed * 100 bits second frame + // + // to get the formula + // + // ticks/frame = 839 / link_speed. + // + // We add link_speed to the numerator also to ensure + // that the value is at least 1. + // + + if ((!Device->UpperDriverBound[IDENTIFIER_RIP]) && + (*(UNALIGNED ULONG *)Configuration->Network != 0)) { + if (RipInsertLocalNetwork( + *(UNALIGNED ULONG *)Configuration->Network, + Binding->NicId, + Adapter->NdisBindingHandle, + (USHORT)((839 + LineUp->LinkSpeed) / LineUp->LinkSpeed)) != STATUS_SUCCESS) { + // + // This means we couldn't allocate memory, or + // the entry already existed. If it already + // exists we can ignore it for the moment. + // + // BUGBUG: Now it will succeed if the network + // exists. + // + + IPX_DEBUG (WAN, ("Line up, could not insert local network\n")); + Binding->LineUp = FALSE; +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; +#else + return; +#endif + } + } + + + // + // Update our addresses. + // + Binding->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + RtlCopyMemory (Binding->WanRemoteNode, Configuration->RemoteNode, 6); + + // + // Return the binding context for this puppy! + // + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])) = + *((ULONG UNALIGNED *)(&Binding)); + + RtlCopyMemory (Binding->LocalMacAddress.Address, LineUp->LocalAddress, 6); + RtlCopyMemory (Binding->RemoteMacAddress.Address, LineUp->RemoteAddress, 6); + + // + // Update the device node and all the address + // nodes if we have only one bound, or this is + // binding one. + // + + if (!Device->VirtualNetwork) { + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + Device->SourceAddress.NetworkAddress = *(UNALIGNED ULONG *)(Configuration->Network); + RtlCopyMemory (Device->SourceAddress.NodeAddress, Configuration->LocalNode, 6); + } + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Address->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Reset this since the line just came up. + // + + Binding->WanInactivityCounter = 0; + + } + + LinkSpeed = LineUp->LinkSpeed; + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count WAN + // bindings when doing this (although it is unlikely + // a LAN binding would be the winner). + // + // BUGBUG: Update other device information? + // + + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + +#ifdef _PNP_POWER + // + // Release the lock after incrementing the reference count + // + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Device->LinkSpeed = LinkSpeed; + + if ((Adapter->ConfigMaxPacketSize == 0) || + (LineUp->MaximumTotalSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = LineUp->MaximumTotalSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + MacInitializeBindingInfo (Binding, Adapter); + +#ifdef _PNP_POWER + + // + // We dont give lineups; instead indicate only if the PnP reserved address + // changed to SPX. NB gets all PnP indications with the reserved address case + // marked out. + // + { + IPX_PNP_INFO NBPnPInfo; + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + // + // NB's reserved address changed. + // + NBPnPInfo.NewReservedAddress = TRUE; + + if (!Device->VirtualNetwork) { + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + IpxPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + } + } else { + NBPnPInfo.NewReservedAddress = FALSE; + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + NBPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADD_DEVICE (lineup) to NB: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + } + + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + + // + // Give line up to RIP as it is not PnP aware. + // + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + (*Device->UpperDrivers[IDENTIFIER_RIP].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + UpdateLineUp ? NULL : Configuration); + } +#else + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + UpdateLineUp ? NULL : Configuration); + } + } +#endif + if (!UpdateLineUp) { + + if ((Device->SingleNetworkActive) && + (Configuration->ConnectionClient == 1)) { + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = TRUE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + // + // If we have a virtual net, do a broadcast now so + // the router on the other end will know about us. + // + // BUGBUG: Use RipSendResponse, and do it even + // if SingleNetworkActive is FALSE?? + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + } + + // + // Find a queued address notify and complete it. + // If WanGlobalNetworkNumber is TRUE, we only do + // this when the first dialin line comes up. + // + + if ((!Device->WanGlobalNetworkNumber || + (!Device->GlobalNetworkIndicated && !Binding->DialOutAsync)) + && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + if (Device->WanGlobalNetworkNumber) { + Device->GlobalWanNetwork = Binding->LocalAddress.NetworkAddress; + Device->GlobalNetworkIndicated = TRUE; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + if (Device->WanGlobalNetworkNumber) { + IpxAddressData->adapternum = Device->SapNicCount - 1; + } else { + IpxAddressData->adapternum = Binding->NicId - 1; + } + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = TRUE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_LINE_DOWN: + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_DOWN)) { + IPX_DEBUG (WAN, ("Line down, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_DOWN))); + return; + } + + LineDown = (PNDIS_WAN_LINE_DOWN)StatusBuffer; + + *((ULONG UNALIGNED*)(&Binding)) = *((ULONG UNALIGNED*)(&LineDown->LocalAddress[2])); + + CTEAssert(Binding != NULL); + + // + // Note that the WAN line is down. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + Binding->LineUp = FALSE; + + // + // PNP_POWER - we hold the exclusive lock to the binding + // and reference to the adapter at this point. + // + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Binding->NicId == MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + // + // This was the old limit, so we have to scan + // backwards to update it -- we stop when we hit + // a non-WAN binding, or a wan binding that is up and + // dialout, or any wan binding if bit 1 in + // DisableDialinNetbios is off. + // + + for (i = Binding->NicId-1; i >= 1; i--) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, i); +#else + TmpBinding = Device->Bindings[i]; +#endif + + if ((TmpBinding != NULL) && + ((!TmpBinding->Adapter->MacInfo.MediumAsync) || + (TmpBinding->LineUp && + ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0))))) { + + break; + } + } + + Device->HighestType20NicId = i; + + } + + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count LAN + // bindings when doing this. + // + // BUGBUG: Update other device information? + // + + LinkSpeed = 0xffffffff; + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || !TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + + if (LinkSpeed != 0xffffffff) { + Device->LinkSpeed = LinkSpeed; + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + // + // Remove our router entry for this net. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + Segment = RipGetSegment ((PUCHAR)&Binding->LocalAddress.NetworkAddress); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&Binding->LocalAddress.NetworkAddress); + + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + RipDeleteRoute (Segment, RouteEntry); + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + } + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDown); + + } + + // + // Indicate to the upper drivers. + // +#ifdef _PNP_POWER + + // + // DeRegister this address with the TDI clients. + // + { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_PNP_INFO NBPnPInfo; + + CTEAssert(Binding->IsnInformed[IDENTIFIER_NB]); + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NewReservedAddress = FALSE; + NBPnPInfo.FirstORLastDevice = FALSE; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_DELETE_DEVICE (linedown) to NB: addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + (*Device->UpperDrivers[IDENTIFIER_RIP].LineDownHandler)( + Binding->NicId); + } +#else + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineDownHandler)( + Binding->NicId); + } + } +#endif + + if ((Device->SingleNetworkActive) && + (Binding->DialOutAsync)) { + + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = FALSE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + } + + // + // Find a queued address notify and complete it. + // + + if ((!Device->WanGlobalNetworkNumber) && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + IpxAddressData->adapternum = Binding->NicId - 1; + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = FALSE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_FRAGMENT: + + // + // No response needed, IPX is a datagram service. + // + // BUGBUG: What about telling Netbios/SPX? + // + + break; + + default: + + break; + + } + +#ifdef _PNP_POWER +error_no_lock: + IpxDereferenceAdapter(Adapter); +#endif + +} /* IpxStatus */ + + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} /* IpxStatusComplete */ + + + diff --git a/private/ntos/tdi/isnp/ipx/nwlnkipx.ini b/private/ntos/tdi/isnp/ipx/nwlnkipx.ini new file mode 100644 index 000000000..416f0c6b7 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/nwlnkipx.ini @@ -0,0 +1,191 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkIpx + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnkipx.sys + DisplayName = NWLINK2 IPX Protocol + Group = TDI + DependOnService = REG_MULTI_SZ + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\Elnkii1" + Export = REG_MULTI_SZ "\Device\NwlnkIpx" + Route = REG_MULTI_SZ ""Elnkii" "Elnkii1"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + NetConfig + Elnkii1 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard2 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard3 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard4 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard5 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Parameters + DedicatedRouter = REG_DWORD 0x00000000 + InitDatagrams = REG_DWORD 0x0000000a + MaxDatagrams = REG_DWORD 0x00000032 + RipAgeTime = REG_DWORD 0x00000005 + RipCount = REG_DWORD 0x00000005 + RipTimeout = REG_DWORD 0x00000001 + RipUsageTime = REG_DWORD 0x0000000f + SourceRouteUsageTime = REG_DWORD 0x0000000a + SocketUniqueness = REG_DWORD 0x00000008 + VirtualNetworkNumber = REG_DWORD 0x00000000 + VirtualNetworkOptional = REG_DWORD 0x00000001 + Winsock + Mapping = REG_BINARY 0x00000c08 + 0x00000100 0x00000003 0x00000006 0x00000002 0x000003e8 0x00000006 0x00000002 0x000003e9 + 0x00000006 0x00000002 0x000003ea 0x00000006 0x00000002 0x000003eb 0x00000006 0x00000002 + 0x000003ec 0x00000006 0x00000002 0x000003ed 0x00000006 0x00000002 0x000003ee 0x00000006 + 0x00000002 0x000003ef 0x00000006 0x00000002 0x000003f0 0x00000006 0x00000002 0x000003f1 + 0x00000006 0x00000002 0x000003f2 0x00000006 0x00000002 0x000003f3 0x00000006 0x00000002 + 0x000003f4 0x00000006 0x00000002 0x000003f5 0x00000006 0x00000002 0x000003f6 0x00000006 + 0x00000002 0x000003f7 0x00000006 0x00000002 0x000003f8 0x00000006 0x00000002 0x000003f9 + 0x00000006 0x00000002 0x000003fa 0x00000006 0x00000002 0x000003fb 0x00000006 0x00000002 + 0x000003fc 0x00000006 0x00000002 0x000003fd 0x00000006 0x00000002 0x000003fe 0x00000006 + 0x00000002 0x000003ff 0x00000006 0x00000002 0x00000400 0x00000006 0x00000002 0x00000401 + 0x00000006 0x00000002 0x00000402 0x00000006 0x00000002 0x00000403 0x00000006 0x00000002 + 0x00000404 0x00000006 0x00000002 0x00000405 0x00000006 0x00000002 0x00000406 0x00000006 + 0x00000002 0x00000407 0x00000006 0x00000002 0x00000408 0x00000006 0x00000002 0x00000409 + 0x00000006 0x00000002 0x0000040a 0x00000006 0x00000002 0x0000040b 0x00000006 0x00000002 + 0x0000040c 0x00000006 0x00000002 0x0000040d 0x00000006 0x00000002 0x0000040e 0x00000006 + 0x00000002 0x0000040f 0x00000006 0x00000002 0x00000410 0x00000006 0x00000002 0x00000411 + 0x00000006 0x00000002 0x00000412 0x00000006 0x00000002 0x00000413 0x00000006 0x00000002 + 0x00000414 0x00000006 0x00000002 0x00000415 0x00000006 0x00000002 0x00000416 0x00000006 + 0x00000002 0x00000417 0x00000006 0x00000002 0x00000418 0x00000006 0x00000002 0x00000419 + 0x00000006 0x00000002 0x0000041a 0x00000006 0x00000002 0x0000041b 0x00000006 0x00000002 + 0x0000041c 0x00000006 0x00000002 0x0000041d 0x00000006 0x00000002 0x0000041e 0x00000006 + 0x00000002 0x0000041f 0x00000006 0x00000002 0x00000420 0x00000006 0x00000002 0x00000421 + 0x00000006 0x00000002 0x00000422 0x00000006 0x00000002 0x00000423 0x00000006 0x00000002 + 0x00000424 0x00000006 0x00000002 0x00000425 0x00000006 0x00000002 0x00000426 0x00000006 + 0x00000002 0x00000427 0x00000006 0x00000002 0x00000428 0x00000006 0x00000002 0x00000429 + 0x00000006 0x00000002 0x0000042a 0x00000006 0x00000002 0x0000042b 0x00000006 0x00000002 + 0x0000042c 0x00000006 0x00000002 0x0000042d 0x00000006 0x00000002 0x0000042e 0x00000006 + 0x00000002 0x0000042f 0x00000006 0x00000002 0x00000430 0x00000006 0x00000002 0x00000431 + 0x00000006 0x00000002 0x00000432 0x00000006 0x00000002 0x00000433 0x00000006 0x00000002 + 0x00000434 0x00000006 0x00000002 0x00000435 0x00000006 0x00000002 0x00000436 0x00000006 + 0x00000002 0x00000437 0x00000006 0x00000002 0x00000438 0x00000006 0x00000002 0x00000439 + 0x00000006 0x00000002 0x0000043a 0x00000006 0x00000002 0x0000043b 0x00000006 0x00000002 + 0x0000043c 0x00000006 0x00000002 0x0000043d 0x00000006 0x00000002 0x0000043e 0x00000006 + 0x00000002 0x0000043f 0x00000006 0x00000002 0x00000440 0x00000006 0x00000002 0x00000441 + 0x00000006 0x00000002 0x00000442 0x00000006 0x00000002 0x00000443 0x00000006 0x00000002 + 0x00000444 0x00000006 0x00000002 0x00000445 0x00000006 0x00000002 0x00000446 0x00000006 + 0x00000002 0x00000447 0x00000006 0x00000002 0x00000448 0x00000006 0x00000002 0x00000449 + 0x00000006 0x00000002 0x0000044a 0x00000006 0x00000002 0x0000044b 0x00000006 0x00000002 + 0x0000044c 0x00000006 0x00000002 0x0000044d 0x00000006 0x00000002 0x0000044e 0x00000006 + 0x00000002 0x0000044f 0x00000006 0x00000002 0x00000450 0x00000006 0x00000002 0x00000451 + 0x00000006 0x00000002 0x00000452 0x00000006 0x00000002 0x00000453 0x00000006 0x00000002 + 0x00000454 0x00000006 0x00000002 0x00000455 0x00000006 0x00000002 0x00000456 0x00000006 + 0x00000002 0x00000457 0x00000006 0x00000002 0x00000458 0x00000006 0x00000002 0x00000459 + 0x00000006 0x00000002 0x0000045a 0x00000006 0x00000002 0x0000045b 0x00000006 0x00000002 + 0x0000045c 0x00000006 0x00000002 0x0000045d 0x00000006 0x00000002 0x0000045e 0x00000006 + 0x00000002 0x0000045f 0x00000006 0x00000002 0x00000460 0x00000006 0x00000002 0x00000461 + 0x00000006 0x00000002 0x00000462 0x00000006 0x00000002 0x00000463 0x00000006 0x00000002 + 0x00000464 0x00000006 0x00000002 0x00000465 0x00000006 0x00000002 0x00000466 0x00000006 + 0x00000002 0x00000467 0x00000006 0x00000002 0x00000468 0x00000006 0x00000002 0x00000469 + 0x00000006 0x00000002 0x0000046a 0x00000006 0x00000002 0x0000046b 0x00000006 0x00000002 + 0x0000046c 0x00000006 0x00000002 0x0000046d 0x00000006 0x00000002 0x0000046e 0x00000006 + 0x00000002 0x0000046f 0x00000006 0x00000002 0x00000470 0x00000006 0x00000002 0x00000471 + 0x00000006 0x00000002 0x00000472 0x00000006 0x00000002 0x00000473 0x00000006 0x00000002 + 0x00000474 0x00000006 0x00000002 0x00000475 0x00000006 0x00000002 0x00000476 0x00000006 + 0x00000002 0x00000477 0x00000006 0x00000002 0x00000478 0x00000006 0x00000002 0x00000479 + 0x00000006 0x00000002 0x0000047a 0x00000006 0x00000002 0x0000047b 0x00000006 0x00000002 + 0x0000047c 0x00000006 0x00000002 0x0000047d 0x00000006 0x00000002 0x0000047e 0x00000006 + 0x00000002 0x0000047f 0x00000006 0x00000002 0x00000480 0x00000006 0x00000002 0x00000481 + 0x00000006 0x00000002 0x00000482 0x00000006 0x00000002 0x00000483 0x00000006 0x00000002 + 0x00000484 0x00000006 0x00000002 0x00000485 0x00000006 0x00000002 0x00000486 0x00000006 + 0x00000002 0x00000487 0x00000006 0x00000002 0x00000488 0x00000006 0x00000002 0x00000489 + 0x00000006 0x00000002 0x0000048a 0x00000006 0x00000002 0x0000048b 0x00000006 0x00000002 + 0x0000048c 0x00000006 0x00000002 0x0000048d 0x00000006 0x00000002 0x0000048e 0x00000006 + 0x00000002 0x0000048f 0x00000006 0x00000002 0x00000490 0x00000006 0x00000002 0x00000491 + 0x00000006 0x00000002 0x00000492 0x00000006 0x00000002 0x00000493 0x00000006 0x00000002 + 0x00000494 0x00000006 0x00000002 0x00000495 0x00000006 0x00000002 0x00000496 0x00000006 + 0x00000002 0x00000497 0x00000006 0x00000002 0x00000498 0x00000006 0x00000002 0x00000499 + 0x00000006 0x00000002 0x0000049a 0x00000006 0x00000002 0x0000049b 0x00000006 0x00000002 + 0x0000049c 0x00000006 0x00000002 0x0000049d 0x00000006 0x00000002 0x0000049e 0x00000006 + 0x00000002 0x0000049f 0x00000006 0x00000002 0x000004a0 0x00000006 0x00000002 0x000004a1 + 0x00000006 0x00000002 0x000004a2 0x00000006 0x00000002 0x000004a3 0x00000006 0x00000002 + 0x000004a4 0x00000006 0x00000002 0x000004a5 0x00000006 0x00000002 0x000004a6 0x00000006 + 0x00000002 0x000004a7 0x00000006 0x00000002 0x000004a8 0x00000006 0x00000002 0x000004a9 + 0x00000006 0x00000002 0x000004aa 0x00000006 0x00000002 0x000004ab 0x00000006 0x00000002 + 0x000004ac 0x00000006 0x00000002 0x000004ad 0x00000006 0x00000002 0x000004ae 0x00000006 + 0x00000002 0x000004af 0x00000006 0x00000002 0x000004b0 0x00000006 0x00000002 0x000004b1 + 0x00000006 0x00000002 0x000004b2 0x00000006 0x00000002 0x000004b3 0x00000006 0x00000002 + 0x000004b4 0x00000006 0x00000002 0x000004b5 0x00000006 0x00000002 0x000004b6 0x00000006 + 0x00000002 0x000004b7 0x00000006 0x00000002 0x000004b8 0x00000006 0x00000002 0x000004b9 + 0x00000006 0x00000002 0x000004ba 0x00000006 0x00000002 0x000004bb 0x00000006 0x00000002 + 0x000004bc 0x00000006 0x00000002 0x000004bd 0x00000006 0x00000002 0x000004be 0x00000006 + 0x00000002 0x000004bf 0x00000006 0x00000002 0x000004c0 0x00000006 0x00000002 0x000004c1 + 0x00000006 0x00000002 0x000004c2 0x00000006 0x00000002 0x000004c3 0x00000006 0x00000002 + 0x000004c4 0x00000006 0x00000002 0x000004c5 0x00000006 0x00000002 0x000004c6 0x00000006 + 0x00000002 0x000004c7 0x00000006 0x00000002 0x000004c8 0x00000006 0x00000002 0x000004c9 + 0x00000006 0x00000002 0x000004ca 0x00000006 0x00000002 0x000004cb 0x00000006 0x00000002 + 0x000004cc 0x00000006 0x00000002 0x000004cd 0x00000006 0x00000002 0x000004ce 0x00000006 + 0x00000002 0x000004cf 0x00000006 0x00000002 0x000004d0 0x00000006 0x00000002 0x000004d1 + 0x00000006 0x00000002 0x000004d2 0x00000006 0x00000002 0x000004d3 0x00000006 0x00000002 + 0x000004d4 0x00000006 0x00000002 0x000004d5 0x00000006 0x00000002 0x000004d6 0x00000006 + 0x00000002 0x000004d7 0x00000006 0x00000002 0x000004d8 0x00000006 0x00000002 0x000004d9 + 0x00000006 0x00000002 0x000004da 0x00000006 0x00000002 0x000004db 0x00000006 0x00000002 + 0x000004dc 0x00000006 0x00000002 0x000004dd 0x00000006 0x00000002 0x000004de 0x00000006 + 0x00000002 0x000004df 0x00000006 0x00000002 0x000004e0 0x00000006 0x00000002 0x000004e1 + 0x00000006 0x00000002 0x000004e2 0x00000006 0x00000002 0x000004e3 0x00000006 0x00000002 + 0x000004e4 0x00000006 0x00000002 0x000004e5 0x00000006 0x00000002 0x000004e6 0x00000006 + 0x00000002 0x000004e7 + + HelperDllName = REG_EXPAND_SZ %SystemRoot%\system32\wshisn.dll + MinSockaddrLength = REG_DWORD 0x0000000e + MaxSockaddrLength = REG_DWORD 0x00000010 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkIpx + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isnp/ipx/nwlnkipx.rc b/private/ntos/tdi/isnp/ipx/nwlnkipx.rc new file mode 100644 index 000000000..0f437a15d --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/nwlnkipx.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkipx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkipx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/ipx/packet.c b/private/ntos/tdi/isnp/ipx/packet.c new file mode 100644 index 000000000..f70154b03 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/packet.c @@ -0,0 +1,1560 @@ +/*++ +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisMacBuffer, + Device->NdisBufferPoolHandle, + Header, + MAC_HEADER_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisIpxBuffer, + Device->NdisBufferPoolHandle, + Header + MAC_HEADER_SIZE, + IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisMacBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisIpxBuffer); + + // + // This flag optimizes the virtual to physical address X-ln + // in the MAC drivers on x86 + // + NdisMacBuffer->MdlFlags|=MDL_NETWORK_HEADER; + NdisIpxBuffer->MdlFlags|=MDL_NETWORK_HEADER; + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisMacBuffer; + Reserved->PaddingBuffer = NULL; +#if BACK_FILL + Reserved->BackFill = FALSE; +#endif + + ExInterlockedInsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeSendPacket */ + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + + IPX_DEBUG (PACKET, ("Initializing backfill packet\n")); + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = NULL; + Reserved->HeaderBuffer = NULL; + Reserved->PaddingBuffer = NULL; + Reserved->BackFill = TRUE; + + ExInterlockedInsertHeadList( + &Device->GlobalBackFillPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + IPX_DEBUG (PACKET, ("Initializing backfill packet Done\n")); + return STATUS_SUCCESS; + +} /* IpxInitializeBackFillPacket */ +#endif + + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PIPX_RECEIVE_RESERVED Reserved; + + IpxAllocateReceivePacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->TransferInProgress = FALSE; + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = NULL; + InitializeListHead (&Reserved->Requests); + + ExInterlockedInsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceivePacket */ + + +NTSTATUS +IpxInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + PDEVICE Device = Adapter->Device; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + ExInterlockedInsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceiveBuffer */ + + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a padding buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + PaddingBuffer - The receive buffer to initialize. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + PaddingBuffer->Data, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NDIS_BUFFER_LINKAGE(NdisBuffer) = (PNDIS_BUFFER)NULL; + PaddingBuffer->NdisBuffer = NdisBuffer; + PaddingBuffer->DataLength = DataBufferLength; + RtlZeroMemory (PaddingBuffer->Data, DataBufferLength); + + return STATUS_SUCCESS; + +} /* IpxInitializePaddingBuffer */ + + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Free the packet in a slightly unconventional way; this + // allows us to not have to NULL out HeaderBuffer's linkage + // field during normal operations when we put it back in + // the free pool. + // + + NdisBuffer = Reserved->HeaderBuffer; + NdisIpxBuffer = NDIS_BUFFER_LINKAGE(NdisBuffer); + NDIS_BUFFER_LINKAGE (NdisBuffer) = NULL; + NDIS_BUFFER_LINKAGE (NdisIpxBuffer) = NULL; + +#if 0 + NdisAdjustBufferLength (NdisBuffer, PACKET_HEADER_SIZE); +#endif + NdisAdjustBufferLength (NdisBuffer, MAC_HEADER_SIZE); + NdisAdjustBufferLength (NdisIpxBuffer, IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + NdisFreeBuffer (NdisBuffer); + NdisFreeBuffer (NdisIpxBuffer); + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + +} /* IpxDeinitializeSendPacket */ + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a back fill packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + IPX_DEBUG (PACKET, ("DeInitializing backfill packet\n")); + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + IPX_DEBUG (PACKET, ("DeInitializing backfill packet Done\n")); + + +} /* IpxDeinitializeBackFillPacket */ +#endif + + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + IpxFreeReceivePacket (Device, Packet); + +} /* IpxDeinitializeReceivePacket */ + + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + + DataBufferLength - The allocated length of the receive buffer. + +Return Value: + + None. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = Adapter->Device; + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + NdisAdjustBufferLength (ReceiveBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* IpxDeinitializeReceiveBuffer */ + + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a padding buffer. + +Arguments: + + Device - The device. + + PaddingBuffer - The padding buffer. + + DataBufferLength - The allocated length of the padding buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisAdjustBufferLength (PaddingBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (PaddingBuffer->NdisBuffer); + +} /* IpxDeinitializePaddingBuffer */ + + + +#ifdef IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (PACKET_HEADER_SIZE * Device->InitDatagrams); + + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitDatagrams]); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (IpxInitializeSendPacket (Device, Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += PACKET_HEADER_SIZE; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedDatagrams += SendPool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateSendPool */ + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (BackFillPoolSize, MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate BackFill pool memory\n")); + return; + } + + + + + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + + if (IpxInitializeBackFillPacket (Device, Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + + } + + BackFillPool->PacketCount = PacketNum; + BackFillPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < BackFillPool->PacketCount; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + + IPX_DEBUG (PACKET, ("Allocation of backfill pool done\n")); + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PIPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + ReceivePool = (PIPX_RECEIVE_POOL)IpxAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (IpxInitializeReceivePacket (Device, Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceivePool */ + + +#else // IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT HeaderSize; + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + NDIS_STATUS Status; + + CTELockHandle LockHandle; + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + HeaderSize = PACKET_HEADER_SIZE * Device->InitDatagrams; + + Header = (PUCHAR)IpxAllocateMemory (HeaderSize, MEMORY_PACKET, "SendPool"); + + if (Header == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate header memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &SendPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + SendPool->Header = Header; + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), SendPool->PoolHandle); + + if (IpxInitializeSendPacket (Device, &Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + Header += PACKET_HEADER_SIZE; + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedDatagrams += PacketNum; + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateSendPool */ + + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + PIPX_SEND_POOL BackFillPool; + NDIS_STATUS Status; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate backfill pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &BackFillPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), BackFillPool->PoolHandle); + + if (IpxInitializeBackFillPacket (Device, &Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + } + + CTEGetLock (&Device->Lock, &LockHandle); + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT PacketNum; + IPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + NDIS_STATUS Status; + + ReceivePool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &ReceivePool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitReceivePackets * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), ReceivePool->PoolHandle); + + if (IpxInitializeReceivePacket (Device, &Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedReceivePackets += PacketNum; + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateReceivePool */ +#endif // IPX_OWN_PACKETS + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this adapter. + +Arguments: + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + UINT DataLength; + PUCHAR Data; + CTELockHandle LockHandle; + + DataLength = Adapter->MaxReceivePacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (DataLength * Device->InitReceiveBuffers); + + ReceiveBufferPool = (PIPX_RECEIVE_BUFFER_POOL)IpxAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Init recv buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitReceiveBuffers, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitReceiveBuffers]); + + + for (BufferNum = 0; BufferNum < Device->InitReceiveBuffers; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (IpxInitializeReceiveBuffer (Adapter, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef IPX_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + IPX_PUSH_ENTRY_LIST (&Adapter->ReceiveBufferList, &ReceiveBuffer->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Adapter->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Adapter->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceiveBufferPool */ + + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateSendPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopSendPacket */ + +#if BACK_FILL + +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + IPX_DEBUG (PACKET, ("Popping backfill packet\n")); + + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBackFillPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + + IPX_DEBUG (PACKET, ("Popping backfill packet done\n")); + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBackFillPacket */ +#endif //BackFill + + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxReceivePackets) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceivePool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the adapter's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Adapter - Pointer to our adapter to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PDEVICE Device = Adapter->Device; + + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Adapter->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceiveBufferPool (Adapter); + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceiveBuffer */ + + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a padding buffer for use by all devices. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the allocated padding buffer. + +--*/ + +{ + PIPX_PADDING_BUFFER PaddingBuffer; + ULONG PaddingBufferSize; + + // + // We are assuming that we can use 1 global padding buffer for ALL + // transmits! We must therefore test to make sure that EthernetExtraPadding + // is not greater than 1. Otherwise, we must assume that the extra padding + // is being used for something and we therefore cannot share across all + // transmit requests. + // + + // + // We cannot support more than 1 byte padding space, since we allocate only + // one buffer for all transmit requests. + // + + if ( Device->EthernetExtraPadding > 1 ) { + IPX_DEBUG (PACKET, ("Padding buffer cannot be more than 1 byte\n")); + DbgBreakPoint(); + } + + // + // Allocate a padding buffer if possible. + // + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + + PaddingBuffer = IpxAllocateMemory (PaddingBufferSize, MEMORY_PACKET, "PaddingBuffer"); + + if (PaddingBuffer != NULL) { + + if (IpxInitializePaddingBuffer (Device, PaddingBuffer, Device->EthernetExtraPadding) != + STATUS_SUCCESS) { + IpxFreeMemory (PaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer"); + } else { + IPX_DEBUG (PACKET, ("Allocate padding buffer %lx\n", PaddingBuffer)); + return PaddingBuffer; + } + } + + return NULL; + +} /* IpxAllocatePaddingBuffer */ + + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine deallocates the padding buffer. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + None + +--*/ + +{ + ULONG PaddingBufferSize; + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + return; + } + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + IpxFreeMemory( IpxPaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer" ); + IpxPaddingBuffer = (PIPX_PADDING_BUFFER)NULL; + +} /* IpxFreePaddingBuffer */ + diff --git a/private/ntos/tdi/isnp/ipx/precomp.h b/private/ntos/tdi/isnp/ipx/precomp.h new file mode 100644 index 000000000..818629e5e --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/precomp.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include +#include +#include +#include +#include +#include "isnipx.h" +#include "config.h" +#include "mac.h" +#include "ipxtypes.h" +#include "ipxprocs.h" +#include diff --git a/private/ntos/tdi/isnp/ipx/query.c b/private/ntos/tdi/isnp/ipx/query.c new file mode 100644 index 000000000..28b38df5c --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/query.c @@ -0,0 +1,297 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PADDRESS_FILE AddressFile; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PBINDING Binding; + union { + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS IpxAddress; + } AddressInfo; + TDI_DATAGRAM_INFO DatagramInfo; + TDI_ADDRESS_IPX IpxAddress; + } TempBuffer; + UINT i; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // what type of status do we want? + // + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + status = IpxVerifyAddressFile (AddressFile); + + if (status == STATUS_SUCCESS) { + + TempBuffer.AddressInfo.ActivityCount = 0; + + IpxBuildTdiAddress( + &TempBuffer.AddressInfo.IpxAddress, + Device->SourceAddress.NetworkAddress, + Device->SourceAddress.NodeAddress, + AddressFile->Address->Socket); + + status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(TempBuffer.AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(Device->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS: + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + + TransportAddress = IpxAllocateMemory(sizeof(int) + (ElementSize * MIN (Device->MaxBindings, Device->ValidBindings)), MEMORY_QUERY, "NetworkAddress"); + + if (TransportAddress == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } +#endif + status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + CTEFreeMem (TransportAddress); + + } + + break; + + default: + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* IpxTdiQueryInformation */ + + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* IpxTdiSetInformation */ + + diff --git a/private/ntos/tdi/isnp/ipx/receive.c b/private/ntos/tdi/isnp/ipx/receive.c new file mode 100644 index 000000000..ff9c68fbd --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/receive.c @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceiveDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to complete any pended requests to our clients. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + PIPX_RECEIVE_RESERVED Reserved = (PIPX_RECEIVE_RESERVED)(NdisPacket->ProtocolReserved); + PREQUEST Request, LastRequest; + PADDRESS_FILE AddressFile; + ULONG ByteOffset; + PLIST_ENTRY p; + PDEVICE Device; + + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + + if (Reserved->SingleRequest) { + + // + // The transfer was directly into the client buffer, + // so simply complete the request. + // + + Request = Reserved->SingleRequest; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (RECEIVE, ("Transferred %d bytes\n", BytesTransferred)); + REQUEST_INFORMATION(Request) = BytesTransferred; + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RECEIVE, ("Transfer failed\n")); + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + LastRequest = Request; + Reserved->SingleRequest = NULL; + + } else { + + // + // Multiple clients requested this datagram. Save + // the last one to delay queueing it for completion. + // + + LastRequest = LIST_ENTRY_TO_REQUEST (Reserved->Requests.Blink); + + while (TRUE) { + + p = RemoveHeadList (&Reserved->Requests); + if (p == &Reserved->Requests) { + break; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + if (AddressFile->ReceiveIpxHeader) { + ByteOffset = 0; + } else { + ByteOffset = sizeof(IPX_HEADER); + } + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl( + Reserved->ReceiveBuffer->Data, + ByteOffset + REQUEST_INFORMATION(Request), + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + if (Request != LastRequest) { + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + } + + } + + // + // Now free the receive buffer back. + // + + IPX_PUSH_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Reserved->ReceiveBuffer->PoolLinkage, + &Adapter->Device->SListsLock); + + Reserved->ReceiveBuffer = NULL; + + } + + + // + // Now free the packet. + // + + NdisReinitializePacket (NdisPacket); + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->ReceivePacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->ReceivePacketInUse); + + } else { + + Device = Adapter->Device; + + IPX_PUSH_ENTRY_LIST( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + + // + // We Delay inserting the last request (or the only one) + // until after we have put the packet back, to keep the + // address around if needed (the address won't go away + // until the last address file does, and the address file + // won't go away until the datagram is completed). + // + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(LastRequest), + Adapter->DeviceLock); + + IpxReceiveComplete ((NDIS_HANDLE)Adapter); + + break; + + default: + + Device = Adapter->Device; + + (*Device->UpperDrivers[Reserved->Identifier].TransferDataCompleteHandler)( + NdisPacket, + NdisStatus, + BytesTransferred); + + break; + + } + +} /* IpxTransferDataComplete */ + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called by all tightly bound clients instead of NdisTransferData. + If this is a loopback packet, the transfer is done directly here, else NdisTransferData + is called. + +Arguments: + + Status - status of operation + NdisBindingHandle - Loopback cookie or Ndis context + MacReceiveContext - Loopback packet or Mac context + ByteOffset - Source offset + BytesToTransfer - length of the transfer desired + Packet - dest packet + BytesTransferred - length of successful transfer + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + // + // If this is a loopback packet, copy the data directly + // + if (NdisBindingHandle == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("LoopbXfer: src: %lx, dest: %lx, bytestoxfer: %lx\n", + MacReceiveContext, Packet, BytesToTransfer)); + + NdisCopyFromPacketToPacket( + Packet, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset + BytesTransferred); // BytesCopied + + *Status = NDIS_STATUS_SUCCESS; + } else { + NdisTransferData( + Status, + NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + Packet, + BytesTransferred); + } +} + + + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS Address; + PADDRESS_FILE AddressFile; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + Address = AddressFile->Address; + + if ((Address == NULL) || + (Address->Size != sizeof (ADDRESS)) || + (Address->Type != IPX_ADDRESS_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + IPX_BEGIN_SYNC (&SyncContext); + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_INVALID_HANDLE; + } + + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, IpxCancelReceiveDatagram); + + if (Request->Cancel) { + + (VOID)RemoveTailList (&AddressFile->ReceiveDatagramQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_CANCELLED; + } + + IPX_DEBUG (RECEIVE, ("RDG posted on %lx\n", AddressFile)); + + IpxReferenceAddressFileLock (AddressFile, AFREF_RCV_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + +} /* IpxTdiReceiveDatagram */ + + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + IPX_DEBUG(RECEIVE, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + IpxFreeRequest(IpxDevice, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* IpxCancelReceiveDatagram */ + + diff --git a/private/ntos/tdi/isnp/ipx/rip.c b/private/ntos/tdi/isnp/ipx/rip.c new file mode 100644 index 000000000..d30770223 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/rip.c @@ -0,0 +1,2655 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rip.c + +Abstract: + + This module contains code that implements the client-side + RIP support and simple router table support. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ) + +/*++ + +Routine Description: + + This routine looks up the proper route for the specified remote + address. If a RIP request needs to be generated it does so. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD. + NOTE: IN THE CASE OF PnP, THIS COMES WITH THE BIND LOCK SHARED. + +Arguments: + + Segment - The segment associate with the remote address. + + RemoteAddress - The IPX address of the remote. + + Type - One of IPX_FIND_ROUTE_NO_RIP, IPX_FIND_ROUTE_RIP_IF_NEEDED, + or IPX_FIND_ROUTE_FORCE_RIP. + + LocalTarget - Returns the next router information. + + Counts - If specified, used to return the tick and hop count. + +Return Value: + + STATUS_SUCCESS if a route is found, STATUS_PENDING if a + RIP request needs to be generated, failure status if a + RIP request packet cannot be allocated. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + PBINDING Binding; + UINT i; + + + // + // Packets sent to network 0 go on the first adapter also. + // + + if (RemoteAddress->NetworkAddress == 0) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 1); + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed) / + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#else + LocalTarget->NicId = 1; + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + Device->Bindings[1]->MediumSpeed) / + Device->Bindings[1]->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#endif + return STATUS_SUCCESS; + } + + // + // See if this is a packet sent to our virtual network. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + // + // Send it through adapter 1. + // BUGBUG: Do real loopback. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); + RtlCopyMemory (LocalTarget->MacAddress, NIC_ID_TO_BINDING(Device, 1)->LocalMacAddress.Address, 6); +#else + // + // Loopback this packet + // + LocalTarget->NicId = 0; + RtlCopyMemory (LocalTarget->MacAddress, Device->Bindings[1]->LocalMacAddress.Address, 6); +#endif + + IPX_DEBUG (LOOPB, ("Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = 1; // tick count + Counts[1] = 1; // hop count + } + return STATUS_SUCCESS; + + } + + // + // Look up the route in the table. If the net is one + // of the ones we are directly attached to, this will + // return an entry with the correct flag set. + // + + RouteEntry = RipGetRoute(Segment, (PUCHAR)&(RemoteAddress->NetworkAddress)); + + if (RouteEntry != NULL) { + + RouteEntry->Timer = 0; +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, RouteEntry->NicId); +#else + LocalTarget->NicId = RouteEntry->NicId; +#endif + if (RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) { + + // + // The machine is on the same net, so send it directly. + // + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + + if (RouteEntry->Flags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // + // The NicId here is bogus, we have to scan through + // our bindings until we find one whose indicated + // IPX remote node matches the destination node of + // this frame. We don't scan into the duplicate + // binding set members since they won't be WANs. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != (PBINDING)NULL) && + (Binding->Adapter->MacInfo.MediumAsync) && + (RtlEqualMemory( + Binding->WanRemoteNode, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + break; + + } + } + } + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + } + + } else { + // + // Find out if this is a loopback packet. If so, return NicId 0 + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + // + // Self-directed - loopback + // + if ((Binding != (PBINDING)NULL) && + (RtlEqualMemory( + Binding->LocalAddress.NodeAddress, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); +#else + LocalTarget->NicId = 0; +#endif + + IPX_DEBUG (LOOPB, ("2.Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + break; + + } + } + } + } + + } else { + + CTEAssert ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0); + + // + // This is not a locally attached net, so if the caller + // is forcing a re-RIP then do that. + // + + if (Type == IPX_FIND_ROUTE_FORCE_RIP) { + goto QueueUpRequest; + } + + // + // Fill in the address of the next router in the route. + // + + RtlCopyMemory (LocalTarget->MacAddress, RouteEntry->NextRouter, 6); + + } + + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = RouteEntry->TickCount; + Counts[1] = RouteEntry->HopCount; + } + + return STATUS_SUCCESS; + + } + +QueueUpRequest: + + if (Type == IPX_FIND_ROUTE_NO_RIP) { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + + } else { + + return RipQueueRequest (RemoteAddress->NetworkAddress, RIP_REQUEST); + + } + +} /* RipGetLocalTarget */ + + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ) + +/*++ + +Routine Description: + + This routine queues up a request for a RIP route. It can be + used to find a specific route or to discover the locally + attached network (if Network is 0). It can also be used + to do a periodic announcement of the virtual net, which + we do once a minute if the router is not bound. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD + IF IT IS A REQUEST AND THE NETWORK IS NOT 0xffffffff. + +Arguments: + + Network - The network to discover. + + Operation - One of RIP_REQUEST, RIP_RESPONSE, or RIP_DOWN. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + PLIST_ENTRY p; + PRIP_PACKET RipPacket; + TDI_ADDRESS_IPX RemoteAddress; + TDI_ADDRESS_IPX LocalAddress; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PNDIS_BUFFER pNdisIpxBuff; + + + // + // Make sure we only queue a request for net 0xffffffff if we + // are auto-detecting, because we assume that in other places. + // + + if ((Network == 0xffffffff) && + (Device->AutoDetectState != AUTO_DETECT_STATE_RUNNING)) { + + return STATUS_BAD_NETWORK_PATH; + + } + + // + // Try to get a packet to use for the RIP request. We + // allocate this now, but check if it succeeded later, + // to make the locking work better (we need to keep + // the lock between when we check for an existing + // request on this network and when we queue this + // request). + // + + s = IpxPopSendPacket (Device); + + // + // There was no router table entry for this network, first see + // if there is already a pending request for this route. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Operation == RIP_REQUEST) { + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + // + // Skip responses. + // + + if (Reserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (Reserved->u.SR_RIP.Network == Network && + !Reserved->u.SR_RIP.RouteFound) { + + // + // There is already one pending, put back the packet if + // we got one (we hold the lock already). + // + + if (s != NULL) { + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, s, &Device->SListsLock); + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + } + } + + } + + + if (s == NULL) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_INTERNAL; + Reserved->SendInProgress = FALSE; + Reserved->DestinationType = DESTINATION_BCAST; + Reserved->u.SR_RIP.CurrentNicId = 0; + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + switch (Operation) { + case RIP_REQUEST: Reserved->u.SR_RIP.RetryCount = 0; break; + case RIP_RESPONSE: Reserved->u.SR_RIP.RetryCount = 0xfe; break; + case RIP_DOWN: Reserved->u.SR_RIP.RetryCount = 0xff; break; + } + Reserved->u.SR_RIP.RouteFound = FALSE; + Reserved->u.SR_RIP.Network = Network; + Reserved->u.SR_RIP.SendTime = Device->RipSendTime; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // Fill in the IPX header at the standard offset (for sending + // to actual bindings it will be moved around if needed). We + // have to construct the local and remote addresses so they + // are in the format that IpxConstructHeader expects. + // + + RemoteAddress.NetworkAddress = Network; + RtlCopyMemory (RemoteAddress.NodeAddress, BroadcastAddress, 6); + RemoteAddress.Socket = RIP_SOCKET; + + RtlCopyMemory (&LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + LocalAddress.Socket = RIP_SOCKET; + + IpxConstructHeader( +// &Reserved->Header[Device->IncludedHeaderOffset], + &Reserved->Header[MAC_HEADER_SIZE], + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + &RemoteAddress, + &LocalAddress); + + // + // Fill in the RIP request also. + // + +#if 0 + RipPacket = (PRIP_PACKET)(&Reserved->Header[Device->IncludedHeaderOffset + sizeof(IPX_HEADER)]); +#endif + RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + RipPacket->Operation = Operation & 0x7fff; + RipPacket->NetworkEntry.NetworkNumber = Network; + + if (Operation == RIP_REQUEST) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(0xffff); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(0xffff); + } else if (Operation == RIP_RESPONSE) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(2); // will be modified when sent + } else { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(16); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(16); + } + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now insert this packet in the queue of pending RIP + // requests and start the timer if needed (this is done + // to ensure the RIP_GRANULARITY milliseconds inter-RIP-packet + // delay). + // + + IPX_DEBUG (RIP, ("RIP %s for network %lx\n", + (Operation == RIP_REQUEST) ? "request" : ((Operation == RIP_RESPONSE) ? "announce" : "down"), + REORDER_ULONG(Network))); + + InsertHeadList( + &Device->WaitingRipPackets, + &Reserved->WaitLinkage); + + ++Device->RipPacketCount; + + if (!Device->RipShortTimerActive) { + + Device->RipShortTimerActive = TRUE; + IpxReferenceDevice (Device, DREF_RIP_TIMER); + + CTEStartTimer( + &Device->RipShortTimer, + 1, // 1 ms, i.e. expire immediately + RipShortTimeout, + (PVOID)Device); + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + +} /* RipQueueRequest */ + + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ) + +/*++ + +Routine Description: + + This routine sends a respond to a RIP request from a client -- + this is only used if we have a virtual network and the router + is not bound, and somebody queries on the virtual network. + +Arguments: + + Binding - The binding on which the request was received. + + RemoteAddress - The IPX source address of the request. + + LocalTarget - The local target of the received packet. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PIPX_SEND_RESERVED Reserved; + TDI_ADDRESS_IPX LocalAddress; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + PRIP_PACKET RipPacket; + PDEVICE Device = IpxDevice; + PBINDING MasterBinding; + NDIS_STATUS NdisStatus; + USHORT TickCount; + PNDIS_BUFFER pNdisIpxBuff; + + // + // Get a packet to use for the RIP response. + // + + s = IpxPopSendPacket (Device); + + if (s == NULL) { + return; + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_RESPONSE; + Reserved->DestinationType = DESTINATION_DEF; + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // If this binding is a binding set member, round-robin through + // the various bindings when responding. We will get some natural + // round-robinning because broadcast requests are received on + // binding set members in turn, but they are only rotated once + // a second. + // + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + + // + // Fill in the IPX header at the correct offset. + // + + LocalAddress.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (LocalAddress.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + LocalAddress.Socket = RIP_SOCKET; +#if 0 + IpxHeader = (PIPX_HEADER)(&Reserved->Header[Binding->DefHeaderSize]); +#endif + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + IpxConstructHeader( + (PUCHAR)IpxHeader, + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + RemoteAddress, + &LocalAddress); + + // + // In case the request comes from net 0, fill that in too. + // + + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + + + // + // Fill in the RIP request. + // + + RipPacket = (PRIP_PACKET)(IpxHeader+1); + + RipPacket->Operation = RIP_RESPONSE; + RipPacket->NetworkEntry.NetworkNumber = Device->VirtualNetworkNumber; + + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + TickCount = (USHORT)(((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + IPX_DEBUG (RIP, ("RIP response for virtual network %lx\n", + REORDER_ULONG(Device->VirtualNetworkNumber))); + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + +#ifdef _PNP_POWER + if (Binding->BindingSetMember) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + return; + +} /* RipSendResponse */ + + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP short timer expires. + It is called every RIP_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p; + PIPX_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + USHORT OldNicId, NewNicId; + ULONG OldOffset, NewOffset; + PIPX_HEADER IpxHeader; + PBINDING Binding, MasterBinding; + NDIS_STATUS NdisStatus; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + +#ifdef _PNP_LATER + static IPX_LOCAL_TARGET BroadcastTarget = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, {0, 0, 0} }; +#else + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif + + static ULONG ZeroNetwork = 0; +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->RipSendTime; + + if (Device->RipPacketCount == 0) { + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // + + while (TRUE) { + + p = Device->WaitingRipPackets.Flink; + if (p == &Device->WaitingRipPackets) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if ((Reserved->u.SR_RIP.RouteFound) && (!Reserved->SendInProgress)) { + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + } + + if ((((SHORT)(Device->RipSendTime - Reserved->u.SR_RIP.SendTime)) < 0) || + Reserved->SendInProgress) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + + // + // Find the right binding to send to. If NoIdAdvance + // is set, then the binding doesn't need to be changed + // this time (this means we wrapped last time). + // + + OldNicId = Reserved->u.SR_RIP.CurrentNicId; + + if (!Reserved->u.SR_RIP.NoIdAdvance) { + + BOOLEAN FoundNext = FALSE; + +#ifdef _PNP_POWER +// +// To maintain the lock order, release Device lock here and re-acquire later +// + USHORT StartId; + + if (Device->ValidBindings == 0) { + IPX_DEBUG(PNP, ("ValidBindings 0 in RipShortTimeOut\n")); + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + return; + } + + StartId = (USHORT)((OldNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); + + NewNicId = StartId; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#else + + USHORT StartId = (USHORT)((OldNicId % Device->BindingCount) + 1); + + NewNicId = StartId; +#endif + do { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NewNicId); +#else + Binding = Device->Bindings[NewNicId]; +#endif + if (Reserved->u.SR_RIP.Network != 0xffffffff) { + + // + // We are looking for a real net; check that + // the next binding is valid. If it is a WAN + // binding, we don't send queries if the router + // is bound. If it is a LAN binding, we don't + // send queries if we are configured for + // SingleNetworkActive and the WAN is up. + // We also don't send queries on binding set + // members which aren't masters. + // + + if ((Binding != NULL) + && + ((!Binding->Adapter->MacInfo.MediumAsync) || + (!Device->UpperDriverBound[IDENTIFIER_RIP])) + && + ((Binding->Adapter->MacInfo.MediumAsync) || + (!Device->SingleNetworkActive) || + (!Device->ActiveNetworkWan)) + && + ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding))) { + + FoundNext = TRUE; + break; + } + + } else { + + // + // We are sending out the initial request to net + // 0xffffffff, to generate traffic so we can figure + // out our real network number. We don't do this + // to nets that already have a number and we don't + // do it on WAN links. We also don't do it on + // auto-detect nets if we have found the default. + // + + + if ((Binding != NULL) && + (Binding->TentativeNetworkAddress == 0) && + (!Binding->Adapter->MacInfo.MediumAsync) && + (!Binding->AutoDetect || !Binding->Adapter->DefaultAutoDetected)) { + FoundNext = TRUE; + break; + } + } +#ifdef _PNP_POWER + // + // [BUGBUGZZ] Why cycle thru the entire list? + // + NewNicId = (USHORT)((NewNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); +#else + NewNicId = (USHORT)((NewNicId % Device->BindingCount) + 1); +#endif + } while (NewNicId != StartId); + + if (!FoundNext) { + + // + // Nothing more needs to be done with this packet, + // leave it off the queue and since we didn't send + // a packet we can check for more. + // +#ifndef _PNP_POWER + // + // This was released above (before the BindAccessLock was taken + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + RipCleanupPacket(Device, Reserved); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + + } + +#ifdef _PNP_POWER + + IPX_DEBUG(RIP, ("RIP: FoundNext: %lx, StartId: %lx, OldNicId: %lx, NewNicId: %lx\n", FoundNext, StartId, OldNicId, NewNicId)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Re-acquire the Device lock + // + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + + Reserved->u.SR_RIP.CurrentNicId = NewNicId; + + // + // Move the data around if needed. + // + +#if 0 + if (OldNicId != NewNicId) { + + if (OldNicId == 0) { + OldOffset = Device->IncludedHeaderOffset; + } else { + OldOffset = Device->Bindings[OldNicId]->BcMcHeaderSize; + } + + NewOffset = Binding->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + + } + + } +#endif + + if (NewNicId <= OldNicId) { + + // + // We found a new binding but we wrapped, so increment + // the counter. If we have done all the resends, or + // this is a response (indicated by retry count of 0xff; + // they are only sent once) then clean up. + // + + if ((Reserved->u.SR_RIP.RetryCount >= 0xfe) || + ((++Reserved->u.SR_RIP.RetryCount) == Device->RipCount)) { + + // + // This packet is stale, clean it up and continue. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + RipCleanupPacket(Device, Reserved); + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + // + // We wrapped, so put ourselves back in the queue + // at the end. + // + + Reserved->u.SR_RIP.SendTime = (USHORT)(Device->RipSendTime + Device->RipTimeout - 1); + Reserved->u.SR_RIP.NoIdAdvance = TRUE; + InsertTailList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + +#ifdef _PNP_POWER + // + // Free the Device lock before deref'ing the Binding so we maintain + // the lock order: BindingAccess > GlobalInterLock > Device + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + } + + continue; + + } +#ifdef _PNP_POWER +// +// To prevent the re-acquire of the device lock, this is moved up... +// + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + } else { + + // + // Next time we need to advance the binding. + // + + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + NewNicId = OldNicId; +#ifdef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NewNicId); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[NewNicId]; +#endif + + } +#ifndef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + // + // This packet should be sent on binding NewNicId; first + // move the data to the right location for the current + // binding. + // +#ifdef _PNP_POWER + CTEAssert (Binding == NIC_ID_TO_BINDING(Device, NewNicId)); // temp, just to make sure +#else + CTEAssert (Binding == Device->Bindings[NewNicId]); // temp, just to make sure +#endif +// NewOffset = Binding->BcMcHeaderSize; + + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&BroadcastTarget, NewNicId); +#else + BroadcastTarget.NicId = NewNicId; +#endif + + // + // Modify the header so the packet comes from this + // specific adapter, not the virtual network. + // + + // IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + if (Reserved->u.SR_RIP.Network == 0xffffffff) { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = 0; + } else { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + } + + if (Reserved->u.SR_RIP.RetryCount < 0xfe) { + + // + // This is an outgoing query. We round-robin these through + // binding sets. + // + + if (Binding->BindingSetMember) { + + // + // Shouldn't have any binding sets during initial + // discovery. + // + + CTEAssert (Reserved->u.SR_RIP.Network != 0xffffffff); + + // + // If we are in a binding set, then use the current binding + // in the set for this send, and advance the current binding. + // The places we have used Binding before here will be fine + // since the binding set members all have the same media + // and frame type. + // + + CTEAssert (Binding->CurrentSendBinding); // should be a master. + MasterBinding = Binding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(MasterBinding, BREF_DEVICE_ACCESS); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + } + + + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + // + // Bug# 6485 + // Rip request, general or specific, is putting the network of the + // node to which the route has to be found in the ipx header remote + // network field. Some novell routers don't like that. This network + // field should be 0. + // + { + PRIP_PACKET RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + + if (RipPacket->Operation != RIP_REQUEST) { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + } else { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = 0; + } + } + + // + // If this is a RIP_RESPONSE, set the tick count for this + // binding. + // + + if (Reserved->u.SR_RIP.RetryCount == 0xfe) { + + PRIP_PACKET RipPacket = (PRIP_PACKET)(IpxHeader+1); + USHORT TickCount = (USHORT) + (((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + } + + if ((NdisStatus = IpxSendFrame( + &BroadcastTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + + break; + + } + + CTEStartTimer( + &Device->RipShortTimer, + RIP_GRANULARITY, + RipShortTimeout, + (PVOID)Device); + +} /* RipShortTimeout */ + + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP long timer expires. + It is called every minute and handles periodic re-RIPping + to ensure that entries are accurate, as well as aging out + of entries if the rip router is not bound. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + UINT i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + + // + // Rotate the broadcast receiver on all binding sets. + // We can loop up to HighestExternal only since we + // are only interested in finding binding set masters. + // +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != NULL) && + (Binding->CurrentSendBinding)) { + + // + // It is a master, so find the current broadcast + // receiver, then advance it. + // + + while (TRUE) { + if (Binding->ReceiveBroadcast) { + Binding->ReceiveBroadcast = FALSE; + Binding->NextBinding->ReceiveBroadcast = TRUE; + break; + } else { + Binding = Binding->NextBinding; + } + } + } + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + + // + // If RIP is bound, we don't do any of this, and + // we stop the timer from running. + // + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + return; + } + + + // + // If we have a virtual net, do our periodic broadcast. + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Device->TempDatagramBytesSent); + Device->Statistics.DatagramsSent += Device->TempDatagramsSent; + + Device->TempDatagramBytesSent = 0; + Device->TempDatagramsSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + Device->TempDatagramBytesReceived); + Device->Statistics.DatagramsReceived += Device->TempDatagramsReceived; + + Device->TempDatagramBytesReceived = 0; + Device->TempDatagramsReceived = 0; + + + // + // We need to scan each hash bucket to see if there + // are any active entries which need to be re-RIPped + // for. We also scan for entries that should be timed + // out. + // + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + RouterSegment = &IpxDevice->Segments[Segment]; + + // + // Don't take the lock if the bucket is empty. + // + + if (RouterSegment->Entries.Flink == &RouterSegment->Entries) { + continue; + } + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry looking for ones to age. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) { + continue; + } + + ++RouteEntry->Timer; + if (RouteEntry->Timer >= Device->RipUsageTime) { + + RipDeleteRoute (Segment, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + continue; + + } + + // + // See if we should re-RIP for this segment. It has + // to have been around for RipAgeTime, and we also + // make sure that the Timer is not too high to + // prevent us from re-RIPping on unused routes. + // + + ++RouteEntry->PRIVATE.Reserved[0]; + + if ((RouteEntry->PRIVATE.Reserved[0] >= Device->RipAgeTime) && + (RouteEntry->Timer <= Device->RipAgeTime)) { + + // + // If we successfully queue a request, then reset + // Reserved[0] so we don't re-RIP for a while. + // + + if (RipQueueRequest (*(UNALIGNED ULONG *)RouteEntry->Network, RIP_REQUEST) == STATUS_PENDING) { + RouteEntry->PRIVATE.Reserved[0] = 0; + } + } + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + } + + + // + // Now restart the timer for the next timeout. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + CTEStartTimer( + &Device->RipLongTimer, + 60000, // one minute timeout + RipLongTimeout, + (PVOID)Device); + + } else { + + // + // Send a DOWN packet if needed, then stop ourselves. + // + + if (Device->RipResponder) { + + if (RipQueueRequest (Device->VirtualNetworkNumber, RIP_DOWN) != STATUS_PENDING) { + + // + // We need to kick this event because the packet completion + // won't. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + } + + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + + } + +} /* RipLongTimeout */ + + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ) + +/*++ + +Routine Description: + + This routine cleans up when a RIP packet times out. + +Arguments: + + Device - The device. + + RipReserved - The ProtocolReserved section of the RIP packet. + +Return Value: + + None. + +--*/ + +{ + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + if (RipReserved->u.SR_RIP.RetryCount < 0xfe) { + + if (RipReserved->u.SR_RIP.Network != 0xffffffff) { + + IPX_DEBUG (RIP, ("Timing out RIP for network %lx\n", + REORDER_ULONG(RipReserved->u.SR_RIP.Network))); + + Segment = RipGetSegment ((PUCHAR)&RipReserved->u.SR_RIP.Network); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipReserved->u.SR_RIP.Network), + LockHandle, + FALSE, + NULL, + 0, + 0); + + } else { + + // + // This was the initial query looking for networks -- + // signal the init thread which is waiting. + // + + IPX_DEBUG (AUTO_DETECT, ("Signalling auto-detect event\n")); + KeSetEvent( + &Device->AutoDetectEvent, + 0L, + FALSE); + + } + + } else if (RipReserved->u.SR_RIP.RetryCount == 0xff) { + + // + // This is a DOWN message, set the device event that + // is waiting for it to complete. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + + // + // Put the RIP packet back in the pool. + // + + RipReserved->Identifier = IDENTIFIER_IPX; + +} /* RipCleanupPacket */ + + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ) + +/*++ + +Routine Description: + + This routine processes a RIP response from the specified + local target, indicating a route to the network in the RIP + header. + +Arguments: + + Device - The device. + + LocalTarget - The router that the frame was received from. + + RipPacket - The RIP response header. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED RipReserved; // ProtocolReserved of RIP packet + ULONG Segment; + PIPX_ROUTE_ENTRY RouteEntry, OldRouteEntry; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + // + // Since we have received a RIP response for this network. + // kill the waiting RIP packets for it if it exists. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + RipReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if (RipReserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (RipReserved->u.SR_RIP.Network == + RipPacket->NetworkEntry.NetworkNumber) { + break; + } + + } + + if (p == &Device->WaitingRipPackets) { + + // + // No packets pending on this, return. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Put the RIP packet back in the pool. + // + + IPX_DEBUG (RIP, ("Got RIP response for network %lx\n", + REORDER_ULONG(RipPacket->NetworkEntry.NetworkNumber))); + + RipReserved->u.SR_RIP.RouteFound = TRUE; + if (!RipReserved->SendInProgress) { + + // + // If the send is done destroy it now, otherwise + // when it pops up in RipShortTimeout it will get + // destroyed because RouteFound is TRUE. + // + + RemoveEntryList (p); + RipReserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &RipReserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + + + // + // Try to allocate and add a router segment unless the + // RIP router is active...if we don't that is fine, we'll + // just re-RIP later. + // + + Segment = RipGetSegment ((PUCHAR)&RipPacket->NetworkEntry.NetworkNumber); + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + *(UNALIGNED LONG *)RouteEntry->Network = RipPacket->NetworkEntry.NetworkNumber; +#ifdef _PNP_POWER + RouteEntry->NicId = NIC_FROM_LOCAL_TARGET(LocalTarget); + RouteEntry->NdisBindingContext = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)->Adapter->NdisBindingHandle; + // BUGBUG: What if this is NULL?? +#else + RouteEntry->NicId = LocalTarget->NicId; + RouteEntry->NdisBindingContext = Device->Bindings[LocalTarget->NicId]->Adapter->NdisBindingHandle; // BUGBUG: What if this is NULL?? +#endif + RouteEntry->Flags = 0; + RouteEntry->Timer = 0; + RouteEntry->PRIVATE.Reserved[0] = 0; + RouteEntry->Segment = Segment; + RouteEntry->HopCount = REORDER_USHORT(RipPacket->NetworkEntry.HopCount); + RouteEntry->TickCount = REORDER_USHORT(RipPacket->NetworkEntry.TickCount); + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + RtlCopyMemory (RouteEntry->NextRouter, LocalTarget->MacAddress, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Replace any existing routes. This is OK because once + // we get the first response to a RIP packet on a given + // route, we will take the packet out of the queue and + // ignore further responses. We will only get a bad route + // if we do two requests really quickly and there + // are two routes, and the second response to the first + // request is picked up as the first response to the second + // request. + // + + if ((OldRouteEntry = RipGetRoute (Segment, (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber))) != NULL) { + + // + // These are saved so timeouts etc. happen right. + // + + RouteEntry->Flags = OldRouteEntry->Flags; + RouteEntry->Timer = OldRouteEntry->Timer; + + RipDeleteRoute (Segment, OldRouteEntry); + IpxFreeMemory(OldRouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } + + RipAddRoute (Segment, RouteEntry); + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + // + // Complete all datagrams etc. that were waiting + // for this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber), + LockHandle, + TRUE, + LocalTarget, + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.HopCount)), + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.TickCount)) + ); + +} /* RipProcessResponse */ + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams, find route + requests, and GET_LOCAL_TARGET ioctls that were + waiting for a route to be found. + + THIS ROUTINE IS CALLED WITH THE SEGMENT LOCK HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + Network - The network in question. + + LockHandle - The handle used to acquire the lock. + + Success - TRUE if the route was successfully found. + + LocalTarget - If Success is TRUE, the local target for the route. + + HopCount - If Success is TRUE, the hop count for the route, + in machine order. + + TickCount - If Success is TRUE, the tick count for the route, + in machine order. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY FindRouteList; + LIST_ENTRY GetLocalTargetList; + LIST_ENTRY ReripNetnumList; + PIPX_SEND_RESERVED WaitReserved; // ProtocolReserved of waiting packet + PIPX_FIND_ROUTE_REQUEST FindRouteRequest; + PREQUEST GetLocalTargetRequest; + PREQUEST ReripNetnumRequest; + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PIPX_NETNUM_DATA NetnumData; + ULONG Segment; + PBINDING Binding, SendBinding; + PLIST_ENTRY p; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + ULONG HeaderSize; + NDIS_STATUS NdisStatus; + ULONG NetworkUlong = *(UNALIGNED ULONG *)Network; + + + InitializeListHead (&DatagramList); + InitializeListHead (&FindRouteList); + InitializeListHead (&GetLocalTargetList); + InitializeListHead (&ReripNetnumList); + + + // + // Put all packets that were waiting for a route to + // this network on DatagramList. They will be sent + // or failed later in the routine. + // + + Segment = RipGetSegment (Network); + + p = Device->Segments[Segment].WaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].WaitingForRoute) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; +#if 0 + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[Device->IncludedHeaderOffset]))->DestinationNetwork) == + NetworkUlong) { +#endif + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[MAC_HEADER_SIZE]))->DestinationNetwork) == + NetworkUlong) { + + RemoveEntryList (&WaitReserved->WaitLinkage); + InsertTailList (&DatagramList, &WaitReserved->WaitLinkage); + } + + } + + // + // Put all find route requests for this network on + // FindRouteList. They will be completed later in the + // routine. + // + + p = Device->Segments[Segment].FindWaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].FindWaitingForRoute) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + if (*(UNALIGNED ULONG *)(FindRouteRequest->Network) == + NetworkUlong) { + + RemoveEntryList (&FindRouteRequest->Linkage); + InsertTailList (&FindRouteList, &FindRouteRequest->Linkage); + } + + } + + // + // Put all get local target action requests for this + // network on GetLocalTargetList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingLocalTarget.Flink; + + while (p != &Device->Segments[Segment].WaitingLocalTarget) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + if (GetLocalTarget->IpxAddress.NetworkAddress == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(GetLocalTargetRequest)); + InsertTailList (&GetLocalTargetList, REQUEST_LINKAGE(GetLocalTargetRequest)); + } + + } + + // + // Put all MIPX_RERIPNETNUM action requests for this + // network on ReripNetnumList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingReripNetnum.Flink; + + while (p != &Device->Segments[Segment].WaitingReripNetnum) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + if (*(UNALIGNED ULONG *)NetnumData->netnum == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(ReripNetnumRequest)); + InsertTailList (&ReripNetnumList, REQUEST_LINKAGE(ReripNetnumRequest)); + } + + } + + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + // + // For sends we will use the master binding of a binding + // set, but we'll return the real NicId for people who + // want that. + // + + if (Success) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, SendBinding->NicId)); + } else { + SendBinding = Binding; + } +#else + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + LocalTarget->NicId = SendBinding->NicId; + } else { + SendBinding = Binding; + } +#endif + } + + + // + // Now that the lock is free, process all packets on + // DatagramList. + // + // NOTE: May misorder packets if they come in right now... + // + + for (p = DatagramList.Flink; p != &DatagramList ; ) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; + Packet = CONTAINING_RECORD (WaitReserved, NDIS_PACKET, ProtocolReserved[0]); + +#if DBG + CTEAssert (!WaitReserved->SendInProgress); + WaitReserved->SendInProgress = TRUE; +#endif + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued packet %lx\n", WaitReserved)); + + if (REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) > + SendBinding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Queued send %d bytes too large (%d)\n", + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request), + SendBinding->RealMaxDatagramSize)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_INVALID_BUFFER_SIZE); + + } else { + +#if 0 + if (WaitReserved->DestinationType == DESTINATION_DEF) { + HeaderSize = SendBinding->DefHeaderSize; + } else { + HeaderSize = SendBinding->BcMcHeaderSize; + } + + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[HeaderSize]); +#endif + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[MAC_HEADER_SIZE]); + + // + // Move the header to the correct location now that + // we know the NIC ID to send to. + // +#if 0 + if (HeaderSize != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + IpxHeader, + &WaitReserved->Header[Device->IncludedHeaderOffset], + sizeof(IPX_HEADER)); + + } +#endif + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // These frames need to look like they come from the + // local network, not the virtual one. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = SendBinding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, SendBinding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)SendBinding->Adapter, + Packet, + NdisStatus); + } + + } + + } else { + + IPX_DEBUG (RIP, ("Timing out packet %lx\n", WaitReserved)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_BAD_NETWORK_PATH); + + } + + } + + + // + // Since we round-robin outgoing rip packets, we just use the + // real NicId here for find route and get local target requests. + // We changed LocalTarget->NicId to be the master above. + // + + if (Success) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + } + + for (p = FindRouteList.Flink; p != &FindRouteList ; ) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + + if (Success) { + + PUSHORT Counts; + + IPX_DEBUG (RIP, ("Found queued find route %lx\n", FindRouteRequest)); + FindRouteRequest->LocalTarget = *LocalTarget; + + Counts = (PUSHORT)&FindRouteRequest->Reserved2; + Counts[0] = TickCount; + Counts[1] = HopCount; + + } else { + + IPX_DEBUG (RIP, ("Timing out find route %lx\n", FindRouteRequest)); + + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + Success); + + } + + for (p = GetLocalTargetList.Flink; p != &GetLocalTargetList ; ) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + GetLocalTarget->LocalTarget = *LocalTarget; + REQUEST_INFORMATION(GetLocalTargetRequest) = sizeof(ISN_ACTION_GET_LOCAL_TARGET); + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + REQUEST_INFORMATION(GetLocalTargetRequest) = 0; + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(GetLocalTargetRequest); + IpxFreeRequest(Device, GetLocalTargetRequest); + + } + + // + // NOTE: LocalTarget->NicId now points to the real binding + // not the master, so we use SendBinding->NicId below. + // + + for (p = ReripNetnumList.Flink; p != &ReripNetnumList ; ) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + NetnumData->hopcount = HopCount; + NetnumData->netdelay = TickCount; + NetnumData->cardnum = (INT)(MIN( Device->MaxBindings, SendBinding->NicId) - 1); + RtlMoveMemory (NetnumData->router, LocalTarget->MacAddress, 6); + + REQUEST_INFORMATION(ReripNetnumRequest) = + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_NETNUM_DATA); + REQUEST_STATUS(ReripNetnumRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + REQUEST_INFORMATION(ReripNetnumRequest) = 0; + REQUEST_STATUS(ReripNetnumRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(ReripNetnumRequest); + IpxFreeRequest(Device, ReripNetnumRequest); + + } + +} /* RipHandleRoutePending */ + + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ) + +/*++ + +Routine Description: + + This routine creates a router entry for a local network + and inserts it in the table. + +Arguments: + + Network - The network. + + NicId - The NIC ID used to route packets + + NdisBindingHandle - The binding handle used for NdisSend + + Count - The tick and hop count for this network (will be + 0 for the virtual net and 1 for attached nets) + +Return Value: + + The status of the operation. + +--*/ + +{ + PIPX_ROUTE_ENTRY RouteEntry; + PDEVICE Device = IpxDevice; + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // BUGBUG: We should allocate the memory in the binding/device + // structure itself. + // + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry == (PIPX_ROUTE_ENTRY)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Segment = RipGetSegment ((PUCHAR)&Network); + + *(UNALIGNED LONG *)RouteEntry->Network = Network; + RouteEntry->NicId = NicId; + RouteEntry->NdisBindingContext = NdisBindingContext; + + if (NicId == 0) { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY; + } else { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY | IPX_ROUTER_LOCAL_NET; + } + RouteEntry->Segment = Segment; + RouteEntry->TickCount = Count; + RouteEntry->HopCount = 1; + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + + // + // RouteEntry->NextRouter is not used for the virtual net or + // when LOCAL_NET is set (i.e. every net that we will add here). + // + + RtlZeroMemory (RouteEntry->NextRouter, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Make sure one doesn't exist. + // + + if (RipGetRoute(Segment, (PUCHAR)&Network) != NULL) { + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_DUPLICATE_NAME; + } + + // + // Add this new entry. + // + + if (RipAddRoute (Segment, RouteEntry)) { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + return STATUS_SUCCESS; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_INSUFFICIENT_RESOURCES; + + } + +} /* RipInsertLocalNetwork */ + + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ) + +/*++ + +Routine Description: + + This routine is called when an auto-detect binding is + deleted or moved, or a WAN line goes down. + + It scans the RIP database for routes equal to this NIC ID + and modifies them appropriately. If ChangeType is + IpxBindingDeleted it will subract one from any NIC IDs + in the database that are higher than NicId. It is assumed + that other code is readjusting the Device->Bindings + array. + +Arguments: + + NicId - The NIC ID of the deleted binding. + + NewNicId - The new NIC ID, for IpxBindingMoved changes. + + ChangeType - Either IpxBindingDeleted, IpxBindingMoved, + or IpxBindingDown. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry comparing the NIC ID. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->NicId == NicId) { + + if (ChangeType != IpxBindingMoved) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, binding deleted\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Changing NIC ID for route entry %lx\n", RouteEntry)); + RouteEntry->NicId = NewNicId; + + } +#ifdef _PNP_POWER + // + // If the NicId is 0, we dont adjust the other entries' NicId's - this is to support the removal + // of the Virtual Net # which resides at NicId=0. + // + } else if (NicId && (ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#else + } else if ((ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#endif + IPX_DEBUG (AUTO_DETECT, ("Decrementing NIC ID for route entry %lx\n", RouteEntry)); + --RouteEntry->NicId; + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipAdjustForBindingChange */ + + +UINT +RipGetSegment( + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the correct segment for the specified + network. + +Arguments: + + Network - The network. + +Return Value: + + The segment. + +--*/ + +{ + + ULONG Total; + + Total = Network[0] ^ Network[1] ^ Network[2] ^ Network[3]; + return (Total % IpxDevice->SegmentCount); + +} /* RipGetSegment */ + + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the router table entry for the given + network, which is in the specified segment of the table. + THE SEGMENT LOCK MUST BE HELD. The returned data is valid + until the segment lock is released or other operations + (add/delete) are performed on the segment. + +Arguments: + + Segment - The segment corresponding to the network. + + Network - The network. + +Return Value: + + The router table entry, or NULL if none exists for this network. + +--*/ + +{ + PLIST_ENTRY p; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + + RouterSegment = &IpxDevice->Segments[Segment]; + + for (p = RouterSegment->Entries.Flink; + p != &RouterSegment->Entries; + p = p->Flink) { + + RouteEntry = CONTAINING_RECORD( + p, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + if ((*(UNALIGNED LONG *)RouteEntry->Network) == + (*(UNALIGNED LONG *)Network)) { + return RouteEntry; + } + } + + return NULL; + +} /* RipGetRoute */ + + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine stores a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is allocated and filled in by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully inserted. + +--*/ + +{ + + IPX_DEBUG (RIP, ("Adding route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + InsertTailList( + &IpxDevice->Segments[Segment].Entries, + &RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipAddRoute */ + + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine deletes a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is freed by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully deleted. + +--*/ + +{ + + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + IPX_DEBUG (RIP, ("Deleting route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + + // + // If the current enumeration point for this segment is here, + // adjust the pointer before deleting the entry. We make it + // point to the previous entry so GetNextRoute will work. + // + + if (RouterSegment->EnumerateLocation == &RouteEntry->PRIVATE.Linkage) { + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Blink; + } + + RemoveEntryList (&RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipDeleteRoute */ + + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the first router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetNextRoute to enumerate all the + entries in a segment. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The first router table entry, or NULL if the segment is empty. + +--*/ + +{ + PIPX_ROUTE_ENTRY FirstEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->Entries.Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + FirstEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return FirstEntry; + + } + +} /* RipGetFirstRoute */ + + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the next router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetFirstRoute to enumerate all the + entries in a segment. + + It is illegal to call RipGetNextRoute on a segment + without first calling RipGetFirstRoute. The segment + lock must be held for the duration of the enumeration + of a single segment. It is legal to stop enumerating + the segment in the middle. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The next router table entry, or NULL if the end of the + segment is reached. + +--*/ + +{ + PIPX_ROUTE_ENTRY NextEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + NextEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return NextEntry; + + } + +} /* RipGetNextRoute */ + + +VOID +RipDropRemoteEntries( + VOID + ) + +/*++ + +Routine Description: + + This routine deletes all non-local entries from the + RIP database. It is called when the WAN line goes up + or down and we want to remove all existing entries. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through, deleting everything but local entries. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, dropping remote entries\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipDropRemoteEntries */ + diff --git a/private/ntos/tdi/isnp/ipx/send.c b/private/ntos/tdi/isnp/ipx/send.c new file mode 100644 index 000000000..fd9a62a7d --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/send.c @@ -0,0 +1,1651 @@ + +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code that implements the send engine for the + IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - August-25-1995 + Bug Fixes - tagged [SA] + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// BUGBUG Using the macro for performance reasons. Should be taken out +// when NdisQueryPacket is optimized. In the near future (after PPC release) +// move this to a header file and use it at other places. +// +#define IPX_PACKET_HEAD(Pkt) (Pkt)->Private.Head + +#if 0 +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} +#endif + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(NdisPacket->ProtocolReserved); + PADAPTER Adapter = (PADAPTER)ProtocolBindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PDEVICE Device = IpxDevice; + PBINDING Binding; + USHORT NewId, OldId; + ULONG NewOffset, OldOffset; + PIPX_HEADER IpxHeader; + IPX_LOCAL_TARGET LocalTarget; + PIO_STACK_LOCATION irpSp; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + +#if DBG + if (Adapter != NULL) { + ASSERT_ADAPTER(Adapter); + } +#endif + + // + // See if this send was padded. + // + + if (Reserved->PaddingBuffer) { + + UINT Offset; + // + // Check if we simply need to re-adjust the buffer length. This will + // happen if we incremented the buffer length in MAC.C. + // + + if (Reserved->PreviousTail) { + CTEAssert (NDIS_BUFFER_LINKAGE(Reserved->PaddingBuffer->NdisBuffer) == NULL); + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT BufferLength; + + NdisQueryBufferOffset( LastBuffer, &Offset, &BufferLength ); + NdisAdjustBufferLength( LastBuffer, (BufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (NdisPacket); + } + } + +FunctionStart:; + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + + // + // Check if this packet should be sent to all + // networks. + // + + if (Reserved->u.SR_DG.CurrentNicId) { + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->u.SR_DG.Net0SendSucceeded = TRUE; + } + + OldId = Reserved->u.SR_DG.CurrentNicId; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if ((Binding = NIC_ID_TO_BINDING(Device, NewId)) +#else + for (NewId = OldId+1; NewId <= Device->HighestExternalNicId; NewId++) { + if ((Binding = Device->Bindings[NewId]) +#endif _PNP_POWER + && + ((!Device->SingleNetworkActive) || + (Device->ActiveNetworkWan == Binding->Adapter->MacInfo.MediumAsync)) + && + ((!Device->DisableDialoutSap) || + (!Binding->DialOutAsync) || + (!Reserved->u.SR_DG.OutgoingSap))) { + + // + // The binding exists, and we either are not configured + // for "SingleNetworkActive", or we are and this binding + // is the right type (i.e. the active network is wan and + // this is a wan binding, or the active network is not + // wan and this is not a wan binding), and this is not + // an outgoing sap that we are trying to send with + // "DisableDialoutSap" set. + // + + break; + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + + Reserved->u.SR_DG.CurrentNicId = NewId; + CTEAssert ((Reserved->DestinationType == DESTINATION_BCAST) || + (Reserved->DestinationType == DESTINATION_MCAST)); + +#if 0 + NewOffset = Binding->BcMcHeaderSize; + OldOffset = Device->Bindings[OldId]->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER)); + + } + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); +#endif + + + +#if BACK_FILL + // This should be a normal packet. Backfill packet is never used for + // reserved other than IPX type + + CTEAssert(!Reserved->BackFill); +#endif + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&LocalTarget, NewId); +#else + LocalTarget.NicId = NewId; +#endif + RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual net. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + + if ((NdisStatus = IpxSendFrame( + &LocalTarget, + NdisPacket, + REQUEST_INFORMATION(Reserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + goto FunctionStart; + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + + return; + + } else { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + + if (Reserved->u.SR_DG.Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + + } + + +#if 0 + // + // NOTE: We don't NULL out the linkage field of the + // HeaderBuffer, which will leave the old buffer chain + // hanging off it; but that is OK because if we reuse + // this packet we will replace that chain with the new + // one, and before we free it we NULL it out. + // + // I.e. we don't do this: + // + + NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer) = NULL; + NdisRecalculatePacketCounts (NdisPacket); +#endif + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer), &ActualLength); + if (ActualLength != REQUEST_INFORMATION(Reserved->u.SR_DG.Request)) { + DbgPrint ("IPX: At completion, IRP %lx has parameter length %d, buffer chain length %d\n", + Reserved->u.SR_DG.Request, REQUEST_INFORMATION(Reserved->u.SR_DG.Request), ActualLength); + DbgBreakPoint(); + } + } +#endif + + // + // Save these so we can free the packet. + // + + Request = Reserved->u.SR_DG.Request; + AddressFile = Reserved->u.SR_DG.AddressFile; + + +#if BACK_FILL + // Check if this is backfilled. If so restore users Mdl back to its original shape + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + + if (Reserved->BackFill) { + + Reserved->HeaderBuffer->MappedSystemVa = Reserved->MappedSystemVa; + Reserved->HeaderBuffer->ByteCount = Reserved->UserLength; + Reserved->HeaderBuffer->StartVa = (PCHAR)((ULONG)Reserved->HeaderBuffer->MappedSystemVa & ~(PAGE_SIZE-1)); + Reserved->HeaderBuffer->ByteOffset = (ULONG)Reserved->HeaderBuffer->MappedSystemVa & (PAGE_SIZE-1); + + IPX_DEBUG(SEND, ("completeing back filled userMdl %x\n",Reserved->HeaderBuffer)); + + NdisPacket->Private.ValidCounts = FALSE; + + NdisPacket->Private.Head = NULL; + NdisPacket->Private.Tail = NULL; + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } + // not a back fill packet. Push it on sendpacket pool + else { + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + + } + +#else + + if (Reserved->OwnedByAddress) { + + + Reserved->Address->SendPacketInUse = FALSE; + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } +#endif + + ++Device->Statistics.PacketsSent; + + // + // If this is a fast send irp, we bypass the file system and + // call the completion routine directly. + // + + REQUEST_STATUS(Request) = NdisStatus; + irpSp = IoGetCurrentIrpStackLocation( Request ); + + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + } else { + IpxCompleteRequest (Request); + } + + IpxFreeRequest(Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + + break; + + case IDENTIFIER_RIP_INTERNAL: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + break; + + case IDENTIFIER_RIP_RESPONSE: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + break; + +#ifdef _PNP_POWER + case IDENTIFIER_NB: + case IDENTIFIER_SPX: + + // + // See if this is an iterative send + // + if (OldId = Reserved->CurrentNicId) { + + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + PUCHAR Header; + PIPX_HEADER IpxHeader; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->Net0SendSucceeded = TRUE; + } + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if (Binding = NIC_ID_TO_BINDING(Device, NewId)) { + // + // Found next NIC to send on + // + break; + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + IPX_DEBUG(SEND, ("ISN iteration: OldId: %lx, NewId: %lx\n", OldId, NewId)); + Reserved->CurrentNicId = NewId; +#if 0 + NewOffset = Binding->BcMcHeaderSize; + OldOffset = Device->Bindings[OldId]->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER)); + + } + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + + +#if BACK_FILL + // This should be a normal packet. Backfill packet is never used for + // reserved other than IPX type + + CTEAssert(!Reserved->BackFill); +#endif +#endif + + NdisQueryPacket (NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + + IpxHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + IPX_DEBUG(SEND, ("SendComplete: IpxHeader: %lx\n", IpxHeader)); + FILL_LOCAL_TARGET(&Reserved->LocalTarget, NewId); + + // + // We don't need to so this since the macaddress is replaced in + // IpxSendFrame anyway. The LocalTarget is the same as the one on + // the original send - this is passed down for further sends. + // + // RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + &Reserved->LocalTarget, + NdisPacket, + Reserved->PacketLength, + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + goto FunctionStart; + } + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + return; + + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + if (Reserved->Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + } + + // + // fall thru' + // +#endif + default: + + (*Device->UpperDrivers[Reserved->Identifier].SendCompleteHandler)( + NdisPacket, + NdisStatus); + break; + } + +} /* IpxSendComplete */ + + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS_FILE AddressFile; + PADDRESS Address; + PNDIS_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + TDI_ADDRESS_IPX UNALIGNED * RemoteAddress; + TDI_ADDRESS_IPX TempAddress; + TA_ADDRESS UNALIGNED * AddressName; + PTDI_CONNECTION_INFORMATION Information; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PBINDING Binding; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = IpxDevice; + UCHAR PacketType; + NTSTATUS Status; + PIPX_HEADER IpxHeader; + NDIS_STATUS NdisStatus; + USHORT LengthIncludingHeader; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PIO_STACK_LOCATION irpSp; \ + BOOLEAN IsLoopback = FALSE; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + IPX_BEGIN_SYNC (&SyncContext); + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) && + ((Address = AddressFile->Address) != NULL)) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_CLOSING) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + Information = Parameters->SendDatagramInformation; + + // + // Do a quick check if this address has only one entry. + // + + AddressName = &((TRANSPORT_ADDRESS UNALIGNED *)(Information->RemoteAddress))->Address[0]; + + if ((AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) && + (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX))) { + + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)(AddressName->Address); + + } else if ((RemoteAddress = IpxParseTdiAddress (Information->RemoteAddress)) == NULL) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_ADDRESS; + goto error_send_no_packet; + } + + IPX_DEBUG (SEND, ("Send on %lx, network %lx socket %lx\n", + Address, RemoteAddress->NetworkAddress, RemoteAddress->Socket)); + +#if 0 + if (Parameters->SendLength > IpxDevice->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + IpxDevice->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_no_packet; + } +#endif + // + // Every address has one packet committed to it, use that + // if possible, otherwise take one out of the pool. + // + + +#if BACK_FILL + + // If the request is coming from the server, which resrves transport header space + // build the header in its space. Allocate a special packet to which does not contain + // mac and ipx headers in its reserved space. + + if ((PMDL)REQUEST_NDIS_BUFFER(Request) && + (((PMDL)REQUEST_NDIS_BUFFER(Request))->MdlFlags & MDL_NETWORK_HEADER) && + (!(Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS))) && + (RemoteAddress->NodeAddress[0] != 0xff)) { + + //if (!Address->BackFillPacketInUse) { + if (InterlockedExchangeAdd(&Address->BackFillPacketInUse, 0) == 0) { + //Address->BackFillPacketInUse = TRUE; + InterlockedIncrement(&Address->BackFillPacketInUse); + + Packet = PACKET(&Address->BackFillPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEBUG(SEND, ("Getting owned backfill %x %x \n", Packet,Reserved)); + + }else { + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBackFillPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBackFillPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotBackFillPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + IPX_DEBUG(SEND, ("getting backfill packet %x %x %x\n", s, Reserved, RemoteAddress->NodeAddress)); + if(!Reserved->BackFill)DbgBreakPoint(); + + } + + }else { + + // if (!Address->SendPacketInUse) { + if (InterlockedExchangeAdd(&Address->SendPacketInUse, 0) == 0) { + // Address->SendPacketInUse = TRUE; + InterlockedIncrement(&Address->SendPacketInUse); + + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + Reserved->BackFill = FALSE; + + } + + } + + +#else + + if (!Address->SendPacketInUse) { + + Address->SendPacketInUse = TRUE; + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + +#endif + + IpxReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + // + // Save this now while we have Parameters available. + // + + REQUEST_INFORMATION(Request) = Parameters->SendLength; + LengthIncludingHeader = (USHORT)(Parameters->SendLength + sizeof(IPX_HEADER)); + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(REQUEST_NDIS_BUFFER(Request), &ActualLength); + if (ActualLength != Parameters->SendLength) { + DbgPrint ("IPX: IRP %lx has parameter length %d, buffer chain length %d\n", + Request, Parameters->SendLength, ActualLength); + DbgBreakPoint(); + } + } +#endif + + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.Request = Request; + CTEAssert (Reserved->Identifier == IDENTIFIER_IPX); + + + // + // Set this to 0; this means the packet is not one that + // should be broadcast on all nets. We will change it + // later if it turns out this is the case. + // + + Reserved->u.SR_DG.CurrentNicId = 0; + + // + // We need this to track these packets specially. + // + + Reserved->u.SR_DG.OutgoingSap = AddressFile->IsSapSocket; + + // + // Add the MDL chain after the pre-allocated header buffer. + // NOTE: THIS WILL ONLY WORK IF WE EVENTUALLY CALL + // NDISRECALCULATEPACKETCOUNTS (which we do in IpxSendFrame). + // + // +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->HeaderBuffer = REQUEST_NDIS_BUFFER(Request); + + //remove the ipx mdl from the packet. + Reserved->UserLength = Reserved->HeaderBuffer->ByteCount; + + IPX_DEBUG(SEND, ("back filling userMdl Reserved %x %x\n", Reserved->HeaderBuffer, Reserved)); + } else { + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); + } +#else + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); +#endif + + + if (Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS)) { + + // + // The caller did not supply the local target for this + // send, so we look it up ourselves. + // + + UINT Segment; + + // + // We calculate this now since we need to know + // if it is directed below. + // + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send + } + + // + // If there are no options, then check if the + // caller is passing the packet type as a final byte + // in the remote address; if not use the default. + // + + if (Information->OptionsLength == 0) { + if (AddressFile->ExtendedAddressing) { + PacketType = ((PUCHAR)(RemoteAddress+1))[0]; + } else { + PacketType = AddressFile->DefaultPacketType; + } + } else { + PacketType = ((PUCHAR)(Information->Options))[0]; + } + + if ((Reserved->DestinationType != DESTINATION_DEF) && + ((RemoteAddress->NetworkAddress == 0) || + (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)))) { + + // + // This packet needs to be broadcast to all networks. + // Make sure it is not too big for any of them. + // + + if (Parameters->SendLength > Device->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, Device->RealMaxDatagramSize)); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + // + // If this is a broadcast to the virtual net, we + // need to construct a fake remote address which + // has network 0 in there instead. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + RtlCopyMemory (&TempAddress, (PVOID)RemoteAddress, sizeof(TDI_ADDRESS_IPX)); + TempAddress.NetworkAddress = 0; + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)&TempAddress; + } + + // + // If someone is sending to the SAP socket and + // we are running with multiple cards without a + // virtual network, AND this packet is a SAP response, + // then we log an error to warn them that the + // system may not work as they like (since there + // is no virtual network to advertise, we use + // the first card's net/node as our local address). + // We only do this once per boot, using the + // SapWarningLogged variable to control that. + // + + if ((RemoteAddress->Socket == SAP_SOCKET) && + (!Device->SapWarningLogged) && + (Device->MultiCardZeroVirtual)) { + + PNDIS_BUFFER FirstBuffer; + UINT FirstBufferLength; + USHORT UNALIGNED * FirstBufferData; + + if ((FirstBuffer = REQUEST_NDIS_BUFFER(Request)) != NULL) { + + NdisQueryBuffer( + FirstBuffer, + (PVOID *)&FirstBufferData, + &FirstBufferLength); + + // + // The first two bytes of a SAP packet are the + // operation, 0x2 (in network order) is response. + // + + if ((FirstBufferLength >= sizeof(USHORT)) && + (*FirstBufferData == 0x0200)) { + + Device->SapWarningLogged = TRUE; + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_IPX_SAP_ANNOUNCE, + 777, + STATUS_NOT_SUPPORTED, + NULL, + 0, + NULL); + } + } + } + + + // + // In this case we do not RIP but instead set the + // packet up so it is sent to each network in turn. + // + // Special case: If this packet is from the SAP + // socket and we are running with multiple cards + // without a virtual network, we only send this + // on the card with NIC ID 1, so we leave + // CurrentNicId set to 0. + // + + // + // BUGBUG: What if NicId 1 is invalid? Should scan + // for first valid one, fail send if none. + // + + if ((Address->Socket != SAP_SOCKET) || + (!Device->MultiCardZeroVirtual)) { + + if (Device->SingleNetworkActive) { + + if (Device->ActiveNetworkWan) { + Reserved->u.SR_DG.CurrentNicId = Device->FirstWanNicId; + } else { + Reserved->u.SR_DG.CurrentNicId = Device->FirstLanNicId; + } + + } else { + + Reserved->u.SR_DG.CurrentNicId = 1; + + } + + Reserved->u.SR_DG.Net0SendSucceeded = FALSE; + + // + // In this case, we need to scan for the first + // non-dialout wan socket. + // + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET)) { + + PBINDING TempBinding; + + CTEAssert (Reserved->u.SR_DG.CurrentNicId <= Device->ValidBindings); + while (Reserved->u.SR_DG.CurrentNicId <= MIN (Device->MaxBindings, Device->ValidBindings)) { +#ifdef _PNP_POWER +// No need to lock the access path since he just looks at it +// + TempBinding = NIC_ID_TO_BINDING(Device, Reserved->u.SR_DG.CurrentNicId); +#else + TempBinding = Device->Bindings[Reserved->u.SR_DG.CurrentNicId]; +#endif _PNP_POWER + if ((TempBinding != NULL) && + (!TempBinding->DialOutAsync)) { + break; + } + ++Reserved->u.SR_DG.CurrentNicId; + } + if (Reserved->u.SR_DG.CurrentNicId > MIN (Device->MaxBindings, Device->ValidBindings)) { + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + + goto error_send_with_packet; + } + } +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, Reserved->u.SR_DG.CurrentNicId); +#else + TempLocalTarget.NicId = Reserved->u.SR_DG.CurrentNicId; +#endif + + } else { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, 1); +#else + TempLocalTarget.NicId = 1; +#endif + } + + RtlCopyMemory(TempLocalTarget.MacAddress, RemoteAddress->NodeAddress, 6); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + } else { + + Segment = RipGetSegment((PUCHAR)&RemoteAddress->NetworkAddress); + + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + Status = RipGetLocalTarget( + Segment, + RemoteAddress, + IPX_FIND_ROUTE_RIP_IF_NEEDED, + &TempLocalTarget, + NULL); + + if (Status == STATUS_SUCCESS) { + + // + // We found the route, TempLocalTarget is filled in. + // + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (NIC_FROM_LOCAL_TARGET(&TempLocalTarget) == 0) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + FILL_LOCAL_TARGET(&TempLocalTarget, 1); + } + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + if (Parameters->SendLength > + Binding->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Binding->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + goto error_send_with_packet; + } +#else + if (TempLocalTarget.NicId == 0) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + TempLocalTarget.NicId = 1; + } + + if (Parameters->SendLength > + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Device->Bindings[TempLocalTarget.NicId]->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + goto error_send_with_packet; + } +#endif _PNP_POWER + + } else if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this packet for transmission when the RIP + // response arrives. First we fill in the IPX + // header; the only thing we don't know is where + // exactly to fill it in, so we choose + // the most common location. + // + + IpxConstructHeader( + &Reserved->Header[Device->IncludedHeaderOffset], + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + // + // Adjust the 2nd mdl's size + // + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + + IPX_DEBUG (RIP, ("Queueing packet %lx\n", Reserved)); + + InsertTailList( + &Device->Segments[Segment].WaitingForRoute, + &Reserved->WaitLinkage); + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + goto error_send_with_packet; + + } + } + + LocalTarget = &TempLocalTarget; + + // + // Now we know the local target, we can figure out + // the offset for the IPX header. + // +#ifdef _PNP_POWER +// Remember that we have got the binding with ref above.... + +#else + Binding = Device->Bindings[LocalTarget->NicId]; +#endif + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; +#if 0 + if (Reserved->DestinationType == DESTINATION_DEF) { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } else { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } +#endif + + } else { + + PacketType = ((PUCHAR)(Information->Options))[0]; + LocalTarget = &((PIPX_DATAGRAM_OPTIONS)(Information->Options))->LocalTarget; + + // + // Calculate the binding and the correct location + // for the IPX header. We can do this at the same + // time as we calculate the DestinationType which + // saves an if like the one 15 lines up. + // + +#ifdef _PNP_POWER +// Get lock to ref. + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // If a loopback packet, use the first binding as place holder + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == 0) { + Binding = NIC_ID_TO_BINDING(Device, 1); + IsLoopback = TRUE; + } else { + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (LocalTarget->NicId == 0) { + Binding = Device->Bindings[1]; + IsLoopback = TRUE; + } else { + Binding = Device->Bindings[LocalTarget->NicId]; + } +#endif _PNP_POWER + if (Parameters->SendLength > Binding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + +#if 0 + // + // This shouldn't be needed because even WAN bindings + // don't go away once they are added. + // + + if (Binding == NULL) { + Status = STATUS_DEVICE_DOES_NOT_EXIST; + goto error_send_with_packet; + } +#endif + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; + + } + + + ++Device->TempDatagramsSent; + Device->TempDatagramBytesSent += Parameters->SendLength; + + +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->MappedSystemVa = Reserved->HeaderBuffer->MappedSystemVa; + IpxHeader = (PIPX_HEADER)((PCHAR)Reserved->HeaderBuffer->MappedSystemVa - sizeof(IPX_HEADER)); + Reserved->HeaderBuffer->ByteOffset -= sizeof(IPX_HEADER); + (ULONG)Reserved->HeaderBuffer->MappedSystemVa-= sizeof(IPX_HEADER); + IPX_DEBUG(SEND, ("Adjusting backfill userMdl Ipxheader %x %x \n",Reserved->HeaderBuffer,IpxHeader)); + } +#endif + + if (Device->MultiCardZeroVirtual || + (Address->LocalAddress.Socket == SAP_SOCKET) || + (RemoteAddress->Socket == SAP_SOCKET)) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual network number. + // + // If this is a binding set member and a local target + // was provided we will send using the real node of + // the binding, even if it was a slave. This is + // intentional. If no local target was provided then + // this will not be a binding slave. + // + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Binding->LocalAddress); + + IpxHeader->SourceSocket = Address->SendSourceSocket; + + } else { + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + } + + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + // + // Adjust the 2nd mdl's size + // +#if BACK_FILL + if (Reserved->BackFill) { + NdisAdjustBufferLength(Reserved->HeaderBuffer, (Reserved->HeaderBuffer->ByteCount+sizeof(IPX_HEADER))); + } else { + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + } +#else + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); +#endif + + IPX_DEBUG(SEND, ("Packet Head %x\n",IPX_PACKET_HEAD(Packet))); + + if (IsLoopback) { + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Packet: %lx, Addr: %lx, Addr->SendPacket: %lx\n", Packet, Address, Address->SendPacket)); + + // + // Recalculate packet counts here. + // + // NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#if BACK_FILL + + if (Reserved->BackFill) { + // + // Set the Header pointer and chain the first MDL + // + Reserved->Header = (PCHAR)Reserved->HeaderBuffer->MappedSystemVa; + NdisChainBufferAtFront(Packet,(PNDIS_BUFFER)Reserved->HeaderBuffer); + } +#endif + NdisRecalculatePacketCounts (Packet); +#ifdef _PNP_POWER + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); +#else + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); +#endif + + } else { + if ((NdisStatus = (*Binding->SendFrameHandler)( + Binding->Adapter, + LocalTarget, + Packet, + Parameters->SendLength + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + } + + IPX_END_SYNC (&SyncContext); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_PENDING; + + } else { + + // + // The address file state was closing. + // + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_HANDLE; + goto error_send_no_packet; + + } + + } else { + + // + // The address file didn't look like one. + // + + Status = STATUS_INVALID_HANDLE; + goto error_send_no_packet; + } + + // + // Jump here if we want to fail the send and we have already + // allocated the packet and ref'ed the address file. + // + +error_send_with_packet: + +#if BACK_FILL + // + // Check if this is backfilled. If so, set the headerbuffer to NULL. Note that we dont need + // restore to restore the user's MDL since it was never touched when this error occurred. + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + // + if (Reserved->BackFill) { + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + } else { + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } else { + // not a back fill packet. Push it on sendpacket pool + if (Reserved->OwnedByAddress) { + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + } +#else + if (Reserved->OwnedByAddress) { + Reserved->Address->SendPacketInUse = FALSE; + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } +#endif + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + +error_send_no_packet: + + // + // Jump here if we fail before doing any of that. + // + + IPX_END_SYNC (&SyncContext); + + irpSp = IoGetCurrentIrpStackLocation( Request ); + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + REQUEST_STATUS(Request) = Status; + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + IpxFreeRequest (DeviceObject, Request); + } + + return Status; + +} /* IpxTdiSendDatagram */ + + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ) + +/*++ + +Routine Description: + + This routine constructs an IPX header in a packet. + +Arguments: + + Header - The location at which the header should be built. + + PacketLength - The length of the packet, including the IPX header. + + PacketType - The packet type of the frame. + + RemoteAddress - The remote IPX address. + + LocalAddress - The local IPX address. + +Return Value: + + None. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)Header; + + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = (UCHAR)(PacketLength / 256); + IpxHeader->PacketLength[1] = (UCHAR)(PacketLength % 256); + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = PacketType; + + // + // These copies depend on the fact that the destination + // network is the first field in the 12-byte address. + // + + RtlCopyMemory(IpxHeader->DestinationNetwork, (PVOID)RemoteAddress, 12); + RtlCopyMemory(IpxHeader->SourceNetwork, LocalAddress, 12); + +} /* IpxConstructHeader */ +#endif + + diff --git a/private/ntos/tdi/isnp/ipx/sources.inc b/private/ntos/tdi/isnp/ipx/sources.inc new file mode 100644 index 000000000..449026087 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/sources.inc @@ -0,0 +1,74 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkipx + +TARGETNAME=nwlnkipx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +NTPROFILEINPUT=yes + + + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -DBACK_FILL=1 -DNDIS40=1 -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\adapter.c \ + ..\address.c \ + ..\config.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\ind.c \ + ..\internal.c \ + ..\nwlnkipx.rc \ + ..\mac.c \ + ..\ndis.c \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\rip.c \ + ..\send.c \ + ..\loopback.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj + +SOURCES_USED=..\sources.inc + + diff --git a/private/ntos/tdi/isnp/ipx/up/makefile b/private/ntos/tdi/isnp/ipx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf b/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isnp/ipx/up/sources b/private/ntos/tdi/isnp/ipx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/nb/action.c b/private/ntos/tdi/isnp/nb/action.c new file mode 100644 index 000000000..9ff843a76 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/action.c @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which implements the TDI action + dispatch routines. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles action requests. + +Arguments: + + Device - The netbios device. + + Request - The request describing the action. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + union { + PNB_ACTION_GET_COUNTS GetCounts; + } u; // BUGBUG: Make these unaligned?? + PNWLINK_ACTION NwlinkAction; + UINT i; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + NB_DEBUG (QUERY, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + NB_DEBUG (QUERY, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != NB_ADDRESSFILE_SIGNATURE)) { + + NB_DEBUG (QUERY, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + NB_DEBUG (QUERY, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + case (I_MIPX | 351): + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(NB_ACTION_GET_COUNTS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetCounts = (PNB_ACTION_GET_COUNTS)(NwlinkAction->Data); + + u.GetCounts->MaximumNicId = NbiDevice->MaximumNicId; + + for (i = 0; i < 32 ; i++) { + u.GetCounts->NicIdCounts[i] = 0; + } + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[i].Connections; + + while (Connection != NULL) { +#if defined(_PNP_POWER) + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicHandle.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicHandle.NicId]; + } +#else + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicId]; + } +#endif _PNP_POWER + Connection = Connection->NextConnection; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + NB_DEBUG (QUERY, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* NbiTdiAction */ + diff --git a/private/ntos/tdi/isnp/nb/address.c b/private/ntos/tdi/isnp/nb/address.c new file mode 100644 index 000000000..2eb882b80 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/address.c @@ -0,0 +1,2406 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == sizeof(TDI_ADDRESS_NETBIOS)) { + return ((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbiParseTdiAddress */ + + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbiPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbiValidateTdiAddress */ + + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the Netbios transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif +#if 0 + TA_NETBIOS_ADDRESS FakeAddress; +#endif + + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; +#if 0 + TdiBuildNetbiosAddress( + "ADAMBA67 ", + FALSE, + &FakeAddress); + name = (PTRANSPORT_ADDRESS)&FakeAddress; +#endif + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type Netbios. This call returns (PVOID)-1 if the + // address is the broadcast address. + // + + NetbiosAddress = NbiParseTdiAddress (name, TRUE); + + if (NetbiosAddress == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no Netbios Address\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // get an address file structure to represent this address. + // + + AddressFile = NbiCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + +#if defined(_PNP_POWER) + + Address = NbiFindAddress ( + Device, + ( NetbiosAddress == (PVOID)-1 ) ? (PVOID)-1 : NetbiosAddress->NetbiosName + ); + + if (Address == NULL) { + +#else + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Address = NbiLookupAddress (Device, NetbiosAddress); + + if (Address == NULL) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif _PNP_POWER + + // + // This address doesn't exist. Create it. + // This initializes the address with a ref + // of type ADDRESS_FILE, so if we fail here + // we need to remove that. + // + + Address = NbiCreateAddress ( + Device, + NetbiosAddress); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { +#else + if (Device->State == DEVICE_STATE_STOPPING) { +#endif _PNP_POWER + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Address = Address; + + NB_INSERT_TAIL_LIST( + &Address->AddressFileDatabase, + &AddressFile->Linkage, + &Address->Lock); + + if (NetbiosAddress == (PVOID)-1) { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } else { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + NbiStartRegistration (Address); + } + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + NbiDestroyAddressFile (AddressFile); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + +#if !defined(_PNP_POWER) + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif !_PNP_POWER + NB_DEBUG2 (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // Make sure the types do not conflict. + // + + if ((NetbiosAddress != (PVOID)-1) && + (NetbiosAddress->NetbiosNameType != Address->NetbiosAddress.NetbiosNameType)) { + + NB_DEBUG (ADDRESS, ("Address types conflict %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + NB_DEBUG (ADDRESS, ("Address access not allowed %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + NB_DEBUG (ADDRESS, ("Address share access wrong %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // Insert the address file on the address + // list; we will pend this open if the address + // is still registering. If the address has + // already failed as duplicate, then we + // fail the open. + // + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Address duplicated %lx\n", Address)); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + // + // Start registration unless it is registered or + // it is the broadcast address. + // + + if ((Address->State == ADDRESS_STATE_REGISTERING) && + (NetbiosAddress != (PVOID)-1)) { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + } else { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + } + + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + + NbiReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } + + } + } + } + + // + // Remove the reference from NbiLookupAddress. + // + + NbiDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* NbiOpenAddress */ + + +VOID +NbiStartRegistration( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process for a netbios name + by sending out the first add name packet and starting the timer + so that NbiRegistrationTimeout is called after the correct timeout. + +Arguments: + + Address - The address which is to be registered. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NB_DEBUG2 (ADDRESS, ("StartRegistration of %lx\n", Address)); + + // + // First send out an add name packet. + // + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + Address->RegistrationCount = 0; + + // + // Now start the timer. + // + + NbiReferenceAddress (Address, AREF_TIMER); + + CTEInitTimer (&Address->RegistrationTimer); + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + +} /* NbiStartRegistration */ + + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the address registration + timer expires. It sends another add name if needed, or + checks the result if the correct number have been sent. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the address pointer. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Context; + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PLIST_ENTRY p; + + ++Address->RegistrationCount; + + if ((Address->RegistrationCount < Address->Device->BroadcastCount) && + ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0)) { + + NB_DEBUG2 (ADDRESS, ("Send add name %d for %lx\n", Address->RegistrationCount+1, Address)); + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + + } else { + + // + // The correct number of frames have been sent, see what + // happened. + // + + NB_DEBUG2 (ADDRESS, ("Done with add names for %lx\n", Address)); + + ReferencedAddressFile = NULL; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + Address->State = ADDRESS_STATE_OPEN; + } else { + Address->State = ADDRESS_STATE_STOPPING; + } + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + CTEAssert (AddressFile->State == ADDRESSFILE_STATE_OPENING); + CTEAssert (AddressFile->OpenRequest != NULL); + + NbiReferenceAddressFileLock (AddressFile, AFREF_TIMEOUT); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + // + // Now see what to do with this address file. + // + + REQUEST_INFORMATION(AddressFile->OpenRequest) = 0; + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Open of address file %lx failed, duplicate\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_DUPLICATE_NAME; + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + NB_DEBUG2 (ADDRESS, ("Complete open of address file %lx\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_SUCCESS; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + } + + NbiCompleteRequest (AddressFile->OpenRequest); + NbiFreeRequest (Address->Device, AddressFile->OpenRequest); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + ReferencedAddressFile = AddressFile; + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + NbiDereferenceAddress (Address, AREF_TIMER); + + } + +} /* NbiRegistrationTimeout */ + + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_FIND_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Quick check for any names starting with this character. + // + + if (Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] == 0) { + return; + } + + // + // Always respond to broadcast requests. + // +#if defined(_PNP_POWER) + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + NbiDereferenceAddress (Address, AREF_FIND); + + } else if ( NbiFindAdapterAddress( NbConnectionless->NameFrame.Name, LOCK_NOT_ACQUIRED ) ) { + + NbiSendNameFrame( + NULL, + (UCHAR)(NB_NAME_UNIQUE | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + } +#else + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + NbiDereferenceAddress (Address, AREF_FIND); + + } +#endif _PNP_POWER +} /* NbiProcessFindName */ + + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_ADD_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + BOOLEAN LocalFrame; + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Ignore any frame that came from us, except for the purpose + // of updating the cache. + // + + if ((Device->Bind.QueryHandler)( + IPX_QUERY_IS_ADDRESS_LOCAL, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + NbConnectionless->IpxHeader.SourceNetwork, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + LocalFrame = TRUE; + + } else { + + LocalFrame = FALSE; + + } + + if (!LocalFrame) { + + if ((Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] != 0) && + (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name))) { + + if (NB_NODE_BROADCAST(NbConnectionless->IpxHeader.DestinationNode)) { + + // + // If this frame is an add name (identified because it is a + // broadcast frame) then respond if we have it registered + // unique, or we have it group and someone is trying to add + // it unique. + // + + if ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) || + ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) && + ((NbConnectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0))) { + + // + // According to GeorgeJ's doc, on a name in use we just + // echo back the name type flags from the request. + // + + NbiSendNameFrame( + Address, + NbConnectionless->NameFrame.NameTypeFlag, + NB_CMD_NAME_IN_USE, + RemoteAddress, +#if defined(_PNP_POWER) + NbConnectionless); +#else + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); +#endif _PNP_POWER + } + + } else if ((*(UNALIGNED ULONG *)NbConnectionless->IpxHeader.DestinationNetwork == + *(UNALIGNED ULONG *)Device->Bind.Network) && + NB_NODE_EQUAL(NbConnectionless->IpxHeader.DestinationNode, Device->Bind.Node)) { + + // + // If this is an add name response (which will be sent + // directly to us) then we need to mark the address + // as such. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + } + + + // + // Pass this frame over to the netbios cache management + // routines to check if they need to update their cache. + // + + CacheUpdateFromAddName (RemoteAddress, NbConnectionless, LocalFrame); + +} /* NbiProcessAddName */ + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetbiosAddress - The name to assign to this address, or -1 if it + is the broadcast address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + + Address = (PADDRESS)NbiAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + NB_DEBUG (ADDRESS, ("Create address %.16s failed\n", + (NetbiosAddress == (PVOID)-1) ? "" : NetbiosAddress->NetbiosName)); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address %lx (%.16s)\n", Address, + (NetbiosAddress == (PVOID)-1) ? "" : NetbiosAddress->NetbiosName)); + RtlZeroMemory (Address, sizeof(ADDRESS)); + + Address->Type = NB_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + Address->State = ADDRESS_STATE_REGISTERING; + Address->Flags = 0; + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock.Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + + if (NetbiosAddress == (PVOID)-1) { + Address->NetbiosAddress.Broadcast = TRUE; + } else { + Address->NetbiosAddress.Broadcast = FALSE; + Address->NetbiosAddress.NetbiosNameType = NetbiosAddress->NetbiosNameType; + RtlCopyMemory (Address->NetbiosAddress.NetbiosName, NetbiosAddress->NetbiosName, 16); + ++Device->AddressCounts[NetbiosAddress->NetbiosName[0]]; + } + + if (Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) { + Address->NameTypeFlag = NB_NAME_UNIQUE; + } else { + Address->NameTypeFlag = NB_NAME_GROUP; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&Device->AddressDatabase, &Address->Linkage); + ++Device->AddressCount; + + NbiReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* NbiCreateAddress */ + + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + + ConflictIsOk - TRUE if we should succeed the verify even if the + corresponding address is in CONFLICT. ( For Close and + cleanup we return STATUS_SUCCESS even if we are in conflict + so that the addressfile can be destroyed) + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + BOOLEAN LockHeld = FALSE; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == NB_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == NB_ADDRESS_SIGNATURE) ) { + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + LockHeld = TRUE; + +#if defined(_PNP_POWER) + if (Address->State != ADDRESS_STATE_STOPPING && + ( ConflictIsOk || ( !(Address->Flags & ADDRESS_FLAGS_CONFLICT) )) ) { +#else + if (Address->State != ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + + NbiReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbiPrint1("NbiVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyAddressFile: AF %lx exception\n", Address); + if (LockHeld) { + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyAddressFile */ + + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by NbiDerefAddress when + the reference count goes to 0. + + This thread is only queued by NbiDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + NB_DEBUG2 (ADDRESS, ("Destroy address %lx <%.16s>\n", Address, + Address->NetbiosAddress.Broadcast ? "" : Address->NetbiosAddress.NetbiosName)); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (!Address->NetbiosAddress.Broadcast) { + --Device->AddressCounts[Address->NetbiosAddress.NetbiosName[0]]; + } + --Device->AddressCount; + RemoveEntryList (&Address->Linkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + NbiDereferenceDevice (Device, DREF_ADDRESS); + +} /* NbiDestroyAddress */ + + +#if DBG +VOID +NbiRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); +} /* NbiRefAddress */ + + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); + +} /* NbiRefAddressLock */ +#endif + + +VOID +NbiDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &Address->ReferenceCount ); + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbiDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + NbiDestroyAddress(Address); +#endif + + } + +} /* NbiDerefAddress */ + + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + UINT i; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)NbiAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + NB_DEBUG (ADDRESS, ("Create address file failed\n")); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = NB_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + for (i = 0; i < 6; i++) { + AddressFile->RegisteredHandler[i] = FALSE; + AddressFile->HandlerContexts[i] = NULL; + AddressFile->Handlers[i] = TdiDefaultHandlers[i]; + } + + CTEAssert (AddressFile->ConnectionHandler == TdiDefaultConnectHandler); + CTEAssert (AddressFile->DisconnectHandler == TdiDefaultDisconnectHandler); + CTEAssert (AddressFile->ErrorHandler == TdiDefaultErrorHandler); + CTEAssert (AddressFile->ReceiveHandler == TdiDefaultReceiveHandler); + CTEAssert (AddressFile->ReceiveDatagramHandler == TdiDefaultRcvDatagramHandler); + CTEAssert (AddressFile->ExpeditedDataHandler == TdiDefaultRcvExpeditedHandler); + + return AddressFile; + +} /* NbiCreateAddressFile */ + + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbiDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + BOOLEAN StopAddress; + + NB_DEBUG2 (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle1); + Address->State = ADDRESS_STATE_STOPPING; + NB_FREE_LOCK (&Device->Lock, LockHandle1); + + StopAddress = TRUE; + + } else { + + StopAddress = FALSE; + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + if (StopAddress && (!Address->NetbiosAddress.Broadcast)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | + NB_NAME_USED | NB_NAME_REGISTERED | NB_NAME_DEREGISTERED), + NB_CMD_DELETE_NAME, + NULL, + NULL); + } + + // + // Now dereference the owning address. + // + + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + NbiFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + NbiCompleteRequest (CloseRequest); + NbiFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* NbiDestroyAddressFile */ + + +#if DBG +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); +} /* NbiRefAddressFile */ + + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); + +} /* NbiRefAddressFileLock */ + +#endif + + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &AddressFile->ReferenceCount ); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + NbiDestroyAddressFile (AddressFile); + } + +} /* NbiDerefAddressFile */ + +#if !defined(_PNP_POWER) + +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosAddress - The name to look up, or -1 if the broadcast + address is being searched for. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->State == ADDRESS_STATE_STOPPING) { + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosAddress != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if (NetbiosAddress == (PVOID)-1) { + continue; + } + + if (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosAddress->NetbiosName, + 16)) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* NbiLookupAddress */ +#endif !_PNP_POWER + + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NetbiosName + values. If a match is found, the address is referenced and the + pointer is returned. + + We ignore any addresses which are either STOPPING or are under + CONFLICT state. + + A name in CONFLICT is dead for all practical purposes + except Close. This routine is called by various name service, + datagram and session sevice routines. We hide any names in CONFLICT + from these routines. + + This routine is also called by NbiTdiOpenAddress(). + A name could have been marked in CONFLICT ages ago(but is not closed + yet). We must allow another open of the same name as that might + succeed now. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosName - The name to look up, or -1 for the broadcast name. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + +#if defined(_PNP_POWER) + if ( ( Address->State == ADDRESS_STATE_STOPPING ) || + ( Address->Flags & ADDRESS_FLAGS_CONFLICT ) ) { +#else + if (Address->State == ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosName != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if ((NetbiosName == (PVOID)-1) || + (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosName, + 16))) { + continue; + } + } + + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_FIND); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + +} /* NbiFindAddress */ + + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PLIST_ENTRY p; + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle1, LockHandle2; + LIST_ENTRY SendDatagramList; + PNB_SEND_RESERVED Reserved; + PREQUEST DatagramRequest; + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + LIST_ENTRY DatagramQ; + + + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + NB_FREE_LOCK (&Address->Lock, LockHandle1); + return STATUS_SUCCESS; + } + + + // + // This prevents anybody else from being put on the + // ConnectionDatabase. + // + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + CTEAssert (Connection->AddressFile == AddressFile); + Connection->AddressFileLinked = FALSE; + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->ReferenceCount == 0) { + + // + // The refcount is already 0, so we can just + // NULL out this field to complete the disassociate. + // + + Connection->AddressFile = NULL; + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + // + // Mark this so we know to disassociate when the + // count goes to 0, but that there is no specific + // request pending on it. We also stop the connection + // to shut it down. + // + + Connection->DisassociatePending = (PVOID)-1; + NbiReferenceConnectionLock (Connection, CREF_DISASSOC); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle3); + + // + // This call frees the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle3)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_DISASSOC); + + } + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Abort all pending send datagrams. + // + // BUGBUG: Also make them cancellable. + // + + InitializeListHead (&SendDatagramList); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + if (Reserved->u.SR_DG.AddressFile == AddressFile) { + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&SendDatagramList, &Reserved->WaitLinkage); + + } + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + for (p = SendDatagramList.Flink; p != &SendDatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Aborting datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + + // + // Abort all pending receive datagrams. + // + + InitializeListHead( &DatagramQ ); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + while (!IsListEmpty(&AddressFile->ReceiveDatagramQueue)) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + // Insert it on a private Q, so it can be completed later. + InsertTailList( &DatagramQ, p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + for( p = DatagramQ.Flink; p != &DatagramQ; ) { + Request = LIST_ENTRY_TO_REQUEST ( p ); + + p = p->Flink; + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + } + + + return STATUS_SUCCESS; + +} /* NbiStopAddressFile */ + + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + NbiStopAddressFile (AddressFile, Address); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* NbiCloseAddressFile */ + +#if defined(_PNP_POWER) + + +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ) + +/*++ + +Routine Description: + + This routine creates an adapter address sttuctures which stores + the netbios name of an adapter. the netbios name has 12 0's + followed by the mac address of the adapter. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AdapterMacAddress - pointer to the adapter mac address given to us + by IPX. + +Return Value: + + The newly created address, or NULL if none can be allocated. + THIS ROUTINE MUST BE CALLED WITH THE DEVICE LOCK HELD. + +--*/ + +{ + PADAPTER_ADDRESS AdapterAddress; + CTELockHandle LockHandle; + PDEVICE Device = NbiDevice; + + AdapterAddress = (PADAPTER_ADDRESS)NbiAllocateMemory (sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "Adapter Address"); + if (AdapterAddress == NULL) { + NB_DEBUG (ADDRESS, ("Create Adapter Address %<2.2x><2.2x><2.2x><2.2x><2.2x><2.2x> failed\n", + AdapterMacAddress[0], + AdapterMacAddress[1], + AdapterMacAddress[2], + AdapterMacAddress[3], + AdapterMacAddress[4], + AdapterMacAddress[5] + )); + return NULL; + } + + AdapterAddress->Type = NB_ADAPTER_ADDRESS_SIGNATURE; + AdapterAddress->Size = sizeof (ADDRESS); + + RtlZeroMemory(AdapterAddress->NetbiosName, 10); + RtlCopyMemory(&AdapterAddress->NetbiosName[10], AdapterMacAddress, 6); + + + InsertTailList (&Device->AdapterAddressDatabase, &AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + return AdapterAddress; + +} /* NbiCreateAdapterAddress */ + + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine destroys the adapter address structure and removes it + from the list. + +Arguments: + + AdapterAddress - Pointer to an adapter address structure to be destroyed + NULL if AdapterMacAddress is given. + + AdapterMacAddress - Mac Address of the adapter which just got deleted. so find + the corresponding adapter address structure and remove it. + NULL if AdapterAddress is supplied. + +Return Value: + + STATUS_SUCCESS or STATUS_UNSUCCESSFUL if address not found. + + THIS ROUTINE ASSUMES THE THE DEVICE IS LOCK IS HELD BY THE CALLER + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + UCHAR NetbiosName[NB_NETBIOS_NAME_SIZE]; + + + // + + CTEAssert( AdapterAddress || AdapterMacAddress ); + if ( !AdapterAddress ) { + RtlZeroMemory( NetbiosName, 10); + RtlCopyMemory( &NetbiosName[10], AdapterMacAddress, 6 ); + + AdapterAddress = NbiFindAdapterAddress( NetbiosName, LOCK_ACQUIRED ); + + if ( !AdapterAddress ) { + return STATUS_UNSUCCESSFUL; + } + } + + NB_DEBUG2 (ADDRESS, ("Destroy Adapter address %lx <%.16s>\n", AdapterAddress,AdapterAddress->NetbiosName)); + RemoveEntryList (&AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + NbiFreeMemory (AdapterAddress, sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "AdapterAddress"); + + return STATUS_SUCCESS; +} /* NbiDestroyAdapterAddress */ + + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ) + +/*++ + +Routine Description: + + This routine finds an adapter address ( netbios name ) for the given + AdapterMacAddress and returns a pointer to it. Note that no reference + is done on this address, so if this routine is called without the device + lock, the caller must not use this pointer directly. + +Arguments: + + NetbiosName - NetbiosName to be found. + + LockHeld - is device lock already held or not. + +Return Value: + + Pointer to the adapter address if found, NULL otherwise. + +--*/ + +{ + + PLIST_ENTRY p; + CTELockHandle LockHandle; + PADAPTER_ADDRESS AdapterAddress; + PDEVICE Device = NbiDevice; + + + if ( !LockHeld ) { + NB_GET_LOCK( &Device->Lock, &LockHandle ); + } + for ( p = Device->AdapterAddressDatabase.Flink; + p != &Device->AdapterAddressDatabase; + p = p->Flink ) { + + AdapterAddress = CONTAINING_RECORD( p, ADAPTER_ADDRESS, Linkage ); + if ( RtlEqualMemory( + NetbiosName, + AdapterAddress->NetbiosName, + NB_NETBIOS_NAME_SIZE ) ) { + break; + } + } + + + if ( !LockHeld ) { + NB_FREE_LOCK( &Device->Lock, LockHandle ); + } + + if ( p == &Device->AdapterAddressDatabase ) { + return NULL; + } else { + return AdapterAddress; + } + +} /* NbiFindAdapterAddress */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/autodial.c b/private/ntos/tdi/isnp/nb/autodial.c new file mode 100644 index 000000000..ec56e2351 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/autodial.c @@ -0,0 +1,526 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + NT specific routines for interfacing with the + RAS AutoDial driver (rasacd.sys). + +Author: + + Anthony Discolo (adiscolo) Aug 30, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + adiscolo 08-30-95 created + +Notes: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include +#include + +// +// Global variables +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbi '; + + + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PDEVICE pDevice = pArgs[0]; + PCONNECTION pConnection = pArgs[1]; + PREQUEST pRequest = pArgs[2]; + CTELockHandle ConnectionLH, DeviceLH; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // +#if notdef // DBG + DbgPrint("NbiRetryTdiConnect: fSuccess=%d, pConnection=0x%x\n", fSuccess, pConnection); +#endif + + status = NbiVerifyConnection(pConnection); + if (!NT_SUCCESS(status)) { + DbgPrint( + "NbiRetryTdiConnect: NbiVerifyConnection failed on connection 0x%x (status=0x%x)\n", + pConnection, + status); + return; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&pConnection->Lock, &ConnectionLH); + NB_GET_LOCK (&pDevice->Lock, &DeviceLH); + +#if notdef // DBG + DbgPrint( + "NbiRetryTdiConnect: AddressFile=0x%x, DisassociatePending=0x%x, ClosePending=0x%x\n", + pConnection->AddressFile, + pConnection->DisassociatePending, + pConnection->ClosePending); +#endif + + if ((pConnection->AddressFile != NULL) && + (pConnection->AddressFile != (PVOID)-1) && + (pConnection->DisassociatePending == NULL) && + (pConnection->ClosePending == NULL)) + { + NbiReferenceConnectionLock(pConnection, CREF_CONNECT); + // + // Clear the AUTOCONNECTING flag since we + // done with the automatic connection attempt. + // Set the AUTOCONNECTED flag to prevent us + // from attempting an automatic connection + // for this connection again. + // + pConnection->Flags &= ~CONNECTION_FLAGS_AUTOCONNECTING; + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTED; + + pConnection->State = CONNECTION_STATE_CONNECTING; + pConnection->Retries = pDevice->ConnectionCount; + status = NbiTdiConnectFindName( + pDevice, + pRequest, + pConnection, + CancelLH, + ConnectionLH, + DeviceLH, + &bLockFreed); + } + else { + DbgPrint("NbiRetryTdiConnect: Connect on invalid connection 0x%x\n", pConnection); + + pConnection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&pDevice->Lock, DeviceLH); + status = STATUS_INVALID_CONNECTION; + } + if (!bLockFreed) { + NB_FREE_LOCK (&pConnection->Lock, ConnectionLH); + NB_FREE_CANCEL_LOCK(CancelLH); + } + // + // Complete the irp if necessary. + // + if (status != STATUS_PENDING) { + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = status; + + NbiCompleteRequest(pRequest); + NbiFreeRequest(pDevice, pRequest); + } + NbiDereferenceConnection(pConnection, CREF_VERIFY); +} /* NbiRetryTdiConnect */ + + + +BOOLEAN +NbiCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ +#if notdef // DBG + DbgPrint("NbiCancelAutodialRequest: pArg=0x%x\n", pArg); +#endif + if (nArgs != 2) + return FALSE; + + return (pArgs[1] == pArg); +} // NbiCancelAutoDialRequest + + + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDevice: a pointer to the device object for this driver + + pRequest: a pointer to the irp to be cancelled + + pConnection: a pointer to the connnection to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint( + "NbiCancelTdiConnect: pIrp=0x%x, RemoteName=%-15.15s, pConnection=0x%x\n", + pRequest, + addr.cNetbios, + pConnection); +#endif + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbiCancelAutoDialRequest, + pConnection); +} // NbiCancelTdiConnect + + + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pDevice - a pointer to the DEVICE structure for this connection + + pConnection - a pointer to the CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pRequest - a pointer to the request irp + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[3]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic connection + // on this connection, don't try it again. + // + if (pConnection->Flags & CONNECTION_FLAGS_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint("NbiAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbiRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pDevice; + pArgs[1] = pConnection; + pArgs[2] = pRequest; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 3, + pArgs); + if (bSuccess) { + // + // Set the AUTOCONNECTING flag so we know + // to also cancel the connection in the + // automatic connection driver if this + // request gets canceled. + // + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTING; + } +} // NbiAttemptAutoDial + + + +VOID +NbiNoteNewConnection( + IN PCONNECTION pConnection + ) +{ + NTSTATUS status; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG i; + TDI_ADDRESS_IPX tdiIpxAddress; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); + // + // Determine the mac address of the adapter + // over which the connection has been made. + // + status = (pConnection->Device->Bind.QueryHandler)( + IPX_QUERY_IPX_ADDRESS, +#if defined(_PNP_POWER) + &pConnection->LocalTarget.NicHandle, +#else + pConnection->LocalTarget.NicId, +#endif _PNP_POWER + &tdiIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + if (status != STATUS_SUCCESS) { +#if notdef // DBG + DbgPrint("NbiNoteNewConnection: QueryHandler(IPX_QUERY_IPX_ADDRESS) failed (status=0x%x)\n", status); + return; +#endif + } + // + // Copy the source mac address to identify + // the adapter. + // + adapter.fType = ACD_ADAPTER_MAC; + for (i = 0; i < 6; i++) + adapter.cMac[i] = tdiIpxAddress.NodeAddress[i]; +#if notdef // DBG + DbgPrint( + "NbiNoteNewConnection: address=%-15.15s, remote mac=%02x:%02x:%02x:%02x:%02x:%02x\n", + addr.cNetbios, + adapter.cMac[0], + adapter.cMac[1], + adapter.cMac[2], + adapter.cMac[3], + adapter.cMac[4], + adapter.cMac[5]); +#endif + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbiNoteNewConnection + + + +VOID +NbiAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdBind + + + +VOID +NbiAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/tdi/isnp/nb/bind.c b/private/ntos/tdi/isnp/nb/bind.c new file mode 100644 index 000000000..7dc20d0d5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/bind.c @@ -0,0 +1,593 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiBind) +#endif + +#if defined(_PNP_POWER) +// +// local functions. +// +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine binds the Netbios module of ISN to the IPX + module, which provides the NDIS binding services. + +Arguments: + + Device - Pointer to the Netbios device. + + Config - Pointer to the configuration information. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; +/* union { + IPX_INTERNAL_BIND_INPUT Input; + IPX_INTERNAL_BIND_OUTPUT Output; + } Bind; +*/ + InitializeObjectAttributes( + &ObjectAttributes, + &Config->BindName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwCreateFile( + &Device->BindHandle, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(Status)) { + + NB_DEBUG (BIND, ("Could not open IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + return Status; + } + + // + // Fill in our bind data. + // + +#if defined(_PNP_POWER) + Device->BindInput.Version = ISN_VERSION; +#else + Device->BindInput.Version = 1; +#endif _PNP_POWER + Device->BindInput.Identifier = IDENTIFIER_NB; + Device->BindInput.BroadcastEnable = TRUE; + Device->BindInput.LookaheadRequired = 192; + Device->BindInput.ProtocolOptions = 0; + Device->BindInput.ReceiveHandler = NbiReceive; + Device->BindInput.ReceiveCompleteHandler = NbiReceiveComplete; + Device->BindInput.StatusHandler = NbiStatus; + Device->BindInput.SendCompleteHandler = NbiSendComplete; + Device->BindInput.TransferDataCompleteHandler = NbiTransferDataComplete; + Device->BindInput.FindRouteCompleteHandler = NbiFindRouteComplete; + Device->BindInput.LineUpHandler = NbiLineUp; + Device->BindInput.LineDownHandler = NbiLineDown; + Device->BindInput.ScheduleRouteHandler = NULL; +#if defined(_PNP_POWER) + Device->BindInput.PnPHandler = NbiPnPNotification; +#endif _PNP_POWER + + + Status = ZwDeviceIoControlFile( + Device->BindHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + &Device->BindInput, // Input Buffer + sizeof(Device->BindInput), // Input Buffer Length + &Device->Bind, // OutputBuffer + sizeof(Device->Bind)); // OutputBufferLength + + // + // We open synchronous, so this shouldn't happen. + // + + CTEAssert (Status != STATUS_PENDING); + + // + // Save the bind data. + // + + if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (BIND, ("Successfully bound to IPX (%ws)\n", + Config->BindName.Buffer)); +// RtlCopyMemory (&Device->Bind, &Bind.Output, sizeof(IPX_INTERNAL_BIND_OUTPUT)); + +#if !defined(_PNP_POWER) + RtlZeroMemory (Device->ReservedNetbiosName, 16); + RtlCopyMemory (&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAXIMUM_NIC_ID, + (USHORT)0, + &Device->MaximumNicId, + sizeof(Device->MaximumNicId), + NULL); + CTEAssert (Status == STATUS_SUCCESS); +#endif !_PNP_POWER + + } else { + + NB_DEBUG (BIND, ("Could not bind to IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_BINDING_FAILED, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + ZwClose(Device->BindHandle); + } + + return Status; + +} /* NbiBind */ + + +VOID +NbiUnbind( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This function closes the binding between the Netbios over + IPX module and the IPX module previously established by + NbiBind. + +Arguments: + + Device - The netbios device object. + +Return Value: + + None. + +--*/ + +{ + ZwClose (Device->BindHandle); + +} /* NbiUnbind */ + + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +/*++ + +Routine Description: + + This function receives a status indication from IPX, + corresponding to a status indication from an underlying + NDIS driver. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + GeneralStatus - The general status code. + + StatusBuffer - The status buffer. + + StatusBufferLength - The length of the status buffer. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiStatus */ + + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + + +/*++ + +Routine Description: + + This function receives line up indications from IPX, + indicating that the specified adapter is now up with + the characteristics shown. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + LineInfo - Information about the adapter's medium. + + DeviceType - The type of the adapter. + + ConfigurationData - IPX-specific configuration data. + +Return Value: + + None. + +--*/ + +{ + PIPXCP_CONFIGURATION Configuration = (PIPXCP_CONFIGURATION)ConfigurationData; + + // + // Update queries have NULL as the ConfigurationData. These + // only indicate changes in LineInfo. BUGBUG Ignore these + // for the moment. + // + + if (Configuration == NULL) { + return; + } + +#if !defined(_PNP_POWER) + // + // Since Netbios outgoing queries only go out on network 1, + // we ignore this (BUGBUG for the moment) unless that is + // the NIC it is on. + // + + if (NicId == 1) { + + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNetwork, Configuration->Network, 4); + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNode, Configuration->LocalNode, 6); + + } +#endif !_PNP_POWER +} /* NbiLineUp */ + + +VOID +NbiLineDown( + IN USHORT NicId + ) + + +/*++ + +Routine Description: + + This function receives line down indications from IPX, + indicating that the specified adapter is no longer + up. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiLineDown */ + +#if defined(_PNP_POWER) + +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX. + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + UCHAR PrevReservedName[NB_NETBIOS_NAME_SIZE]; + UNICODE_STRING UnicodeDeviceName; + + + NB_DEBUG2( DEVICE, ("Received a pnp notification, opcode %d\n",OpCode )); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + BOOLEAN ReallocReceiveBuffers = FALSE; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + if ( PnPInfo->NewReservedAddress ) { + + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + +// RtlZeroMemory(Device->ReservedNetbiosName, NB_NETBIOS_NAME_SIZE); +// RtlCopyMemory(&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + } + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->State != DEVICE_STATE_OPEN ); + + + // + // we must do this while we still have the device lock. + // + if ( !Device->LongTimerRunning ) { + Device->LongTimerRunning = TRUE; + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } + + Device->State = DEVICE_STATE_OPEN; + + CTEAssert( !Device->MaximumNicId ); + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + Device->Bind.LineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumSendSize; + ReallocReceiveBuffers = TRUE; + } else { + if ( PnPInfo->LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + ReallocReceiveBuffers = TRUE; + } + // + // MaxSendSize could become smaller. + // + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + } + + Device->MaximumNicId++; + + + // + // + NbiCreateAdapterAddress( PnPInfo->NodeAddress ); + + // + // And finally remove all the failed cache entries since we might + // find those routes using this new adapter + // + FlushFailedNetbiosCacheEntries(Device->NameCache); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + if ( ReallocReceiveBuffers ) { + PWORK_QUEUE_ITEM WorkItem; + + WorkItem = NbiAllocateMemory( sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buffer work item"); + + if ( WorkItem ) { + ExInitializeWorkItem( WorkItem, NbiReAllocateReceiveBufferPool, (PVOID) WorkItem ); + ExQueueWorkItem( WorkItem, DelayedWorkQueue ); + } else { + NB_DEBUG( DEVICE, ("Cannt schdule work item to realloc receive buffer pool\n")); + } + } + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->DeviceName; + UnicodeDeviceName.MaximumLength = Device->DeviceNameLength; + UnicodeDeviceName.Length = Device->DeviceNameLength - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to register nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + CTEAssert( Device->MaximumNicId ); + Device->MaximumNicId--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->State = DEVICE_STATE_LOADED; + Device->MaximumNicId = 0; + + } + + + // + // MaximumSendSize could change if the card with the smallest send size just + // got removed. MaximumPacketSize could only become smaller and we ignore that + // since we dont need to(want to) realloc ReceiveBuffers. + // + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + RemoveInvalidRoutesFromNetbiosCacheTable( Device->NameCache, &PnPInfo->NicHandle ); + + NbiDestroyAdapterAddress( NULL, PnPInfo->NodeAddress ); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + +/* // + // Now mark the previous reserved name in conflict if it has + // been registered by any of our client + // + if ( Address = NbiFindAddress( Device, PrevReservedName ) ) { + NB_GET_LOCK( &Address->Lock, &LockHandle ); + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + NB_FREE_LOCK( &Address->Lock, LockHandle ); + + NB_DEBUG( ADDRESS, ("Reserved Address %lx<%.16s> is marked CONFLICT\n",Address,Address->NetbiosAddress.NetbiosName)); + // + // nbifindaddress added a reference, so deref + // + NbiDereferenceAddress( Address, AREF_FIND ); + } +*/ + + // + // inform tdi clients about the device deletion + // + if ( PnPInfo->FirstORLastDevice ) { + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to Deregister nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + PADDRESS Address; + BOOLEAN ReservedNameClosing = FALSE; + + CTEAssert( PnPInfo->NewReservedAddress ); + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/cache.c b/private/ntos/tdi/isnp/nb/cache.c new file mode 100644 index 000000000..cbd27ad67 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/cache.c @@ -0,0 +1,2746 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module contains the name cache routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 20-December-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include +#include + +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ); + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); +#endif // RASAUTODIAL + +// +// BUGBUG: We should change to monitor add name packets better, +// so if we get an add for a different place we attempt to determine +// if it is real or bogus and update if possible. +// + + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +) + +/*++ + +Routine Description: + + This routine looks up a particular remote name in the + Netbios name cache. If it cannot find it, a find name + request is queued up. + + THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The netbios device. + + Type - Defines the type. The effect this has is: + FindNameConnect - On connects we will ignore an existing + cache entry if it got no response before. + FindNameNetbiosFindName - For these we ignore an existing + cache entry if it is for a group name -- this is + because the find name wants the address of every + machine, not just the network list. + FindNameOther - Normal handling is done. + + RemoteName - The name to be discovered -- will be NULL if it + is the broadcast address. + + CacheName - Returns the cache entry that was discovered. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE FoundCacheName; + PNB_SEND_RESERVED Reserved; + PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName + + // + // First scan the netbios name cache to see if we know + // about this remote. + // + + if (RemoteName) { + RealRemoteName = RemoteName; + } else { + RealRemoteName = NetbiosBroadcastName; + } + + if ( FindInNetbiosCacheTable ( Device->NameCache, + RealRemoteName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + // + // If this is a netbios find name, we only can use unique + // names in the cache; for the group ones we need to requery + // because the cache only lists networks, not individual machines. + // For connect requests, if we find an empty cache entry we + // remove it and requery. + // + + if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) { + + if (FoundCacheName->NetworksUsed > 0) { + + *CacheName = FoundCacheName; + NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_SUCCESS; + + } else { + + if (Type != FindNameConnect) { + + if (FoundCacheName->FailedOnDownWan) { + NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_DEVICE_DOES_NOT_EXIST; + } else { + NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + // + // This is a connect and the current cache entry + // has zero names; delete it. + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + CTEAssert (FoundCacheName->ReferenceCount == 1); + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + } + + } + } + } + } + + + // + // There was no suitable cache entry for this network, first see + // if there is one pending. + // + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // For this purpose we ignore a packet if a route + // has been found and it was for a unique name. This + // is because the cache information has already been + // inserted for this name. Otherwise if the name has + // since been deleted from the cache, the request + // that is looking for this name will starve because + // FindNameTimeout will just destroy the packet. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + continue; + } + + if (RtlEqualMemory( + Reserved->u.SR_FN.NetbiosName, + RealRemoteName, 16)) { + + NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "")); + + // + // There is already one pending. If it is for a group + // name and this is a netbios find name, we make sure + // the retry count is such that at least one more + // query will be sent, so the netbios find name + // buffer can be filled with the responses from this. + // + + if ((Type == FindNameNetbiosFindName) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) && + (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) { + + --Reserved->u.SR_FN.RetryCount; + } + + return STATUS_PENDING; + } + } + + s = NbiPopSendPacket(Device, TRUE); + + if (s == NULL) { + NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = FALSE; + Reserved->Type = SEND_TYPE_FIND_NAME; + RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16); + Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE + Reserved->u.SR_FN.RetryCount = 0; + Reserved->u.SR_FN.NewCache = NULL; + Reserved->u.SR_FN.SendTime = Device->FindNameTime; +#if !defined(_PNP_POWER) + Reserved->u.SR_FN.CurrentNicId = 1; + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAX_TYPE_20_NIC_ID, + (USHORT)0, + &Reserved->u.SR_FN.MaximumNicId, + sizeof(USHORT), + NULL); + + if (Reserved->u.SR_FN.MaximumNicId == 0) { + Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one + } +#endif !_PNP_POWER + NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n", + Reserved, RemoteName ? RemoteName : "")); + + + InsertHeadList( + &Device->WaitingFindNames, + &Reserved->WaitLinkage); + + ++Device->FindNamePacketCount; + + if (!Device->FindNameTimerActive) { + + Device->FindNameTimerActive = TRUE; + NbiReferenceDevice (Device, DREF_FN_TIMER); + + CTEStartTimer( + &Device->FindNameTimer, + 1, // 1 ms, i.e. expire immediately + FindNameTimeout, + (PVOID)Device); + } + + NbiReferenceDevice (Device, DREF_FIND_NAME); + + return STATUS_PENDING; + +} /* CacheFindName */ + + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the find name timer expires. + It is called every FIND_NAME_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, q; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + PNETBIOS_CACHE FoundCacheName; + NDIS_STATUS NdisStatus; +#if !defined(_PNP_POWER) + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif !_PNP_POWER + NB_DEFINE_LOCK_HANDLE (LockHandle) + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->FindNameTime; + + if (Device->FindNamePacketCount == 0) { + + NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n")); + + Device->FindNameTimerActive = FALSE; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + NbiDereferenceDevice (Device, DREF_FN_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // +#if defined(_PNP_POWER) + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // + + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If this is the first retry, we need to initialize the packet + // + if ( Reserved->u.SR_FN.RetryCount == 1 ) { + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + + } + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + + break; + + } +#else + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + // + // Save this now, then change it if needed. + // + + BroadcastTarget.NicId = Reserved->u.SR_FN.CurrentNicId; + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + } + + + // + // Increment the current NIC ID for next time. + // + + if (Reserved->u.SR_FN.CurrentNicId >= Reserved->u.SR_FN.MaximumNicId) { + Reserved->u.SR_FN.CurrentNicId = 1; + } else { + ++Reserved->u.SR_FN.CurrentNicId; + } + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // If we are going to wrap around the maximum NIC ID + // after sending this packet we wait the full configured + // amount, otherwise we wait almost the minimum (but still + // insert ourselves at the back of the queue to be fair). + // + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + } else { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + 2); + } + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx to %d\n", Reserved, BroadcastTarget.NicId)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + if (NdisStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + + // + // This send was done on a down wan line. To avoid + // extensive delays sending find names on systems + // with a lot of wan lines, we loop around immediately + // and do the next send. We put this packet back on + // the head of the list and change its SendTime so + // it will be resent immediately. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + NB_DEBUG2 (CACHE, ("FindNameTimeout resending %lx, wan send failed\n", Reserved)); + + RemoveEntryList (&Reserved->WaitLinkage); + InsertHeadList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + Reserved->u.SR_FN.SendTime = Device->FindNameTime; + + continue; + + } else { + + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } + + + break; + + } +#endif _PNP_POWER + + // + // Since we did something this time, we restart the timer. + // + + CTEStartTimer( + &Device->FindNameTimer, + FIND_NAME_GRANULARITY, + FindNameTimeout, + (PVOID)Device); + +} /* FindNameTimeout */ + + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams and connects + that were waiting for a route to be discovered to a + given Netbios NAME. THIS ROUTINE IS CALLED WITH + DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + RemoteName - The netbios name that was being searched for. + + Result - Indicates if the name was found, or not found due + to no response or wan lines being down. + + CacheName - If Result is NetbiosNameFound, the cache entry for this name. + This entry has been referenced and this routine will deref it. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY ConnectList; + LIST_ENTRY AdapterStatusList; + LIST_ENTRY NetbiosFindNameList; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + PLIST_ENTRY p; + PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + InitializeListHead (&DatagramList); + InitializeListHead (&ConnectList); + InitializeListHead (&AdapterStatusList); + InitializeListHead (&NetbiosFindNameList); + + // + // Put all connect requests on ConnectList. They will + // be continued or failed later. + // + + p = Device->WaitingConnects.Flink; + + while (p != &Device->WaitingConnects) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + p = p->Flink; + + if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) { + + RemoveEntryList (REQUEST_LINKAGE(ConnectRequest)); + InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest)); + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + } + + } + + + // + // Put all the datagrams on Datagram list. They will be + // sent or failed later. + // + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + // + // Check differently based on whether we were looking for + // the broadcast address or not. + // + + if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) { + if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) { + continue; + } + } else { + + if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) { + continue; + } + } + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&DatagramList, &Reserved->WaitLinkage); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the adapter status requests on AdapterStatus + // list. They will be sent or failed later. + // + + p = Device->WaitingAdapterStatus.Flink; + + while (p != &Device->WaitingAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(AdapterStatusRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the netbios find name requests on NetbiosFindName + // list. They will be completed later. + // + + p = Device->WaitingNetbiosFindName.Flink; + + while (p != &Device->WaitingNetbiosFindName) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest)); + InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest)); + + } + + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Now that the lock is free, process all the packets on + // the various lists. + // + + for (p = ConnectList.Flink; p != &ConnectList; ) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection)); + + // + // Continue with the connection sequence. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!ConnectRequest->Cancel) { + + IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 ); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection)); + + goto AbortConnect; + + // + // Jumps down into the else below. + // + + } + + } else { + BOOLEAN bAutodialAttempt = FALSE; + + NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection)); +AbortConnect: + + ASSERT (Connection->ConnectRequest == ConnectRequest); + +#ifdef RASAUTODIAL + if (fAcdLoadedG) { + CTELockHandle adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + CTEGetLock(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbiAttemptAutoDial( + Device, + Connection, + 0, + NbiRetryTdiConnect, + ConnectRequest)) + { + NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + bAutodialAttempt = TRUE; + } + } +#endif // RASAUTODIAL + + if (!bAutodialAttempt) { + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH; + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + } + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + } else { + + // BUGBUG What happens to the IRP? Who completes it? + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + + } + + + for (p = DatagramList.Flink; p != &DatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile)); + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + + // + // CacheName was referenced above. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest)); + } + + NbiTransmitDatagram (Reserved); + + } else { + + // + // BETABUGBUG: Should we send it once as a broadcast + // on net 0, just in case?? + // + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // If the failure was due to a down wan line indicate + // that, otherwise return success (so the browser won't + // confuse this with a down wan line). + // + + if (Result == NetbiosNameNotFoundWanDown) { + REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST; + REQUEST_INFORMATION(DatagramRequest) = 0; + } else { + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + } + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + } + + } + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest)); + + // + // Continue with the AdapterStatus sequence. We put + // it in ActiveAdapterStatus, it will either get + // completed when a response is received or timed + // out by the long timeout. + // + + REQUEST_STATUS(AdapterStatusRequest) = (NTSTATUS)CacheName; + + // + // CacheName was referenced above. + // + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + + NB_INSERT_TAIL_LIST( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (AdapterStatusRequest), + &Device->Lock); + + NbiSendStatusQuery (AdapterStatusRequest); + + } else { + + NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest)); + + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + } + + + for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // + // In fact there is not much difference between success or + // failure, since in the successful case the information + // will already have been written to the buffer. Just + // complete the request with the appropriate status, + // which will already be stored in the request. + // + + if (Result == NetbiosNameFound) { + + if (CacheName->Unique) { + + NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } else { + + NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + } else { + + CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT); + NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + // + // This sets REQUEST_INFORMATION(Request) to the correct value. + // + + NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest); + + NbiCompleteRequest(NetbiosFindNameRequest); + NbiFreeRequest (Device, NetbiosFindNameRequest); + + NbiDereferenceDevice (Device, DREF_NB_FIND_NAME); + + } + + + // + // We referenced this temporarily so we could use it in here, + // deref and check if we need to delete it. + // + + if (Result == NetbiosNameFound) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free in CacheHandlePending"); + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + + } + +} /* CacheHandlePending */ + + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_NAME_RECOGNIZED frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + PNETBIOS_CACHE NameCache; + PREQUEST NetbiosFindNameRequest; + PNB_SEND_RESERVED Reserved; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteNetbiosAddress; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + +#if 0 + // + // BETABUGBUG: We should handle responses from network 0 + // differently -- if they are for a group name, we should + // keep them around but only until we get a non-zero + // response from the same card. + // + + if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) { + return; + } +#endif + + + // + // We need to scan our queue of pending find name packets + // to see if someone is waiting for this name. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // Find names which have already found unique names are + // "dead", waiting for FindNameTimeout to remove them, + // and should be ignored when scanning the list. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + continue; + } + + if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) { + break; + } + } + + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Scan for any netbios find name requests on the queue, and + // inform them about this remote. We need to do this on every + // response because group names need every computer recorded, + // but the normal cache only includes one entry per network. + // + + for (p = Device->WaitingNetbiosFindName.Flink; + p != &Device->WaitingNetbiosFindName; + p = p->Flink) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + Connectionless->NameFrame.Name, + RemoteNetbiosAddress->NetbiosName, + 16)) { + continue; + } + + // + // This will update the request status if needed. + // + + NbiUpdateNetbiosFindName( + NetbiosFindNameRequest, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + (TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork, + (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0)); + + } + + + // + // See what is up with this pending find name packet. + // + + if (Reserved->u.SR_FN.NewCache == NULL) { + + // + // This is the first response we have received, so we + // allocate the initial entry with room for a single + // entry. + // + + NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (NameCache == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n", + NameCache, Reserved->u.SR_FN.NetbiosName, + *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork))); + + RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16); + NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); + NameCache->ReferenceCount = 1; + RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + NameCache->NetworksAllocated = 1; + NameCache->NetworksUsed = 1; + NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + + if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) { + + NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup); + NameCache->Unique = FALSE; + + } else { + + NB_SET_SR_FN_STATUS( + Reserved, + NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup); + + } + + Reserved->u.SR_FN.NewCache = NameCache; + + // + // If this packet was not routed to us and is for a group name, + // rather than use whatever local target it happened to come + // from we set it up so that it is broadcast on that net. + // + + if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) { +#if defined(_PNP_POWER) + NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); + RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[0].LocalTarget = *RemoteAddress; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // Complete pending requests now, since it is a unique + // name we have all the information we will get. + // + + NameCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + NameCache); + + // + // Reference it since CacheHandlePending uses it + // with the lock released. CacheHandlePending + // will dereference it. + // + + ++NameCache->ReferenceCount; + + // + // This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + NameCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // We already have a response to this frame. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // BUGBUG: Should we check that the response is also + // unique? Not much to do since I don't know of an + // equivalent to the netbeui NAME_IN_CONFLICT. + // + + } else { + + // + // This is a group name. + // + + if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) { + + // + // Update our information about this network if needed. + // This may free the existing cache and allocate a new one. + // + + Reserved->u.SR_FN.NewCache = + CacheUpdateNameCache( + Reserved->u.SR_FN.NewCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *) + Connectionless->IpxHeader.SourceNetwork, + FALSE); + + } else { + + // + // BUGBUG: This respondent thinks it is a unique name + // but we think it is group, should we do something? + // + + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessNameRecognized */ + + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ) + +/*++ + +Routine Description: + + This routine is called to update a netbios cache entry + with a new network, if it is does not already contain + information about the network. It is called when a frame + is received advertising the appropriate cache entry, which + is either a group name or the broadcast name. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH IT HELD. + +Arguments: + + NameCache - The name cache entry to update. + + RemoteAddress - The remote address on which a frame was received. + + IpxAddress - The source IPX address of the frame. + + ModifyQueue - TRUE if we should update the queue which this + cache entry is in, if we reallocate it. + +Return Value: + + The netbios cache entry, either the original or a reallocated one. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT NewNetworks; + PNETBIOS_CACHE NewNameCache; + PLIST_ENTRY OldPrevious; + UINT i; + + // + // See if we already know about this network. + // + + for (i = 0; i < NameCache->NetworksUsed; i++) { + if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) { + return NameCache; + } + } + + // + // We need to add information about this network + // to the name cache entry. If we have to allocate + // a new one we do that. + // + + NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n", + SourceAddress->NetworkAddress, + NameCache->NetbiosName)); + + if (NameCache->NetworksUsed == NameCache->NetworksAllocated) { + + // + // We double the number of entries allocated until + // we hit 16, then add 8 at a time. + // + + if (NameCache->NetworksAllocated < 16) { + NewNetworks = NameCache->NetworksAllocated * 2; + } else { + NewNetworks = NameCache->NetworksAllocated + 8; + } + + + NewNameCache = NbiAllocateMemory( + sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge cache entry"); + + if (NewNameCache == NULL) { + return NameCache; + } + + NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n", + NameCache, NewNameCache, NameCache->NetbiosName)); + + // + // Copy the new current data to the new one. + // + + RtlCopyMemory( + NewNameCache, + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK))); + + NewNameCache->NetworksAllocated = NewNetworks; + NewNameCache->ReferenceCount = 1; + + if (ModifyQueue) { + + // + // Insert at the same place as the old one. The time + // stamp is the same as the old one. + // + + + ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache ); + + } + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + NameCache = NewNameCache; + + } + + NameCache->Networks[NameCache->NetworksUsed].Network = + SourceAddress->NetworkAddress; + + // + // If this packet was not routed to us, then store the local + // target for a correct broadcast. + // + + if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) { +#if defined(_PNP_POWER) + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress; + } + + ++NameCache->NetworksUsed; + + return NameCache; + +} /* CacheUpdateNameCache */ + + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ) + +/*++ + +Routine Description: + + This routine is called when an add name frame is received. + If it is for a group name it checks if our cache entry for + that group name needs to be updated to include a new network; + for all frames it checks if our broadcast cache entry needs + to be updated to include a new network. + +Arguments: + + RemoteAddress - The address the frame was received from. + + Connectionless - The header of the received add name. + + LocalFrame - TRUE if the frame was sent locally. + +Return Value: + + None. + +--*/ + +{ + PUCHAR NetbiosName; + PNETBIOS_CACHE NameCache; + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + // + // First look up the broadcast name. + // + // BUGBUG: We should cache a pointer to the cache name + // for the broadcast entry, if there is one. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if (!LocalFrame) { + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosBroadcastName, + &NameCache ) == STATUS_SUCCESS ) { + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + } + + } + + + // + // Now see if our database needs to be updated based on this. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Connectionless->NameFrame.Name, + &NameCache ) == STATUS_SUCCESS ) { + + + if (!NameCache->Unique) { + + if (!LocalFrame) { + + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + + } + + } else { + + // + // To be safe, delete any unique names we get add + // names for (we will requery next time we need it). + // BUGBUG: Update the database instead -- but then + // we may not get the best route?? + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache ); + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + +} /* CacheUpdateFromAddName */ + + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_DELETE_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PUCHAR NetbiosName; + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // We want to update our netbios cache to reflect the + // fact that this name is no longer valid. + // + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosName, + &CacheName ) == STATUS_SUCCESS ) { + + // + // We don't track group names since we don't know if + // this is the last person that owns it. We also drop + // the frame if does not come from the person we think + // owns this name. + // + + if ((!CacheName->Unique) || + (CacheName->NetworksUsed == 0) || + (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName)); + + }else { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // We have a cache entry, take it out of the list. If no + // one else is using it, delete it; if not, they will delete + // it when they are done. + // + + + RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName); + + if (--CacheName->ReferenceCount == 0) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessDeleteName */ + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry in the hash table + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + USHORT HashIndex; + + // + // Keep a threshold of how many entries do we keep in the table. + // If it crosses the threshold, just remove the oldest entry + // + if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) { + PNETBIOS_CACHE OldestCacheEntry = NULL; + PNETBIOS_CACHE NextEntry; + PLIST_ENTRY p; + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) { + NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + if ( OldestCacheEntry ) { + if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) { + OldestCacheEntry = NextEntry; + } + } else { + OldestCacheEntry = NextEntry; + } + } + } + + CTEAssert( OldestCacheEntry ); + + NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry)); + RemoveEntryList (&OldestCacheEntry->Linkage); + CacheTable->CurrentEntries--; + + if (--OldestCacheEntry->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry)); + + NbiFreeMemory( + OldestCacheEntry, + sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } + HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage ); + CacheTable->CurrentEntries++; +} /* InsertInNetbiosCacheTable */ + + +__inline +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry at the same place where + the old entry was. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY OldPrevious; + + OldPrevious = OldEntry->Linkage.Blink; + RemoveEntryList (&OldEntry->Linkage); + InsertHeadList (OldPrevious, &NewEntry->Linkage); +} /* ReinsertInNetbiosCacheTable */ + +__inline +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine removes an entry from the cache table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + RemoveEntryList( &CacheEntry->Linkage ); + CacheTable->CurrentEntries--; +} /* RemoveFromNetbiosCacheTable */ + + + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ) + +/*++ + +Routine Description: + + This routine removes all the old entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + AgeLimit - All the entries older than AgeLimit will be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // see if any entries have been around for more than agelimit + // + + if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) { + + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } else { + + break; + + } + } // for loop + } // for loop +} /* FlushOldFromNetbiosCacheTable */ + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all the failed entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // flush all the failed cache entries. + // We do this when a new adapter appears, and there's a possiblity that + // the failed entries might succeed now on the new adapter. + // + + if (CacheName->NetworksUsed == 0) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + CTEAssert( CacheName->ReferenceCount == 1 ); + CTEAssert( CacheName->NetworksAllocated == 1 ); + + NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE), + MEMORY_CACHE, + "Aged out"); + + } + } // for loop + } // for loop +} /* FlushFailedNetbiosCacheEntries */ + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ) + +/*++ + +Routine Description: + + This routine removes all invalid route entries from the hash table. + Routes become invalid when the binding is deleted in Ipx due to PnP + event. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + InvalidRouteNicId - NicId of the invalid routes. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + USHORT HashIndex; + PDEVICE Device = NbiDevice; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + + for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) { + for (p = Device->NameCache->Bucket[ HashIndex ].Flink; + p != &Device->NameCache->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Flink; + + + // + // Remove each of those routes which is using this NicId. + // if no routes left, then flush the cache entry also. + // ( unique names have only one route anyways ) + // + for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) { + if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) { + CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE))); + for ( j = i+1; j < CacheName->NetworksUsed; j++ ) { + CacheName->Networks[j-1] = CacheName->Networks[j]; + } + NetworksRemoved++; + } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) { + CacheName->Networks[i].LocalTarget.NicHandle.NicId--; + } + } + CTEAssert( NetworksRemoved <= CacheName->NetworksUsed ); + if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId )); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + } + } // for loop + } // for loop +} /* RemoveInvalidRoutesFromNetbiosCacheTable */ + + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ) + +/*++ + +Routine Description: + + This routine finds a netbios name in the Hash Table and returns + the corresponding cache entry. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Pointer to the netbios cache entry if found. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_UNSUCCESSFUL - otherwise. + +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE FoundCacheName; + + + HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink; + p != &CacheTable->Bucket[ HashIndex ]; + p = p->Flink) { + + FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + // + // See if this entry is for the same name we are looking for. + + if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) { + *CacheEntry = FoundCacheName; + return STATUS_SUCCESS; + } + } + + return STATUS_UNSUCCESSFUL; +} /* FindInNetbiosCacheTable */ + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ) + +/*++ + +Routine Description: + + This routine creates a new hash table for netbios cache + and initializes it. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + NewTable - The pointer of the table to be created. + + MaxHashIndex - Number of buckets in the hash table. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory. + +--*/ + +{ + USHORT i; + + *NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) , + MEMORY_CACHE, "Cache Table"); + + if ( *NewTable ) { + for ( i = 0; i < MaxHashIndex; i++ ) { + InitializeListHead(& (*NewTable)->Bucket[i] ); + } + + (*NewTable)->MaxHashIndex = MaxHashIndex; + (*NewTable)->CurrentEntries = 0; + return STATUS_SUCCESS; + } + else { + NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") ); + return STATUS_INSUFFICIENT_RESOURCES; + } + +} /* CreateNetbiosCacheTable */ + + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all entries from the hash table. + and free up the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) { + + p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] )); + CacheTable->CurrentEntries--; + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free entries"); + + } + } // for loop + + CTEAssert( CacheTable->CurrentEntries == 0 ); + + NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) , + MEMORY_CACHE, "Free Cache Table"); + +} /* DestroyNetbiosCacheTable */ + + diff --git a/private/ntos/tdi/isnp/nb/config.c b/private/ntos/tdi/isnp/nb/config.c new file mode 100644 index 000000000..8689c5d20 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.c @@ -0,0 +1,661 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN Netbios module. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiGetConfiguration) +#pragma alloc_text(INIT,NbiFreeConfiguration) +#pragma alloc_text(INIT,NbiGetConfigValue) +#pragma alloc_text(INIT,NbiAddBind) +#pragma alloc_text(INIT,NbiAddExport) +#pragma alloc_text(INIT,NbiReadLinkageInformation) +#endif + + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of Netbios' node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG One = 1; + ULONG Two = 2; + ULONG Three = 3; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG FortyEight = 48; + ULONG Sixty = 60; + ULONG TwoFifty = 250; + ULONG FiveHundred = 500; + ULONG MaxMTU = 0xffffffff; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"AckDelayTime", &TwoFifty } , // milliseconds + { L"AckWindow", &Two } , + { L"AckWindowThreshold", &FiveHundred } , // milliseconds + { L"EnablePiggyBackAck", &One } , + { L"Extensions", &One } , + { L"RcvWindowMax", &Four } , + { L"BroadcastCount", &Three } , + { L"BroadcastTimeout", &One } , // half-seconds + { L"ConnectionCount", &Five } , + { L"ConnectionTimeout", &Two } , // half-seconds + { L"InitPackets", &Eight } , + { L"MaxPackets", &FortyEight } , + { L"InitialRetransmissionTime", &FiveHundred } , // milliseconds + { L"Internet", &One } , + { L"KeepAliveCount", &Eight } , + { L"KeepAliveTimeout", &Sixty } , // half-seconds + { L"RetransmitMax", &Eight } , + { L"RouterMTU", &MaxMTU } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = NbiAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, sizeof(CONFIG), MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + Config->BindName.Buffer = NULL; + Config->DriverObject = DriverObject; // save this to log errors + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbiReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)NbiAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG); + NbiFreeConfiguration(Config); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + + // + // Determine what name to export and who to bind to. + // + + Status = NbiReadLinkageInformation (Config); + + if (Status != STATUS_SUCCESS) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + return Status; + } + + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-18) Call NbiSetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = NbiGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 19) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + NbiFreeConfiguration(Config); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 701, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + NbiFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* NbiGetConfiguration */ + + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get free any storage that was allocated + by NbiGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + if (Config->BindName.Buffer) { + NbiFreeMemory (Config->BindName.Buffer, Config->BindName.MaximumLength, MEMORY_CONFIG, "BindName"); + } + + if (Config->DeviceName.Buffer) { + NbiFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + NbiFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* NbiFreeConfig */ + + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + ULONG Data = *(UNALIGNED ULONG *)ValueData; + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + + switch ( (ULONG) EntryContext ) { + case CONFIG_ROUTER_MTU: + if ( ( Data - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER) ) <= 0 ) { + Config->Parameters[CONFIG_ROUTER_MTU] = 0xffffffff; + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 704, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_SUCCESS; + } + break; + default: + break; + } + + NB_DEBUG2 (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, Data)); + Config->Parameters[(ULONG)EntryContext] = Data; + + return STATUS_SUCCESS; + +} /* NbiGetConfigValue */ + + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read bind value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "BindName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->BindName.Buffer = NameBuffer; + Config->BindName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->BindName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddBind */ + + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddExport */ + + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; // set to TRUE when a value is read correctly + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call NbiAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = NbiAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 702, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + + // + // 1) Change to call NbiAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = NbiAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 703, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* NbiReadLinkageInformation */ + diff --git a/private/ntos/tdi/isnp/nb/config.h b/private/ntos/tdi/isnp/nb/config.h new file mode 100644 index 000000000..99b6c6357 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN Netbios module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_ACK_DELAY_TIME 0 +#define CONFIG_ACK_WINDOW 1 +#define CONFIG_ACK_WINDOW_THRESHOLD 2 +#define CONFIG_ENABLE_PIGGYBACK_ACK 3 +#define CONFIG_EXTENSIONS 4 +#define CONFIG_RCV_WINDOW_MAX 5 +#define CONFIG_BROADCAST_COUNT 6 +#define CONFIG_BROADCAST_TIMEOUT 7 +#define CONFIG_CONNECTION_COUNT 8 +#define CONFIG_CONNECTION_TIMEOUT 9 +#define CONFIG_INIT_PACKETS 10 +#define CONFIG_MAX_PACKETS 11 +#define CONFIG_INIT_RETRANSMIT_TIME 12 +#define CONFIG_INTERNET 13 +#define CONFIG_KEEP_ALIVE_COUNT 14 +#define CONFIG_KEEP_ALIVE_TIMEOUT 15 +#define CONFIG_RETRANSMIT_MAX 16 +#define CONFIG_ROUTER_MTU 17 +#define CONFIG_PARAMETERS 18 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + NDIS_STRING BindName; // device to bind to + PWSTR RegistryPathBuffer; // path to config info + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ); + diff --git a/private/ntos/tdi/isnp/nb/connect.c b/private/ntos/tdi/isnp/nb/connect.c new file mode 100644 index 000000000..7ee7204b5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/connect.c @@ -0,0 +1,3628 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This routine contains the code to handle connect requests + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef RASAUTODIAL +#include +#include + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ); +#endif // RASAUTODIAL + + + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +/*++ + +Routine Description: + + This routine is called when a find route request + previously issued to IPX completes. + +Arguments: + + FindRouteRequest - The find route request that was issued. + + FoundRoute - TRUE if the route was found. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + UINT i; + BOOLEAN LocalRoute; + USHORT TickCount; + PREQUEST RequestToComplete; + PUSHORT Counts; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + Connection = CONTAINING_RECORD (FindRouteRequest, CONNECTION, FindRouteRequest); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection->FindRouteInProgress = FALSE; + + if (FoundRoute) { + + // + // See if the route is local or not (for local routes + // we use the real MAC address in the local target, but + // the NIC ID may not be what we expect. + // + + LocalRoute = TRUE; + + for (i = 0; i < 6; i++) { + if (FindRouteRequest->LocalTarget.MacAddress[i] != 0x00) { + LocalRoute = FALSE; + } + } + + if (LocalRoute) { + +#if defined(_PNP_POWER) + Connection->LocalTarget.NicHandle = FindRouteRequest->LocalTarget.NicHandle; +#else + Connection->LocalTarget.NicId = FindRouteRequest->LocalTarget.NicId; +#endif _PNP_POWER + + } else { + + Connection->LocalTarget = FindRouteRequest->LocalTarget; + + } + + Counts = (PUSHORT)(&FindRouteRequest->Reserved2); + TickCount = Counts[0]; + + if (TickCount > 1) { + + // + // Each tick is 55 ms, and for our timeout we use 10 ticks + // worth (this makes tick count of 1 be about 500 ms, the + // default). + // + // We get 55 milliseconds from + // + // 1 second * 1000 milliseconds 55 ms + // -------- ----------------- = ----- + // 18.21 ticks 1 second tick + // + + Connection->TickCount = TickCount; + Connection->BaseRetransmitTimeout = (TickCount * 550) / SHORT_TIMER_DELTA; + if (Connection->State != CONNECTION_STATE_ACTIVE) { + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + } + + Connection->HopCount = Counts[1]; + + } + + // + // If the call failed we just use whatever route we had before + // (on a connect it will be from the name query response, on + // a listen from whatever the incoming connect frame had). + // + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_W_ROUTE)) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // Continue on with the session init frame. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + // Maximum packet size is the lower of RouterMtu and MaximumSendSize. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER) , Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION) ; + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + // + // Don't set RcvSequenceMax yet because we don't know + // if the connection is old or new netbios. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + + // + // We found a route, we need to start the connect + // process by sending out the session initialize + // frame. We start the timer to handle retries. + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiSendSessionInitialize (Connection); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ROUTE)) { + + if (Connection->ListenRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_ACTIVE); + RequestToComplete = Connection->ListenRequest; + Connection->ListenRequest = NULL; + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + + } else if (Connection->AcceptRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_ACCEPT, CREF_ACTIVE); + RequestToComplete = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } else { + + CTEAssert (FALSE); + RequestToComplete = NULL; + + } + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + + // Take the lowest of MaximumPacketSize ( set from the sessionInit + // frame ), MaximumSendSize and RouterMtu. + + if (Connection->MaximumPacketSize > Connection->LineInfo.MaximumSendSize - sizeof(NB_CONNECTION)) { + + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER), Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION); + + } else { + + // Connection->MaximumPacketSize is what was set by the sender so already + // accounts for the header. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER), Connection->MaximumPacketSize ) ; + + } + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + if (Connection->NewNetbios) { + CTEAssert (Connection->LocalRcvSequenceMax == 4); // should have been set + Connection->LocalRcvSequenceMax = Connection->ReceiveWindowSize; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + ++Device->Statistics.OpenConnections; + + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // StartWatchdog acquires TimerLock, so we have to + // free Lock first. + // + + + NbiStartWatchdog (Connection); + + // + // This releases the connection lock, so that SessionInitAckData + // can't be freed before it is copied. + // + + NbiSendSessionInitAck( + Connection, + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + &LockHandle1); + + if (RequestToComplete != NULL) { + + REQUEST_STATUS(RequestToComplete) = STATUS_SUCCESS; + + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_FIND_ROUTE); + +} /* NbiFindRouteComplete */ + + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PCONNECTION Connection; + PFILE_FULL_EA_INFORMATION ea; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + if (!(Connection = NbiCreateConnection (Device))) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &Connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)Connection; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONNECTION_FILE; +#ifdef ISN_NT + Connection->FileObject = IrpSp->FileObject; +#endif + + return STATUS_SUCCESS; + +} /* NbiOpenConnection */ + + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called to stop an active connection. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection to be stopped. + + DisconnectStatus - The reason for the disconnect. One of: + STATUS_LINK_FAILED: We timed out trying to probe the remote. + STATUS_REMOTE_DISCONNECT: The remote sent a session end. + STATUS_LOCAL_DISCONNECT: The local side disconnected. + STATUS_CANCELLED: A send or receive on this connection was cancelled. + STATUS_INVALID_CONNECTION: The local side closed the connection. + STATUS_INVALID_ADDRESS: The local side closed the address. + + LockHandle - The handle which the connection lock was acquired with. + +Return Value: + + None. + +--*/ + +{ + PREQUEST ListenRequest, AcceptRequest, SendRequest, ReceiveRequest, + DisconnectWaitRequest, ConnectRequest; + PREQUEST Request, TmpRequest; + BOOLEAN DerefForPacketize; + BOOLEAN DerefForWaitPacket; + BOOLEAN DerefForActive; + BOOLEAN DerefForWaitCache; + BOOLEAN SendSessionEnd; + BOOLEAN ActiveReceive; + BOOLEAN IndicateToClient; + BOOLEAN ConnectionWasActive; + PDEVICE Device = NbiDevice; + PADDRESS_FILE AddressFile; + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + + NB_DEBUG2 (CONNECTION, ("Stop connection %lx (%lx)\n", Connection, DisconnectStatus)); + + // + // These flags control our actions after we set the state to + // DISCONNECT. + // + + DerefForPacketize = FALSE; + DerefForWaitPacket = FALSE; + DerefForActive = FALSE; + DerefForWaitCache = FALSE; + SendSessionEnd = FALSE; + ActiveReceive = FALSE; + IndicateToClient = FALSE; + ConnectionWasActive = FALSE; + + // + // These contain requests or queues of request to complete. + // + + ListenRequest = NULL; + AcceptRequest = NULL; + SendRequest = NULL; + ReceiveRequest = NULL; + DisconnectWaitRequest = NULL; + ConnectRequest = NULL; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + --Device->Statistics.OpenConnections; + + ConnectionWasActive = TRUE; + + Connection->Status = DisconnectStatus; + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_LOCAL_DISCONNECT)) { + + // + // Send out session end frames, but fewer if + // we timed out. + // + // BUGBUG: What about STATUS_CANCELLED? + // + + Connection->Retries = (DisconnectStatus == STATUS_LOCAL_DISCONNECT) ? + Device->ConnectionCount : + (Device->ConnectionCount / 2); + + SendSessionEnd = TRUE; + Connection->SubState = CONNECTION_SUBSTATE_D_W_ACK; + + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + if (Connection->ReceiveState == CONNECTION_RECEIVE_TRANSFER) { + ActiveReceive = TRUE; + } + + Connection->State = CONNECTION_STATE_DISCONNECT; + DerefForActive = TRUE; + + if (Connection->DisconnectWaitRequest != NULL) { + DisconnectWaitRequest = Connection->DisconnectWaitRequest; + Connection->DisconnectWaitRequest = NULL; + } + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_REMOTE_DISCONNECT) || + (DisconnectStatus == STATUS_CANCELLED)) { + + IndicateToClient = TRUE; + + } + + // + // If we are inside NbiAssignSequenceAndSend, add + // a reference so the connection won't go away during it. + // + + if (Connection->NdisSendsInProgress > 0) { + *(Connection->NdisSendReference) = TRUE; + NB_DEBUG2 (SEND, ("Adding CREF_NDIS_SEND to %lx\n", Connection)); + NbiReferenceConnectionLock (Connection, CREF_NDIS_SEND); + } + + // + // Clean up some other stuff. + // + + Connection->ReceiveUnaccepted = 0; + Connection->CurrentIndicateOffset = 0; + + // + // Update our counters. BUGBUG: Some of these we + // never use. + // + + switch (DisconnectStatus) { + + case STATUS_LOCAL_DISCONNECT: + ++Device->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + ++Device->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + ++Device->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + ++Device->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + ++Device->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + ++Device->Statistics.RemoteResourceFailures; + break; + case STATUS_INVALID_CONNECTION: + case STATUS_INVALID_ADDRESS: + case STATUS_INSUFFICIENT_RESOURCES: + ++Device->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + case STATUS_REMOTE_NOT_LISTENING: + ++Device->Statistics.NotFoundFailures; + break; + default: + CTEAssert(FALSE); + break; + } + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // + // There is a connect in progress. We have to find ourselves + // in the pending connect queue if we are there. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) { + RemoveEntryList (REQUEST_LINKAGE(Connection->ConnectRequest)); + DerefForWaitCache = TRUE; + } + + if (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) { + + ConnectRequest = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + } + + } + + + // + // If we allocated this memory, free it. + // + + if (Connection->SessionInitAckDataLength > 0) { + + NbiFreeMemory( + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + MEMORY_CONNECTION, + "SessionInitAckData"); + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + + } + + + if (Connection->ListenRequest != NULL) { + + ListenRequest = Connection->ListenRequest; + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(ListenRequest)); // take out of Device->ListenQueue + + } + + if (Connection->AcceptRequest != NULL) { + + AcceptRequest = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } + + + // + // BUGBUG: Do we need to stop the connection timer? + // I don't think so. + // + + + + // + // A lot of this we only have to tear down if we were + // active before this, because once we are stopping nothing + // new will get started. BUGBUG: Some of the other stuff + // can be put inside this if also. + // + + if (ConnectionWasActive) { + + // + // Stop any receives. If there is one that is actively + // transferring we leave it and just run down the rest + // of the queue. If not, we queue the rest of the + // queue on the back of the current one and run + // down them all. + // + + if (ActiveReceive) { + + ReceiveRequest = Connection->ReceiveQueue.Head; + + // + // Connection->ReceiveRequest will get set to NULL + // when the transfer completes. + // + + } else { + + ReceiveRequest = Connection->ReceiveRequest; + if (ReceiveRequest) { + REQUEST_SINGLE_LINKAGE (ReceiveRequest) = Connection->ReceiveQueue.Head; + } else { + ReceiveRequest = Connection->ReceiveQueue.Head; + } + Connection->ReceiveRequest = NULL; + + } + + Connection->ReceiveQueue.Head = NULL; + + + if ((Request = Connection->FirstMessageRequest) != NULL) { + + // + // If the current request has some sends outstanding, then + // we dequeue it from the queue to let it complete when + // the sends complete. In that case we set SendRequest + // to be the rest of the queue, which will be aborted. + // If the current request has no sends, then we put + // queue everything to SendRequest to be aborted below. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + // + // NOTE: If this is a multi-request message, then + // the linkage of Request will already point to the + // send queue head, but we don't bother checking. + // + + SendRequest = Request; + REQUEST_SINGLE_LINKAGE (Request) = Connection->SendQueue.Head; + + } else { + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->FirstMessageRequest = NULL; + + } else { + + // + // This may happen if we were sending a probe when a + // send was submitted, and the probe timed out. + // + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->SendQueue.Head = NULL; + + } + + + if (Connection->OnWaitPacketQueue) { + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + DerefForWaitPacket = TRUE; + } + + if (Connection->OnPacketizeQueue) { + Connection->OnPacketizeQueue = FALSE; + RemoveEntryList (&Connection->PacketizeLinkage); + DerefForPacketize = TRUE; + } + + // + // BUGBUG: Should we check if DataAckPending is TRUE and + // send an ack?? + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // We can't acquire TimerLock with Lock held, since + // we sometimes call ReferenceConnection (which does an + // interlocked add using Lock) with TimerLock held. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle3); + + if (Connection->OnShortList) { + Connection->OnShortList = FALSE; + RemoveEntryList (&Connection->ShortList); + } + + if (Connection->OnLongList) { + Connection->OnLongList = FALSE; + RemoveEntryList (&Connection->LongList); + } + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + RemoveEntryList (&Connection->DataAckLinkage); + Device->DataAckQueueChanged = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle3); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + + if (IndicateToClient) { + + AddressFile = Connection->AddressFile; + + if (AddressFile->RegisteredHandler[TDI_EVENT_DISCONNECT]) { + + NB_DEBUG2 (CONNECTION, ("Session end indicated on connection %lx\n", Connection)); + + (*AddressFile->DisconnectHandler)( + AddressFile->HandlerContexts[TDI_EVENT_DISCONNECT], + Connection->Context, + 0, // DisconnectData + NULL, + 0, // DisconnectInformation + NULL, + TDI_DISCONNECT_RELEASE); // DisconnectReason. BUGBUG: Clean it up? + + } + + } + + + if (DisconnectWaitRequest != NULL) { + + // + // Make the TDI tester happy by returning CONNECTION_RESET + // here. + // + + if (DisconnectStatus == STATUS_REMOTE_DISCONNECT) { + REQUEST_STATUS(DisconnectWaitRequest) = STATUS_CONNECTION_RESET; + } else { + REQUEST_STATUS(DisconnectWaitRequest) = DisconnectStatus; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (DisconnectWaitRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (DisconnectWaitRequest); + NbiFreeRequest (Device, DisconnectWaitRequest); + + } + + if (ConnectRequest != NULL) { + + REQUEST_STATUS (ConnectRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ConnectRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION(ListenRequest) = 0; + REQUEST_STATUS(ListenRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest(Device, ListenRequest); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } + + if (AcceptRequest != NULL) { + + REQUEST_INFORMATION(AcceptRequest) = 0; + REQUEST_STATUS(AcceptRequest) = STATUS_LOCAL_DISCONNECT; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest(Device, AcceptRequest); + + NbiDereferenceConnection (Connection, CREF_ACCEPT); + + } + + while (ReceiveRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (ReceiveRequest); + + REQUEST_STATUS (ReceiveRequest) = DisconnectStatus; + REQUEST_INFORMATION (ReceiveRequest) = 0; + + NB_DEBUG2 (RECEIVE, ("StopConnection aborting receive %lx\n", ReceiveRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ReceiveRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ReceiveRequest); + NbiFreeRequest (Device, ReceiveRequest); + + ++Connection->ConnectionInfo.ReceiveErrors; + + ReceiveRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } + + while (SendRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + + REQUEST_STATUS (SendRequest) = DisconnectStatus; + REQUEST_INFORMATION (SendRequest) = 0; + + NB_DEBUG2 (SEND, ("StopConnection aborting send %lx\n", SendRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + + ++Connection->ConnectionInfo.TransmissionErrors; + + SendRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + if (SendSessionEnd) { + NbiSendSessionEnd (Connection); + } + + if (DerefForWaitCache) { + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + } + + if (DerefForPacketize) { + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + } + + if (DerefForWaitPacket) { + NbiDereferenceConnection (Connection, CREF_W_PACKET); + } + + if (DerefForActive) { + NbiDereferenceConnection (Connection, CREF_ACTIVE); + } + +} /* NbiStopConnection */ + + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (CONNECTION, ("Close connection %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (Connection->ReferenceCount == 0) { + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // Even if the ref count is zero and some thread has already done cleanup, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + if ( Connection->CanBeDestroyed && ( Connection->ThreadsInHandleConnectionZero == 0 ) ) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NbiDestroyConnection(Connection); + Status = STATUS_SUCCESS; + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + return Status; + +} /* NbiCloseConnection */ + + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and + the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; +#ifdef ISN_NT + PFILE_OBJECT FileObject; +#endif + PADDRESS_FILE AddressFile; + PADDRESS Address; + PTDI_REQUEST_KERNEL_ASSOCIATE Parameters; + CTELockHandle LockHandle; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + + // + // The request request parameters hold + // get a pointer to the address FileObject, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)REQUEST_PARAMETERS(Request); + +#ifdef ISN_NT + + Status = ObReferenceObjectByHandle ( + Parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + AddressFile = (PADDRESS_FILE)(FileObject->FsContext); + +#else + + // + // I don't know how this works in a VxD. + // + + AddressFile = (PADDRESS_FILE)(Parameters->AddressHandle); + +#endif + + // + // Make sure the address file is valid, and reference it. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + +#ifdef ISN_NT + ObDereferenceObject (FileObject); +#endif + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Associate connection %lx with address file %lx\n", + Connection, AddressFile)); + + + // + // Now insert the connection into the database of the address. + // + + Address = AddressFile->Address; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFile != NULL) { + + // + // The connection is already associated with + // an address file. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_CONNECTION; + + } else { + + if (AddressFile->State == ADDRESSFILE_STATE_OPEN) { + + Connection->AddressFile = AddressFile; + Connection->AddressFileLinked = TRUE; + InsertHeadList (&AddressFile->ConnectionDatabase, &Connection->AddressFileLinkage); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_ADDRESS; + } + + } + +#ifdef ISN_NT + + // + // We don't need the reference to the file object, we just + // used it to get from the handle to the object. + // + + ObDereferenceObject (FileObject); + +#endif + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAssociateAddress */ + + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the disassociation of the connection + and the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Disassociate connection %lx\n", Connection)); + + + // + // First check if the connection is still active. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if (Connection->State != CONNECTION_STATE_INACTIVE) { + + // + // This releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + // + // BUGBUG: Keep the sync through the function?? + // + + NB_END_SYNC (&SyncContext); + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Make sure the connection is associated and is not in the + // middle of disassociating. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL)) { + + if (Connection->ReferenceCount == 0) { + + // + // Because the connection still has a reference to + // the address file, we know it is still valid. We + // set the connection address file to the temporary + // value of -1, which prevents somebody else from + // disassociating it and also prevents a new association. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + // + // Set this so when the count goes to 0 it will + // be disassociated and the request completed. + // + + Connection->DisassociatePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisassociateAddress */ + + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine posts a listen on a connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the listen. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_WAITING; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + + if (!Request->Cancel) { + + NB_DEBUG2 (CONNECTION, ("Queued listen %lx on %lx\n", Request, Connection)); + InsertTailList (&Device->ListenQueue, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelListen); + Connection->ListenRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_LISTEN); + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled listen %lx on %lx\n", Request, Connection)); + Connection->State = CONNECTION_STATE_INACTIVE; + Status = STATUS_CANCELLED; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiListen */ + + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine accepts a connection to a remote machine. The + connection must previously have completed a listen with + the TDI_QUERY_ACCEPT flag on. + +Arguments: + + Device - The netbios device. + + Request - The request describing the accept. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiTransferReferenceConnection (Connection, CREF_W_ACCEPT, CREF_ACCEPT); + Connection->AcceptRequest = Request; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->Retries = NbiDevice->KeepAliveCount; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + NB_DEBUG2 (CONNECTION, ("Accept received on %lx\n", Connection)); + + Status = STATUS_PENDING; + + } else { + + NB_DEBUG (CONNECTION, ("Accept received on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAccept */ + + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_CONNECT Parameters; +#if 0 + PLARGE_INTEGER RequestedTimeout; + LARGE_INTEGER RealTimeout; +#endif + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteName == NULL) { + + // + // There is no netbios remote address specified. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_BAD_NETWORK_PATH; + + } else { + + NbiReferenceConnectionLock (Connection, CREF_CONNECT); + Connection->State = CONNECTION_STATE_CONNECTING; + RtlCopyMemory (Connection->RemoteName, RemoteName->NetbiosName, 16); + + Connection->Retries = Device->ConnectionCount; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + Status = NbiTdiConnectFindName( + Device, + Request, + Connection, + CancelLH, + LockHandle1, + LockHandle2, + &bLockFreed); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Connect on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + if (!bLockFreed) { + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiConnect */ + + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ) +{ + NTSTATUS Status; + PNETBIOS_CACHE CacheName; + + // + // See what is up with this Netbios name. + // + + Status = CacheFindName( + Device, + FindNameConnect, + Connection->RemoteName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this connect + // request and processing will be resumed when + // we get a response. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_FIND_NAME; + + + if (!Request->Cancel) { + + InsertTailList( &Device->WaitingConnects, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelConnectFindName); + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_WAIT_CACHE); + NB_DEBUG2 (CONNECTION, ("Queueing up connect %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // We don't need to worry about referencing CacheName + // because we stop using it before we release the lock. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelConnectWaitResponse); + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, ConnectionLH); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_DEBUG2 (CONNECTION, ("Found connect cached %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NB_FREE_LOCK (&Connection->Lock, ConnectionLH); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + Status = STATUS_PENDING; + + // + // This jump is like falling out of the if, except + // it skips over freeing the connection lock since + // we just did that. + // + + *pbLockFreed = TRUE; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else { + + // + // We could not find or queue a request for + // this remote, fail it. When the refcount + // drops the state will go to INACTIVE and + // the connection ID will be deassigned. + // + + if (Status == STATUS_DEVICE_DOES_NOT_EXIST) { + Status = STATUS_BAD_NETWORK_PATH; + } + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + } + + return Status; +} /* NbiTdiConnectFindName */ + + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + BOOLEAN DisconnectWait; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + CTELockHandle CancelLH; + + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + DisconnectWait = (BOOLEAN) + ((((PTDI_REQUEST_KERNEL_DISCONNECT)(REQUEST_PARAMETERS(Request)))->RequestFlags & + TDI_DISCONNECT_WAIT) != 0); + + NB_GET_CANCEL_LOCK( &CancelLH ); + + // + // We need to be inside a sync because NbiStopConnection + // expects that. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (DisconnectWait) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // This disconnect wait will get completed by + // NbiStopConnection. + // + + if (Connection->DisconnectWaitRequest == NULL) { + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelDisconnectWait); + NB_DEBUG2 (CONNECTION, ("Disconnect wait queued on connection %lx\n", Connection)); + Connection->DisconnectWaitRequest = Request; + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled disconnect wait on connection %lx\n", Connection)); + Status = STATUS_CANCELLED; + } + + } else { + + // + // We got a second disconnect request and we already + // have one pending. + // + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, already queued on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + NB_DEBUG (CONNECTION, ("Disconnect wait submitted on disconnected connection %lx\n", Connection)); + Status = Connection->Status; + + } else { + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, bad state on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } else { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + + NB_DEBUG2 (CONNECTION, ("Disconnect of active connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + // + // This call releases the connection lock, sets + // the state to DISCONNECTING, and sends out + // the first session end. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // There is already a disconnect pending. Queue + // this one up so it completes when the refcount + // goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of disconnecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + // + // We were waiting for an accept, but instead we got + // a disconnect. Remove the reference and the teardown + // will proceed. The disconnect will complete when the + // refcount goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of accept pending connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_W_ACCEPT); + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // We are connecting, and got a disconnect. We call + // NbiStopConnection which will handle this case + // and abort the connect. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of connecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // This call releases the connection lock and + // aborts the connect request. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Disconnect of invalid connection (%d) %lx\n", + Connection->State, Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Status = STATUS_INVALID_CONNECTION; + + } + + } + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisconnect */ + + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to assign a connection ID. It picks + one whose hash table has the fewest entries. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. THE CONNECTION IS INSERTED INTO THE CORRECT HASH + ENTRY BY THIS CALL. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + TRUE if it could be successfully assigned. + +--*/ + +{ + UINT Hash; + UINT i; + USHORT ConnectionId, HashId; + PCONNECTION CurConnection; + + + CTEAssert (Connection->LocalConnectionId == 0xffff); + + // + // Find the hash bucket with the fewest entries. + // + + Hash = 0; + for (i = 1; i < CONNECTION_HASH_COUNT; i++) { + if (Device->ConnectionHash[i].ConnectionCount < Device->ConnectionHash[Hash].ConnectionCount) { + Hash = i; + } + } + + + // + // Now find a valid connection ID within that bucket. + // + + ConnectionId = Device->ConnectionHash[Hash].NextConnectionId; + + while (TRUE) { + + // + // Scan through the list to see if this ID is in use. + // + + HashId = (USHORT)(ConnectionId | (Hash << CONNECTION_HASH_SHIFT)); + + CurConnection = Device->ConnectionHash[Hash].Connections; + + while (CurConnection != NULL) { + if (CurConnection->LocalConnectionId != HashId) { + CurConnection = CurConnection->NextConnection; + } else { + break; + } + } + + if (CurConnection == NULL) { + break; + } + + if (ConnectionId >= CONNECTION_MAXIMUM_ID) { + ConnectionId = 1; + } else { + ++ConnectionId; + } + + // + // BUGBUG: What if we have 64K-1 sessions and loop forever? + // + } + + if (Device->ConnectionHash[Hash].NextConnectionId >= CONNECTION_MAXIMUM_ID) { + Device->ConnectionHash[Hash].NextConnectionId = 1; + } else { + ++Device->ConnectionHash[Hash].NextConnectionId; + } + + Connection->LocalConnectionId = HashId; + Connection->RemoteConnectionId = 0xffff; + NB_DEBUG2 (CONNECTION, ("Assigned ID %lx to %x\n", Connection->LocalConnectionId, Connection)); + + Connection->NextConnection = Device->ConnectionHash[Hash].Connections; + Device->ConnectionHash[Hash].Connections = Connection; + ++Device->ConnectionHash[Hash].ConnectionCount; + + return TRUE; + +} /* NbiAssignConnectionId */ + + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to deassign a connection ID. It removes + the connection from the hash bucket for its ID. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + None. + +--*/ + +{ + UINT Hash; + PCONNECTION CurConnection; + PCONNECTION * PrevConnection; + + // + // Make sure the connection has a valid ID. + // + + CTEAssert (Connection->LocalConnectionId != 0xffff); + + Hash = (Connection->LocalConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + CurConnection = Device->ConnectionHash[Hash].Connections; + PrevConnection = &Device->ConnectionHash[Hash].Connections; + + while (TRUE) { + + CTEAssert (CurConnection != NULL); + + // + // We can loop until we find it because it should be + // on here. + // + + if (CurConnection == Connection) { + *PrevConnection = Connection->NextConnection; + --Device->ConnectionHash[Hash].ConnectionCount; + break; + } + + PrevConnection = &CurConnection->NextConnection; + CurConnection = CurConnection->NextConnection; + + } + + Connection->LocalConnectionId = 0xffff; + +} /* NbiDeassignConnectionId */ + + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the connection timer expires. + This is either because we need to send the next session + initialize, or because our listen has timed out. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the connection. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection = (PCONNECTION)Context; + PDEVICE Device = NbiDevice; + PREQUEST Request; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (CancelLH) + + // + // Take the lock and see what we need to do. + // + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (--Connection->Retries == 0) { + + NB_DEBUG2 (CONNECTION, ("Timing out session initializes on %lx\n", Connection)); + + // + // We have just timed out this connect, we fail the + // request. When the reference count goes to 0 we + // will set the state to INACTIVE and deassign + // the connection ID. + // + + Request = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_BAD_NETWORK_PATH; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session initialize. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionInitialize (Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + if ((Connection->SubState != CONNECTION_SUBSTATE_D_W_ACK) || + (--Connection->Retries == 0)) { + + NB_DEBUG2 (CONNECTION, ("Timing out disconnect of %lx\n", Connection)); + + // + // Just dereference the connection, that will cause the + // disconnect to be completed, the state to be set + // to INACTIVE, and our connection ID deassigned. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session end. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionEnd(Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } + +} /* NbiConnectionTimeout */ + + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + listen. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_LISTEN)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_WAITING) && + (Connection->ListenRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled listen on %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel listen on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelListen */ + + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for the name to be found. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) && + (Connection->ConnectRequest == Request)) { + + // + // Make sure the request is still on the queue + // before cancelling it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + for (p = Device->WaitingConnects.Flink; + p != &Device->WaitingConnects; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + break; + } + } + + if (p != &Device->WaitingConnects) { + + NB_DEBUG2 (CONNECTION, ("Cancelled find name connect on %lx\n", Connection)); + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + Connection->ConnectRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + +#ifdef RASAUTODIAL + if (Connection->Flags & CONNECTION_FLAGS_AUTOCONNECTING) + fCanceled = NbiCancelTdiConnect(Device, Request, Connection); +#endif // RASAUTODIAL + + if (fCanceled) { + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect not found on queue %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectFindName */ + + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for a rip or session init response + from the remote. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN TimerWasStopped = FALSE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) && + (Connection->ConnectRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled wait response connect on %lx\n", Connection)); + + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectWaitResponse */ + + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + disconnect wait. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_DISCONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->DisconnectWaitRequest == Request) { + + Connection->DisconnectWaitRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelDisconnectWait */ + + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine looks up a connection based on the context. + The connection is assumed to be associated with the + specified address file. + +Arguments: + + AddressFile - Pointer to an address file. + + ConnectionContext - Connection context to find. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + CTELockHandle LockHandle1, LockHandle2; + PLIST_ENTRY p; + PADDRESS Address = AddressFile->Address; + PCONNECTION Connection; + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p=AddressFile->ConnectionDatabase.Flink; + p != &AddressFile->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + NB_GET_LOCK (&Connection->Lock, &LockHandle2); + + // + // BUGBUG: Does this spinlock ordering hurt us + // somewhere else? + // + + if (Connection->Context == ConnectionContext) { + + NbiReferenceConnection (Connection, CREF_BY_CONTEXT); + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return Connection; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return NULL; + +} /* NbiLookupConnectionByContext */ + + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates a transport connection and associates it with + the specified transport device context. The reference count in the + connection is automatically set to 1, and the reference count of the + device context is incremented. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + connection. + +Return Value: + + The newly created connection, or NULL if none can be allocated. + +--*/ + +{ + PCONNECTION Connection; + PNB_SEND_RESERVED SendReserved; + ULONG ConnectionSize; + ULONG HeaderLength; + NTSTATUS Status; + CTELockHandle LockHandle; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION); + ConnectionSize = FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + HeaderLength; + + Connection = (PCONNECTION)NbiAllocateMemory (ConnectionSize, MEMORY_CONNECTION, "Connection"); + if (Connection == NULL) { + NB_DEBUG (CONNECTION, ("Create connection failed\n")); + return NULL; + } + + NB_DEBUG2 (CONNECTION, ("Create connection %lx\n", Connection)); + RtlZeroMemory (Connection, ConnectionSize); + + +#if defined(NB_OWN_PACKETS) + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + +#else // !NB_OWN_PACKETS + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &Connection->SendPacketPoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status)); + Connection->SendPacketInUse = TRUE; + } else { + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(Connection->SendPacketPoolHandle); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + } + +#endif NB_OWN_PACKETS + + Connection->Type = NB_CONNECTION_SIGNATURE; + Connection->Size = (USHORT)ConnectionSize; + +#if 0 + Connection->AddressFileLinked = FALSE; + Connection->AddressFile = NULL; +#endif + + Connection->State = CONNECTION_STATE_INACTIVE; +#if 0 + Connection->SubState = 0; + Connection->ReferenceCount = 0; +#endif + + Connection->CanBeDestroyed = TRUE; + + Connection->TickCount = 1; + Connection->HopCount = 1; + + // + // Device->InitialRetransmissionTime is in milliseconds, as is + // SHORT_TIMER_DELTA. + // + + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + // + // Device->KeepAliveTimeout is in half-seconds, while LONG_TIMER_DELTA + // is in milliseconds. + // + + Connection->WatchdogTimeout = (Device->KeepAliveTimeout * 500) / LONG_TIMER_DELTA; + + + Connection->LocalConnectionId = 0xffff; + + // + // When the connection becomes active we will replace the + // destination address of this header with the correct + // information. + // + + RtlCopyMemory(&Connection->RemoteHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + Connection->Device = Device; + Connection->DeviceLock = &Device->Lock; + CTEInitLock (&Connection->Lock.Lock); + + CTEInitTimer (&Connection->Timer); + + InitializeListHead (&Connection->NdisSendQueue); +#if 0 + Connection->NdisSendsInProgress = 0; + Connection->DisassociatePending = NULL; + Connection->ClosePending = NULL; + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; +#endif + Connection->Flags = 0; + + NbiReferenceDevice (Device, DREF_CONNECTION); + + return Connection; + +} /* NbiCreateConnection */ + + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. We reference + it to keep it from disappearing while we use it. + +Arguments: + + Connection - potential pointer to a CONNECTION object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE Device = NbiDevice; + BOOLEAN LockHeld = FALSE; + + try { + + if ((Connection->Size == FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) && + (Connection->Type == NB_CONNECTION_SIGNATURE)) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LockHeld = TRUE; + + if (Connection->State != CONNECTION_STATE_CLOSING) { + + NbiReferenceConnectionLock (Connection, CREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx closing\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx bad signature\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyConnection: C %lx exception\n", Connection); + if (LockHeld) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyConnection */ + + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to nonpaged system pool. + +Arguments: + + Connection - Pointer to a transport connection structure to be destroyed. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = Connection->Device; +#if 0 + CTELockHandle LockHandle; +#endif + + NB_DEBUG2 (CONNECTION, ("Destroy connection %lx\n", Connection)); + + if (!Connection->SendPacketInUse) { + NbiDeinitializeSendPacket (Device, &Connection->SendPacket, Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(Connection->SendPacketPoolHandle); +#endif + } + + NbiFreeMemory (Connection, (ULONG)Connection->Size, MEMORY_CONNECTION, "Connection"); + + NbiDereferenceDevice (Device, DREF_CONNECTION); + +} /* NbiDestroyConnection */ + + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + (VOID)ExInterlockedAddUlong ( + &Connection->ReferenceCount, + 1, + &Connection->DeviceLock->Lock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnection */ + + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when the device lock is already held. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + ++Connection->ReferenceCount; + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionLock */ + + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when we are in a sync routine. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + (VOID)NB_ADD_ULONG ( + &Connection->ReferenceCount, + 1, + Connection->DeviceLock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionSync */ + + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiHandleConnectionZero to complete any disconnect, disassociate, + or close requests that have pended on the connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + CTELockHandle LockHandle; + + NB_GET_LOCK( Connection->DeviceLock, &LockHandle ); + CTEAssert( Connection->ReferenceCount ); + if ( !(--Connection->ReferenceCount) ) { + + Connection->ThreadsInHandleConnectionZero++; + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + + // + // If the refcount has dropped to 0, then the connection can + // become inactive. We reacquire the spinlock and if it has not + // jumped back up then we handle any disassociates and closes + // that have pended. + // + + NbiHandleConnectionZero (Connection); + } else { + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + } + + +} /* NbiDerefConnection */ + + +#endif + + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine handles a connection's refcount going to 0. + + BUGBUG: If two threads are in this at the same time and + the close has already come through, one of them might + destroy the connection while the other one is looking + at it. We minimize the chance of this by not derefing + the connection after calling CloseConnection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST DisconnectPending; + PREQUEST DisassociatePending; + PREQUEST ClosePending; + + + Device = Connection->Device; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + +#if DBG + // + // Make sure if our reference count is zero, all the + // sub-reference counts are also zero. + // + + if (Connection->ReferenceCount == 0) { + + UINT i; + for (i = 0; i < CREF_TOTAL; i++) { + if (Connection->RefTypes[i] != 0) { + DbgPrint ("NBI: Connection reftype mismatch on %lx\n", Connection); + DbgBreakPoint(); + } + } + } +#endif + + // + // If the connection was assigned an ID, then remove it + // (it is assigned one when it leaves INACTIVE). + // + + if (Connection->LocalConnectionId != 0xffff) { + NbiDeassignConnectionId (Device, Connection); + } + + // + // Complete any pending disconnects. + // + + if (Connection->DisconnectRequest != NULL) { + + DisconnectPending = Connection->DisconnectRequest; + Connection->DisconnectRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_STATUS(DisconnectPending) = STATUS_SUCCESS; + NbiCompleteRequest (DisconnectPending); + NbiFreeRequest (Device, DisconnectPending); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // This should have been completed by NbiStopConnection, + // or else not allowed to be queued. + // + + CTEAssert (Connection->DisconnectWaitRequest == NULL); + + + Connection->State = CONNECTION_STATE_INACTIVE; + + // + // BUGBUG: Make NbiInitializeConnection() to take care of all this. + // + + RtlZeroMemory (&Connection->ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + Connection->TickCount = 1; + Connection->HopCount = 1; + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + + Connection->ConnectionInfo.TransmittedTsdus = 0; + Connection->ConnectionInfo.TransmissionErrors = 0; + Connection->ConnectionInfo.ReceivedTsdus = 0; + Connection->ConnectionInfo.ReceiveErrors = 0; + + // + // See if we need to do a disassociate now. + // + + if ((Connection->ReferenceCount == 0) && + (Connection->DisassociatePending != NULL)) { + + // + // A disassociate pended, now we complete it. + // + + DisassociatePending = Connection->DisassociatePending; + Connection->DisassociatePending = NULL; + + // + // Set this so nobody else tries to disassociate. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + if (DisassociatePending != (PVOID)-1) { + REQUEST_STATUS(DisassociatePending) = STATUS_SUCCESS; + NbiCompleteRequest (DisassociatePending); + NbiFreeRequest (Device, DisassociatePending); + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + + // + // If a close was pending, complete that. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Connection->ReferenceCount == 0) && + (Connection->ClosePending)) { + + ClosePending = Connection->ClosePending; + Connection->ClosePending = NULL; + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Even if the ref count is zero and we just cleaned up everything, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + if (ExInterlockedAddUlong ( &Connection->ThreadsInHandleConnectionZero, (ULONG)-1, &Device->Lock.Lock) == 1) { + NbiDestroyConnection(Connection); + } + + REQUEST_STATUS(ClosePending) = STATUS_SUCCESS; + NbiCompleteRequest (ClosePending); + NbiFreeRequest (Device, ClosePending); + + } else { + + if ( Connection->ReferenceCount == 0 ) { + Connection->CanBeDestroyed = TRUE; + } + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + Connection->ThreadsInHandleConnectionZero--; + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiHandleConnectionZero */ + diff --git a/private/ntos/tdi/isnp/nb/datagram.c b/private/ntos/tdi/isnp/nb/datagram.c new file mode 100644 index 000000000..e81579d1b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/datagram.c @@ -0,0 +1,1089 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + datagram.c + +Abstract: + + This module contains the code to handle datagram reception + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ) + +/*++ + +Routine Description: + + This routine handles datagram indications. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + + Broadcast - TRUE if the frame was a broadcast datagram. + +Return Value: + + None. + +--*/ + +{ + + PADDRESS Address; + NDIS_STATUS NdisStatus; + PUCHAR NetbiosName; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + PDEVICE Device = NbiDevice; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + ULONG DataOffset; + UINT BytesTransferred; + PNDIS_PACKET Packet; + CTELockHandle LockHandle; + + + // + // See if there is an address that might want this. + // + + if (Broadcast) { + NetbiosName = (PVOID)-1; + } else { + NetbiosName = (PUCHAR)Connectionless->Datagram.DestinationName; + if (Device->AddressCounts[NetbiosName[0]] == 0) { + return; + } + } + + DataOffset = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + +#if defined(_PNP_POWER) + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->CurMaxReceiveBufferSize)) { +#else + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->Bind.LineInfo.MaximumPacketSize)) { +#endif _PNP_POWER + + NB_DEBUG (DATAGRAM, ("Datagram length %d discarded\n", PacketSize)); + return; + } + + Address = NbiFindAddress (Device, NetbiosName); + + if (Address == NULL) { + return; + } + + // + // We need to cache the remote name if the packet came across the router. + // This allows this machine to get back to the RAS client which might + // have sent this datagram. We currently dont allow broadcasts to go out + // on the dial-in line. + // Dont cache some of the widely used group names, that would be too much + // to store in cache. + // + +#if 0 + if ( Connectionless->IpxHeader.TransportControl && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x0 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x01 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x1E ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { +#endif + if ( Connectionless->IpxHeader.TransportControl && + ( (Address->NetbiosAddress.NetbiosName[15] == 0x1c ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { + + PNETBIOS_CACHE CacheName; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + if ( FindInNetbiosCacheTable ( Device->NameCache, + Connectionless->Datagram.SourceName, + &CacheName ) != STATUS_SUCCESS ) { + + CacheName = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (CacheName ) { + RtlCopyMemory (CacheName->NetbiosName, Connectionless->Datagram.SourceName, 16); + CacheName->Unique = TRUE; + CacheName->ReferenceCount = 1; + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->NetworksAllocated = 1; + CacheName->NetworksUsed = 1; + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + NB_DEBUG2 (CACHE, ("Alloc new cache from Datagram %lx for <%.16s>\n", + CacheName, CacheName->NetbiosName)); + + CacheName->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + CacheName); + + } + } else if ( CacheName->Unique ) { + // + // We already have an entry for this remote. We should update + // the address. This is so that if the ras client dials-out + // then dials-in again and gets a new address, we dont end up + // caching the old address. + // + if ( !RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12) ) { + + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + + } + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + // + // We need to allocate a packet and buffer for the transfer. + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + + + s = NbiPopReceiveBuffer (Device); + if (s == NULL) { + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveBuffer = CONTAINING_RECORD (s, NB_RECEIVE_BUFFER, PoolLinkage); + + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + ReceiveReserved->u.RR_DG.ReceiveBuffer = ReceiveBuffer; + + + // + // Now that we have a packet and a buffer, set up the transfer. + // The indication to the TDI clients will happen at receive + // complete time. + // + + NdisChainBufferAtFront (Packet, ReceiveBuffer->NdisBuffer); + ReceiveBuffer->Address = Address; + + ReceiveReserved->Type = RECEIVE_TYPE_DATAGRAM; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + TdiCopyLookaheadData( + &ReceiveBuffer->RemoteName, + Connectionless->Datagram.SourceName, + 16, + (MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + DataOffset, + PacketSize - DataOffset, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == PacketSize - DataOffset); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessDatagram */ + + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine indicates a datagram to clients on the specified + address. It is called from NbiReceiveComplete. + +Arguments: + + Address - The address the datagram was sent to. + + RemoteName - The source netbios address of the datagram. + + Data - The data. + + DataLength - The length of the data. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p, q; + PIRP Irp; + ULONG IndicateBytesCopied; + PREQUEST Request; + TA_NETBIOS_ADDRESS SourceName; + PTDI_CONNECTION_INFORMATION RemoteInformation; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // Update our statistics. + // + + ++Device->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + DataLength); + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress (RemoteName, FALSE, &SourceName); + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = AddressFile->ReceiveDatagramQueue.Flink; + q != &AddressFile->ReceiveDatagramQueue; + q = q->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (q); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbiParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + RemoteName, + DatagramAddress->NetbiosName, + 16))) { + continue; + } + break; + } + + if (q != &AddressFile->ReceiveDatagramQueue) { + + RemoveEntryList (q); + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // Do this deref now, we hold another one so it + // will stick around. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IndicateBytesCopied = 0; + + // + // Fall past the else to copy the data. + // + + } else { + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No receive datagram requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE_DATAGRAM]) { + + IndicateBytesCopied = 0; + + if ((*AddressFile->ReceiveDatagramHandler)( + AddressFile->HandlerContexts[TDI_EVENT_RECEIVE_DATAGRAM], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + DataLength, // indicated + DataLength, // available + &IndicateBytesCopied, + Data, + &Irp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + Request = NbiAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + } else { + + // + // The client has nothing posted and no handler, + // go on to the next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + } + + // + // We have a request; copy the actual user data. + // + if ( REQUEST_NDIS_BUFFER (Request) ) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl ( + Data, + IndicateBytesCopied, + DataLength - IndicateBytesCopied, + REQUEST_NDIS_BUFFER (Request), + 0, + &REQUEST_INFORMATION (Request)); + + } else { + // + // No buffer specified in the request + // + REQUEST_INFORMATION (Request) = 0; + // + // If there was any data to be copied, return error o/w success + // + REQUEST_STATUS(Request) = ( (DataLength - IndicateBytesCopied) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ); + } + + // + // Copy the addressing information. + // + + RemoteInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReturnDatagramInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + +} /* NbiIndicateDatagram */ + + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends a datagram on an address. + +Arguments: + + Device - The netbios device. + + Request - The request describing the datagram send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS_FILE AddressFile; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle; + NTSTATUS Status; + + // + // Make sure that the address is valid. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status == STATUS_SUCCESS) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->SendDatagramInformation->RemoteAddress), TRUE); + + + // + // Check that datagram size is less than the maximum allowable + // by the adapters. In the worst case this would be + // 576 - 64 = 512. + // + +#if defined(_PNP_POWER) + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumSendSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumSendSize )); + return STATUS_INVALID_PARAMETER; + } +#else + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumPacketSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumPacketSize )); + return STATUS_INVALID_PARAMETER; + } +#endif _PNP_POWER + + if (RemoteName != NULL) { + + // + // Get a packet to use in this send. + // + + s = NbiPopSendPacket (Device, FALSE); + + if (s != NULL) { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Check on the cache status of this name. + // + + Reserved->u.SR_DG.DatagramRequest = Request; + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.RemoteName = RemoteName; + + REQUEST_INFORMATION (Request) = Parameters->SendLength; + + ++Device->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Parameters->SendLength); + + if (Device->Internet) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + (RemoteName == (PVOID)-1) ? NULL : (PUCHAR)RemoteName->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this datagram + // request and processing will be resumed when + // we get a response. + // + + NB_DEBUG2 (CONNECTION, ("Queueing up datagram %lx on %lx\n", + Request, AddressFile)); + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + InsertTailList( + &Device->WaitingDatagrams, + &Reserved->WaitLinkage); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (CONNECTION, ("Found datagram cached %lx on %lx\n", + Request, AddressFile)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + ++CacheName->ReferenceCount; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } else { + + // + // Only this failure gets passed back up to + // the caller, to avoid confusing the browser. + // + + if (Status != STATUS_DEVICE_DOES_NOT_EXIST) { + + Status = STATUS_SUCCESS; + + } else { + + REQUEST_INFORMATION (Request) = 0; + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + + + + } + + } else { + + // + // We are not in internet mode, so we do not + // need to do the name discovery. + // + + NB_DEBUG2 (CONNECTION, ("Sending datagram direct %lx on %lx\n", + Request, AddressFile)); + + Reserved->u.SR_DG.Cache = NULL; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } + + } else { + + // + // Could not allocate a packet for the datagram. + // + + NB_DEBUG (DATAGRAM, ("Couldn't get packet to send DG %lx\n", Request)); + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + + } else { + + // + // There is no netbios remote address specified. + // + + NB_DEBUG (DATAGRAM, ("No netbios address in DG %lx\n", Request)); + Status = STATUS_BAD_NETWORK_PATH; + + } + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } else { + + NB_DEBUG (DATAGRAM, ("Invalid address file for DG %lx\n", Request)); + + } + + return Status; + +} /* NbiTdiSendDatagram */ + + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine sends a datagram to the next net in the + cache entry for the remote name. + +Arguments: + + Reserved - The reserved section of the packet that has + been allocated for this send. Reserved->u.SR_DG.Cache + will be NULL if Internet mode is off, otherwise it + will contain the cache entry to use when sending + this datagram. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_PACKET Packet; + PNETBIOS_CACHE CacheName; + NB_CONNECTIONLESS UNALIGNED * Header; + ULONG HeaderLength; + ULONG PacketLength; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DATAGRAM; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + if (CacheName == NULL) { + +#if defined(_PNP_POWER) + // + // IPX will send this on all the Nics. + // + TempLocalTarget.NicHandle.NicId = 0; +#else + TempLocalTarget.NicId = 1; +#endif _PNP_POWER + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + + } else { + + if (CacheName->Unique) { + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + } else { + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + } + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + } + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), HeaderLength); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiTransmitDatagram */ + + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Request - Describes this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + CTELockHandle CancelLH; + + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status != STATUS_SUCCESS) { + return Status; + } + + Address = AddressFile->Address; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_INVALID_HANDLE; + } + + + if (Request->Cancel) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_CANCELLED; + } + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, NbiCancelReceiveDatagram); + + NB_DEBUG2 (DATAGRAM, ("RDG posted on %lx\n", AddressFile)); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_RCV_DGRAM); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + return STATUS_PENDING; + +} /* NbiTdiReceiveDatagram */ + + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + NB_DEBUG (DATAGRAM, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest((PDEVICE)DeviceObject, Request); + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* NbiCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/isnp/nb/device.c b/private/ntos/tdi/isnp/nb/device.c new file mode 100644 index 000000000..d1a9af781 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/device.c @@ -0,0 +1,461 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiCreateDevice) +#endif + + +VOID +NbiRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Device->ReferenceCount); + +} /* NbiRefDevice */ + + +VOID +NbiDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + NbiDestroyDevice (Device); + } + +} /* NbiDerefDevice */ + + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + NB_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject; + + NB_DEBUG2 (DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer, Device)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(Device+1); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "NDC1", 4); + RtlCopyMemory(Device->Signature2, "NDC2", 4); +#endif + + // + // BETABUGBUG: Clean this up a bit. + // + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 65535; + Device->Information.MaxConnectionUserData = 0; + Device->Information.MaxDatagramSize = 500; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTION_MODE | TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_MULTICAST_SUPPORTED | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_MESSAGE_MODE; + Device->Information.MinimumLookaheadData = 128; + Device->Information.MaximumLookaheadData = 1500; + Device->Information.NumberOfResources = 0; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + Device->Statistics.MaximumSendWindow = 4; + Device->Statistics.AverageSendWindow = 4; + + // + // Set this so we won't ignore the broadcast name. + // + + Device->AddressCounts['*'] = 1; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock.Lock); + CTEInitLock (&Device->Lock.Lock); + + CTEInitTimer (&Device->FindNameTimer); + + Device->ControlChannelIdentifier = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); + + InitializeListHead (&Device->AddressDatabase); +#if defined(_PNP_POWER) + InitializeListHead (&Device->AdapterAddressDatabase); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingFindNames); + + InitializeListHead (&Device->WaitingConnects); + InitializeListHead (&Device->WaitingDatagrams); + + InitializeListHead (&Device->WaitingAdapterStatus); + InitializeListHead (&Device->ActiveAdapterStatus); + + InitializeListHead (&Device->WaitingNetbiosFindName); + + InitializeListHead (&Device->ReceiveDatagrams); + InitializeListHead (&Device->ConnectIndicationInProgress); + + InitializeListHead (&Device->ListenQueue); + + InitializeListHead (&Device->ReceiveCompletionQueue); + + InitializeListHead (&Device->WaitPacketConnections); + InitializeListHead (&Device->PacketizeConnections); + InitializeListHead (&Device->DataAckConnections); + + Device->MemoryUsage = 0; + + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + InitializeListHead (&Device->ReceiveBufferPoolList); + + ExInitializeSListHead( &Device->SendPacketList ); + ExInitializeSListHead( &Device->ReceivePacketList ); + Device->ReceiveBufferList.Next = NULL; + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + Device->ConnectionHash[i].Connections = NULL; + Device->ConnectionHash[i].ConnectionCount = 0; + Device->ConnectionHash[i].NextConnectionId = 1; + } + + KeQuerySystemTime (&Device->NbiStartTime); + + Device->State = DEVICE_STATE_CLOSED; + + Device->Type = NB_DEVICE_SIGNATURE; + Device->Size - sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* NbiCreateDevice */ + + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PNB_SEND_POOL SendPool; + PNB_SEND_PACKET SendPacket; + UINT SendPoolSize; + PNB_RECEIVE_POOL ReceivePool; + PNB_RECEIVE_PACKET ReceivePacket; + UINT ReceivePoolSize; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + ULONG HeaderLength; + UINT i; + + NB_DEBUG2 (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the connectionless packets out of its pools. + // + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, NB_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + NbiDeinitializeSendPacket (Device, SendPacket, HeaderLength); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", SendPool)); + +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(SendPool->PoolHandle); +#endif + + NbiFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, NB_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + NbiDeinitializeReceivePacket (Device, ReceivePacket); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", ReceivePool)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(ReceivePool->PoolHandle); +#endif + NbiFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } + +#if defined(_PNP_POWER) + NbiDestroyReceiveBufferPools( Device ); + + // + // Destroy adapter address list. + // + while(!IsListEmpty( &Device->AdapterAddressDatabase ) ){ + PADAPTER_ADDRESS AdapterAddress; + AdapterAddress = CONTAINING_RECORD( Device->AdapterAddressDatabase.Flink, ADAPTER_ADDRESS, Linkage ); + NbiDestroyAdapterAddress( AdapterAddress, NULL ); + } +#else + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (Device->Bind.LineInfo.MaximumPacketSize * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Device->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } +#endif _PNP_POWER + + NB_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); + +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (NbiMemoryTag[i].BytesAllocated != 0) { + NB_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, NbiMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + } + +} /* NbiDestroyDevice */ + diff --git a/private/ntos/tdi/isnp/nb/dirs b/private/ntos/tdi/isnp/nb/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/nb/driver.c b/private/ntos/tdi/isnp/nb/driver.c new file mode 100644 index 000000000..c4df8cbe6 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/driver.c @@ -0,0 +1,1794 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include +#include +#include + + +PDEVICE NbiDevice = NULL; +DEFINE_LOCK_STRUCTURE(NbiGlobalPoolInterlock); + +#ifdef RSRC_TIMEOUT_DBG + +ULONG NbiGlobalDebugResTimeout = 1; +LARGE_INTEGER NbiGlobalMaxResTimeout; + // the packet is allocated from ndis pool. +NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +UCHAR NbiGlobalDeathPacketHeader[100]; + +VOID +NbiInitDeathPacket() +{ + + NDIS_HANDLE PoolHandle; // poolhandle for sendpacket below when + NTSTATUS Status; + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &PoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + } else { + + if (NbiInitializeSendPacket( + NbiDevice, + PoolHandle, + &NbiGlobalDeathPacket, + NbiGlobalDeathPacketHeader, + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) != STATUS_SUCCESS) { + + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(PoolHandle); + } + } + +} +#endif //RSRC_TIMEOUT_DBG + +#if DBG + +ULONG NbiDebug = 0xffffffff; +ULONG NbiDebug2 = 0x00000000; +ULONG NbiMemoryDebug = 0x0002482c; + +UCHAR NbiTempDebugBuffer[150]; +UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +PUCHAR NbiDebugMemoryLoc = NbiDebugMemory[0]; +PUCHAR NbiDebugMemoryEnd = NbiDebugMemory[NB_MEMORY_LOG_SIZE]; +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (NbiTempDebugBuffer, 150); + ArgLen = vsprintf(NbiTempDebugBuffer,FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (NbiDebugMemoryLoc, 64); + RtlCopyMemory( NbiDebugMemoryLoc, NbiTempDebugBuffer, ArgLen ); + + NbiDebugMemoryLoc += 64; + if (NbiDebugMemoryLoc >= NbiDebugMemoryEnd) { + NbiDebugMemoryLoc = NbiDebugMemory[0]; + } + } + +} /* NbiDebugMemoryLog */ + + +DEFINE_LOCK_STRUCTURE(NbiMemoryInterlock); +MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// +DEFINE_LOCK_STRUCTURE(NbiGlobalInterlock); + + +#ifdef RASAUTODIAL +VOID +NbiAcdBind(); + +VOID +NbiAcdUnbind(); +#endif + +#ifdef NB_PACKET_LOG + +ULONG NbiPacketLogDebug = NB_PACKET_LOG_RCV_OTHER | NB_PACKET_LOG_SEND_OTHER; +USHORT NbiPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(NbiPacketLogLock); +NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +PNB_PACKET_LOG_ENTRY NbiPacketLogLoc = NbiPacketLog; +PNB_PACKET_LOG_ENTRY NbiPacketLogEnd = &NbiPacketLog[NB_PACKET_LOG_LENGTH]; + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PNB_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&NbiPacketLogLock, &LockHandle); + + PacketLog = NbiPacketLogLoc; + + ++NbiPacketLogLoc; + if (NbiPacketLogLoc >= NbiPacketLogEnd) { + NbiPacketLogLoc = NbiPacketLog; + } + *(UNALIGNED ULONG *)NbiPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&NbiPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(NB_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, Length); + } else { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* NbiLogPacket */ + +#endif // NB_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiFreeResources ( + IN PVOID Adapter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + + +// +// These two are used in various places in the driver. +// + +#if defined(_PNP_POWER) +IPX_LOCAL_TARGET BroadcastTarget = { {0}, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif _PNP_POWER + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +UCHAR NetbiosBroadcastName[16] = { '*', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +ULONG NbiFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the Netbios ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of Netbios's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("Netbios/IPX Transport"); + PDEVICE Device; + PIPX_HEADER IpxHeader; + CTELockHandle LockHandle; + + PCONFIG Config = NULL; + +#if 0 + DbgPrint ("NBI: FailLoad at %lx\n", &NbiFailLoad); + DbgBreakPoint(); + + if (NbiFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + NB_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&NbiGlobalInterlock); + CTEInitLock (&NbiMemoryInterlock); + { + UINT i; + for (i = 0; i < MEMORY_MAX; i++) { + NbiMemoryTag[i].Tag = i; + NbiMemoryTag[i].BytesAllocated = 0; + } + } +#endif +#ifdef NB_PACKET_LOG + CTEInitLock (&NbiPacketLogLock); +#endif + +#if defined(NB_OWN_PACKETS) + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + NB_DEBUG2 (DEVICE, ("ISN Netbios loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = NbiGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + PANIC (" Failed to initialize transport, ISN Netbios initialization failed.\n"); + return status; + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbiDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbiDispatchDeviceControl; + + DriverObject->DriverUnload = NbiUnload; + + + // + // Create the device object which exports our name. + // + + status = NbiCreateDevice (DriverObject, &Config->DeviceName, &Device); + + if (!NT_SUCCESS (status)) { + + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + NbiFreeConfiguration(Config); + return status; + } + + NbiDevice = Device; + + // + // Initialize the global pool interlock + // + CTEInitLock (&NbiGlobalPoolInterlock); + + + // + // Save the relevant configuration parameters. + // + + Device->AckDelayTime = (Config->Parameters[CONFIG_ACK_DELAY_TIME] / SHORT_TIMER_DELTA) + 1; + Device->AckWindow = Config->Parameters[CONFIG_ACK_WINDOW]; + Device->AckWindowThreshold = Config->Parameters[CONFIG_ACK_WINDOW_THRESHOLD]; + Device->EnablePiggyBackAck = Config->Parameters[CONFIG_ENABLE_PIGGYBACK_ACK]; + Device->Extensions = Config->Parameters[CONFIG_EXTENSIONS]; + Device->RcvWindowMax = Config->Parameters[CONFIG_RCV_WINDOW_MAX]; + Device->BroadcastCount = Config->Parameters[CONFIG_BROADCAST_COUNT]; + Device->BroadcastTimeout = Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500; + Device->ConnectionCount = Config->Parameters[CONFIG_CONNECTION_COUNT]; + Device->ConnectionTimeout = Config->Parameters[CONFIG_CONNECTION_TIMEOUT] * 500; + Device->InitPackets = Config->Parameters[CONFIG_INIT_PACKETS]; + Device->MaxPackets = Config->Parameters[CONFIG_MAX_PACKETS]; + Device->InitialRetransmissionTime = Config->Parameters[CONFIG_INIT_RETRANSMIT_TIME]; + Device->Internet = Config->Parameters[CONFIG_INTERNET]; + Device->KeepAliveCount = Config->Parameters[CONFIG_KEEP_ALIVE_COUNT]; + Device->KeepAliveTimeout = Config->Parameters[CONFIG_KEEP_ALIVE_TIMEOUT]; + Device->RetransmitMax = Config->Parameters[CONFIG_RETRANSMIT_MAX]; + Device->RouterMtu = Config->Parameters[CONFIG_ROUTER_MTU]; + + Device->FindNameTimeout = + ((Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500) + (FIND_NAME_GRANULARITY/2)) / + FIND_NAME_GRANULARITY; + + Device->MaxReceiveBuffers = 20; // BUGBUG: Make it configurable? + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + + NbiInitializeTimers (Device); +#endif _PNP_POWER + + // + // Now bind to IPX via the internal interface. + // + + status = NbiBind (Device, Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + +#ifdef RSRC_TIMEOUT_DBG + NbiInitDeathPacket(); + // NbiGlobalMaxResTimeout.QuadPart = 50; // 1*1000*10000; + NbiGlobalMaxResTimeout.QuadPart = 20*60*1000; + NbiGlobalMaxResTimeout.QuadPart *= 10000; +#endif // RSRC_TIMEOUT_DBG + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Create Hash Table to store netbios cache entries + // For server create a big table, for workstation a small one + // + + if ( MmIsThisAnNtAsSystem() ) { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_LARGE ); + } else { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_SMALL ); + } + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NB_FREE_LOCK(&Device->Lock, LockHandle); + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + + // + // Allocate our initial connectionless packet pool. + // + + NbiAllocateSendPool (Device); + + // + // Allocate our initial receive packet pool. + // + + NbiAllocateReceivePool (Device); + + // + // Allocate our initial receive buffer pool. + // + // +#if !defined(_PNP_POWER) + NbiAllocateReceiveBufferPool (Device); +#endif !_PNP_POWER + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == Device->State ) { + Device->State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#if !defined(_PNP_POWER) + // + // Start the timer system. + // + + NbiInitializeTimers (Device); +#endif !_PNP_POWER + + + // + // Fill in the default connnectionless header. + // + + IpxHeader = &Device->ConnectionlessHeader; + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = 0; + IpxHeader->PacketLength[1] = 0; + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = 0; + *(UNALIGNED ULONG *)(IpxHeader->DestinationNetwork) = 0; + RtlCopyMemory(IpxHeader->DestinationNode, BroadcastAddress, 6); + IpxHeader->DestinationSocket = NB_SOCKET; + IpxHeader->SourceSocket = NB_SOCKET; +#if !defined(_PNP_POWER) + RtlCopyMemory(IpxHeader->SourceNetwork, Device->Bind.Network, 4); + RtlCopyMemory(IpxHeader->SourceNode, Device->Bind.Node, 6); + + Device->State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + + NbiFreeConfiguration(Config); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbiAcdBind(); +#endif + + return STATUS_SUCCESS; + +} /* DriverEntry */ + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has Netbios open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbiAcdUnbind(); +#endif + + Device->State = DEVICE_STATE_STOPPING; + + // + // Free the cache of netbios names. + // + + DestroyNetbiosCacheTable( Device->NameCache ); + + // + // Cancel the long timer. + // + + if (CTEStopTimer (&Device->LongTimer)) { + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + + // + // Unbind from the IPX driver. + // + + NbiUnbind (Device); + + // + // This event will get set when the reference count + // drops to 0. + // + + KeInitializeEvent( + &Device->UnloadEvent, + NotificationEvent, + FALSE); + Device->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + NbiDereferenceDevice (Device, DREF_LOADED); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &Device->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} /* NbiUnload */ + + +VOID +NbiFreeResources ( + IN PVOID Adapter + ) +/*++ + +Routine Description: + + This routine is called by Netbios to clean up the data structures associated + with a given Device. When this routine exits, the Device + should be deleted as it no longer has any assocaited resources. + +Arguments: + + Device - Pointer to the Device we wish to clean up. + +Return Value: + + None. + +--*/ +{ +#if 0 + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; +#endif + + +#if 0 + // + // Clean up packet pool. + // + + while ( Device->PacketPool.Next != NULL ) { + s = PopEntryList( &Device->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbiDeallocateSendPacket (Device, packet); + } + + // + // Clean up receive packet pool + // + + while ( Device->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&Device->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbiDeallocateReceivePacket (Device, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( Device->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &Device->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbiDeallocateReceiveBuffer (Device, BufferTag); + } + +#endif + +} /* NbiFreeResources */ + + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPXNB device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST Request; + UINT i; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + +#if !defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenConnection (Device, Request); + break; + } + + } else { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier); + ++Device->ControlChannelIdentifier; + if (Device->ControlChannelIdentifier == 0) { + Device->ControlChannelIdentifier = 1; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONTROL_CHANNEL_FILE; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbiCloseAddressFile (Device, Request); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // We don't call VerifyConnection because the I/O + // system should only give us one close and the file + // object should be valid. This helps avoid a window + // where two threads call HandleConnectionZero at the + // same time. + // + + Status = NbiCloseConnection (Device, Request); + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbiStopAddressFile (AddressFile, AddressFile->Address); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection(Connection); + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + // + // This call releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_CONNECTION + NB_LOCK_HANDLE_ARG (LockHandle1)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + Status = STATUS_SUCCESS; + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchOpenClose */ + + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = NbiDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + + return Status; + +} /* NbiDeviceControl */ + + +NB_TDI_DISPATCH_ROUTINE NbiDispatchInternalTable[] = { + NbiTdiAssociateAddress, + NbiTdiDisassociateAddress, + NbiTdiConnect, + NbiTdiListen, + NbiTdiAccept, + NbiTdiDisconnect, + NbiTdiSend, + NbiTdiReceive, + NbiTdiSendDatagram, + NbiTdiReceiveDatagram, + NbiTdiSetEventHandler, + NbiTdiQueryInformation, + NbiTdiSetInformation, + NbiTdiAction + }; + + +NTSTATUS +NbiDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request; + UCHAR MinorFunction; + + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + + // + // Branch to the appropriate request handler. + // + + MinorFunction = REQUEST_MINOR_FUNCTION(Request) - 1; + + if (MinorFunction <= (TDI_ACTION-1)) { + + Status = (*NbiDispatchInternalTable[MinorFunction]) ( + Device, + Request); + + } else { + + NB_DEBUG (DRIVER, ("Unsupported minor code %d\n", MinorFunction+1)); + if ((MinorFunction+1) == TDI_DISCONNECT) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchInternal */ + + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = NbiDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + NbiPrint1 ("Nbi: Could not allocate %d: limit\n", BytesNeeded); + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' IBN'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + NbiPrint1("Nbi: Could not allocate %d: no pool\n", BytesNeeded); + + if (ChargeDevice) { + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; + +} /* NbipAllocateMemory */ + + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* NbipFreeMemory */ + +#if DBG + + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = NbipAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &NbiMemoryInterlock); + } + + return Memory; + +} /* NbipAllocateTaggedMemory */ + + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &NbiMemoryInterlock); + + NbipFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* NbipFreeTaggedMemory */ + +#endif + + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + INT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteResourceErrorLog */ + + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[8] = L"NwlnkNb"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (Device->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteGeneralErrorLog */ + + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + Device - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteOidErrorLog */ + diff --git a/private/ntos/tdi/isnp/nb/event.c b/private/ntos/tdi/isnp/nb/event.c new file mode 100644 index 000000000..f6cff7105 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/event.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +PVOID TdiDefaultHandlers[6] = { + TdiDefaultConnectHandler, + TdiDefaultDisconnectHandler, + TdiDefaultErrorHandler, + TdiDefaultReceiveHandler, + TdiDefaultRcvDatagramHandler, + TdiDefaultRcvExpeditedHandler + }; + + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Device - The netbios device object. + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + UINT EventType; + + UNREFERENCED_PARAMETER (Device); + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + EventType = (UINT)(Parameters->EventType); + + if (Parameters->EventType > TDI_EVENT_RECEIVE_EXPEDITED) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + if (Parameters->EventHandler == NULL) { + AddressFile->RegisteredHandler[EventType] = FALSE; + AddressFile->Handlers[EventType] = TdiDefaultHandlers[EventType]; + AddressFile->HandlerContexts[EventType] = NULL; + } else { + AddressFile->Handlers[EventType] = Parameters->EventHandler; + AddressFile->HandlerContexts[EventType] = Parameters->EventContext; + AddressFile->RegisteredHandler[EventType] = TRUE; + } + + } + + NB_FREE_LOCK (&AddressFile->Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + return Status; + +} /* NbiTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isnp/nb/frame.c b/private/ntos/tdi/isnp/nb/frame.c new file mode 100644 index 000000000..bbc14fd56 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/frame.c @@ -0,0 +1,1095 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + frame.c + +Abstract: + + This module contains code which creates and sends various + types of frames. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if defined(_PNP_POWER) + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + ReqFrame - If specified, the request frame for which this + response is being sent. The reqframe contains the + destination ipx address and the netbios name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(ReqFrame)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)ReqFrame->IpxHeader.SourceNetwork, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : ReqFrame->NameFrame.Name, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + LocalTarget = &BroadcastTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#else + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + DestAddress - If specified, the destination IPX address to + use for the send (if not, it will be broadcast on net 0). + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.CurrentNicId = 1; + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } else { + Reserved->u.SR_NF.CurrentNicId = 0; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(DestAddress)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)DestAddress, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : NetbiosBroadcastName, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + TempLocalTarget.NicId = 1; // BUGBUG: What if 1 isn't valid? + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#endif _PNP_POWER + + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + sizeof(NB_SESSION_INIT)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Device->Extensions) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = 0xffff; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = sizeof(NB_SESSION_INIT); + Header->Session.Offset = 0; + Header->Session.DataLength = sizeof(NB_SESSION_INIT); + Header->Session.ReceiveSequence = 0; + if (Device->Extensions) { + Header->Session.ReceiveSequenceMax = 1; // low estimate for the moment + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + sizeof(NB_SESSION_INIT), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitialize */ + + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize ack + frame for the specified connection. If extra data was + specified in the session initialize frame it is echoed + back to the remote. + +Arguments: + + Connection - The connection on which the frame is sent. + + ExtraData - Any extra data (after the SESSION_INIT buffer) + in the frame. + + ExtraDataLength - THe length of the extra data. + + LockHandle - If specified, indicates the connection lock + is held and should be released. This is for cases + where the ExtraData is in memory which may be freed + once the connection lock is released. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + ULONG SessionInitBufferLength; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitBufferLength = sizeof(NB_SESSION_INIT) + ExtraDataLength; + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + // + // Save the extra data, now we can free the lock. + // + + if (ExtraDataLength != 0) { + RtlCopyMemory (SessionInitMemory+1, ExtraData, ExtraDataLength); + } + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + SessionInitBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Connection->NewNetbios) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; + } + CTEAssert (Connection->CurrentSend.SendSequence == 0); + CTEAssert (Connection->ReceiveSequence == 1); + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = (USHORT)SessionInitBufferLength; + Header->Session.Offset = 0; + Header->Session.DataLength = (USHORT)SessionInitBufferLength; + Header->Session.ReceiveSequence = 1; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + SessionInitBufferLength, + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitAck */ + + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ) + +/*++ + +Routine Description: + + This routine allocates and sends a data ack frame. + + THIS ROUTINE IS CALLED WITH THE LOCK HANDLE HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection on which the frame is sent. + + AckType - Indicates if this is a query to the remote, + a response to a received probe, or a request to resend. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, try for the connection + // packet. If that's not available, that's OK since data + // acks are connectionless anyway. + // + + if (s == NULL) { + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + } else { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.PacketLength = sizeof(NB_CONNECTION); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + switch (AckType) { + case NbiAckQuery: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_SEND_ACK; break; + case NbiAckResponse: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; break; + case NbiAckResend: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_RESEND; break; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentSend.MessageOffset; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + NbiReferenceConnectionSync(Connection, CREF_FRAME); + + // + // Set this so we will accept a probe from a remote without + // the send ack bit on. However if we receive such a request + // we turn this flag off until we get something else from the + // remote. + // + + Connection->IgnoreNextDosProbe = FALSE; + + Connection->ReceivesWithoutAck = 0; + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + +} /* NbiSendDataAck */ + + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. We don't advance the + // send pointer, since it is the last frame of the session + // and we want it to stay the same in the case of resends. + // + + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + Header->Session.DataStreamType = NB_CMD_SESSION_END; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + NbiReferenceConnection (Connection, CREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEnd */ + + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame. Generally it is sent on a connection but we + are not tied to that, to allow us to respond to + session ends from unknown remotes. + +Arguments: + + RemoteAddress - The remote IPX address. + + LocalTarget - The local target of the remote. + + SessionEnd - The received session end frame. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = NULL; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, (PVOID)RemoteAddress, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->Session.ConnectionControlFlag = 0x00; + Header->Session.DataStreamType = NB_CMD_SESSION_END_ACK; + Header->Session.SourceConnectionId = SessionEnd->DestConnectionId; + Header->Session.DestConnectionId = SessionEnd->SourceConnectionId; + Header->Session.SendSequence = SessionEnd->ReceiveSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + if (SessionEnd->BytesReceived != 0) { // BUGBUG: Will this detect new netbios? + Header->Session.ReceiveSequence = SessionEnd->SendSequence + 1; + Header->Session.ReceiveSequenceMax = SessionEnd->SendSequence + 3; + } else { + Header->Session.ReceiveSequence = SessionEnd->SendSequence; + Header->Session.BytesReceived = 0; + } + + NbiReferenceDevice (Device, DREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/isnnb.h b/private/ntos/tdi/isnp/nb/isnnb.h new file mode 100644 index 000000000..2d142e346 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/isnnb.h @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnnb.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#define NB_MAXIMUM_MAC 40 + +#define NB_SOCKET 0x5504 + +#if defined(_PNP_POWER) +#define NB_NETBIOS_NAME_SIZE 16 + +#define LOCK_ACQUIRED TRUE +#define LOCK_NOT_ACQUIRED FALSE +#endif _PNP_POWER + +// +// Defined granularity of find name timeouts in milliseconds -- +// we make this the same as the spec'ed RIP gap to avoid +// flooding routers. +// + +#define FIND_NAME_GRANULARITY 55 + + +// +// Defines the number of milliseconds between expirations of the +// short and long timers. +// + +#define MILLISECONDS 10000 // number of NT time units in one + +#define SHORT_TIMER_DELTA 100 +#define LONG_TIMER_DELTA 2000 + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#include + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of the Netbios header for name frames. +// + +typedef struct _NB_NAME_FRAME { + union { + struct { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + }; + UCHAR RoutingInfo[32]; + }; + UCHAR NameTypeFlag; + UCHAR DataStreamType2; + UCHAR Name[16]; +} NB_NAME_FRAME, *PNB_NAME_FRAME; + +// +// Definition of the Netbios header for directed datagrams. +// + +typedef struct _NB_DATAGRAM { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR SourceName[16]; + UCHAR DestinationName[16]; +} NB_DATAGRAM, *PNB_DATAGRAM; + +// +// Definition of the Netbios header for a status query. +// + +typedef struct _NB_STATUS_QUERY { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR Padding[14]; +} NB_STATUS_QUERY, *PNB_STATUS_QUERY; + +// +// Definition of the Netbios header for a status response +// (this does not include the status buffer itself). +// + +typedef struct _NB_STATUS_RESPONSE { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; +} NB_STATUS_RESPONSE, *PNB_STATUS_RESPONSE; + + +// +// Definition of the general Netbios connectionless header. +// + +typedef struct _NB_CONNECTIONLESS { + IPX_HEADER IpxHeader; + union { + NB_NAME_FRAME NameFrame; + NB_DATAGRAM Datagram; + NB_STATUS_QUERY StatusQuery; + NB_STATUS_RESPONSE StatusResponse; + }; +} NB_CONNECTIONLESS, *PNB_CONNECTIONLESS; + + +// +// Definition of the Netbios session frame. +// + +typedef struct _NB_SESSION { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT SourceConnectionId; + USHORT DestConnectionId; + USHORT SendSequence; + USHORT TotalDataLength; + USHORT Offset; + USHORT DataLength; + USHORT ReceiveSequence; + union { + USHORT BytesReceived; + USHORT ReceiveSequenceMax; + }; +} NB_SESSION, *PNB_SESSION; + + +// +// Definition of the extra fields in a Netbios +// session frame for session init and session init +// ack. +// + +typedef struct _NB_SESSION_INIT { + UCHAR SourceName[16]; + UCHAR DestinationName[16]; + USHORT MaximumDataSize; + USHORT MaximumPacketTime; + USHORT StartTripTime; +} NB_SESSION_INIT, *PNB_SESSION_INIT; + + +// +// Definition of the general Netbios connection-oriented header. +// + +typedef struct _NB_CONNECTION { + IPX_HEADER IpxHeader; + NB_SESSION Session; +} NB_CONNECTION, *PNB_CONNECTION; + + +// +// Definition of a Netbios packet. +// + +typedef union _NB_FRAME { + NB_CONNECTIONLESS Connectionless; + NB_CONNECTION Connection; +} NB_FRAME, *PNB_FRAME; + +#include + + +// +// Definitions for the DataStreamType field, with the +// format used shown in the comment afterward. +// + +#define NB_CMD_FIND_NAME 0x01 // NAME_FRAME +#define NB_CMD_NAME_RECOGNIZED 0x02 // NAME_FRAME +#define NB_CMD_ADD_NAME 0x03 // NAME_FRAME +#define NB_CMD_NAME_IN_USE 0x04 // NAME_FRAME +#define NB_CMD_DELETE_NAME 0x05 // NAME_FRAME +#define NB_CMD_SESSION_DATA 0x06 // SESSION +#define NB_CMD_SESSION_END 0x07 // SESSION +#define NB_CMD_SESSION_END_ACK 0x08 // SESSION +#define NB_CMD_STATUS_QUERY 0x09 // STATUS_QUERY +#define NB_CMD_STATUS_RESPONSE 0x0a // STATUS_RESPONSE +#define NB_CMD_DATAGRAM 0x0b // DATAGRAM +#define NB_CMD_BROADCAST_DATAGRAM 0x0c // BROADCAST_DATAGRAM + +#ifdef RSRC_TIMEOUT_DBG +#define NB_CMD_DEATH_PACKET 0x99 // +#endif // RSRC_TIMEOUT_DBG + +// +// Bit values in the NameTypeFlag of NB_NAME_FRAME frames. +// + +#define NB_NAME_UNIQUE 0x00 +#define NB_NAME_GROUP 0x80 +#define NB_NAME_USED 0x40 +#define NB_NAME_REGISTERED 0x04 +#define NB_NAME_DUPLICATED 0x02 +#define NB_NAME_DEREGISTERED 0x01 + +// +// Bit values in the ConnectionControlFlag. +// + +#define NB_CONTROL_SYSTEM 0x80 +#define NB_CONTROL_SEND_ACK 0x40 +#define NB_CONTROL_ATTENTION 0x20 +#define NB_CONTROL_EOM 0x10 +#define NB_CONTROL_RESEND 0x08 +#define NB_CONTROL_NEW_NB 0x01 + + + +#define NB_DEVICE_SIGNATURE 0x1401 +#if defined(_PNP_POWER) +#define NB_ADAPTER_ADDRESS_SIGNATURE 0x1403 +#endif _PNP_POWER +#define NB_ADDRESS_SIGNATURE 0x1404 +#define NB_ADDRESSFILE_SIGNATURE 0x1405 +#define NB_CONNECTION_SIGNATURE 0x1406 + + +// +// Useful in various places. +// +#if defined(_PNP_POWER) +extern IPX_LOCAL_TARGET BroadcastTarget; +#endif _PNP_POWER +extern UCHAR BroadcastAddress[6]; +extern UCHAR NetbiosBroadcastName[16]; + + +// +// Contains the default handler for each of the TDI event types +// that are supported. +// + +extern PVOID TdiDefaultHandlers[6]; + + +// +// Define a structure that can track lock acquire/release. +// + +typedef struct _NB_LOCK { + CTELock Lock; +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif +} NB_LOCK, *PNB_LOCK; + + + +#if DBG + +extern ULONG NbiDebug; +extern ULONG NbiDebug2; +extern ULONG NbiMemoryDebug; + +#define NB_MEMORY_LOG_SIZE 128 +extern UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +extern PUCHAR NbiDebugMemoryLoc; +extern PUCHAR NbiDebugMemoryEnd; + +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define NB_DEBUG(_Flag, _Print) { \ + if (NbiDebug & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#define NB_DEBUG2(_Flag, _Print) { \ + if (NbiDebug2 & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#else + +#define NB_DEBUG(_Flag, _Print) +#define NB_DEBUG2(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; +typedef struct _REQUEST_LIST_HEAD { + PREQUEST Head; // list is empty if this is NULL + PREQUEST Tail; // undefined if the list is empty. +} REQUEST_LIST_HEAD, *PREQUEST_LIST_HEAD; + + +// +// PREQUEST +// NbiAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define NbiAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// NbiFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define NbiFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// REQUEST_SINGLE_LINKAGE( +// IN PREQUEST Request +// ); +// +// Used to access a single list linkage field in the request. +// + +#define REQUEST_SINGLE_LINKAGE(_Request) \ + (*((PREQUEST *)&((_Request)->Tail.Overlay.ListEntry.Flink))) + + +// +// ULONG +// REQUEST_REFCOUNT( +// IN PREQUEST Request +// ); +// +// Used to access a field in the request which can be used for +// the reference count, as long as it is on a REQUEST_LIST. +// + +#define REQUEST_REFCOUNT(_Request) \ + (*((PULONG)&((_Request)->Tail.Overlay.ListEntry.Blink))) + + +// +// VOID +// REQUEST_LIST_INSERT_TAIL( +// IN PREQUEST_LIST_HEAD Head, +// IN PREQUEST Entry +// ); +// +// Inserts a request into a single list linkage queue. +// + +#define REQUEST_LIST_INSERT_TAIL(_Head,_Entry) { \ + if ((_Head)->Head == NULL) { \ + (_Head)->Head = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } else { \ + REQUEST_SINGLE_LINKAGE((_Head)->Tail) = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } \ +} + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// NbiCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define NbiCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +// +// some utility macros. + +// Minimum of two +// +#define NB_MIN( _a , _b ) ( ( (_a) < (_b) ) ? (_a) : (_b) ) + +// +// Swap the _s1 and _s2 of Type _T +// + +#define NB_SWAP(_s1, _s2, _T) { \ + _T _temp; \ + _temp = (_s1); \ + (_s1) = (_s2); \ + (_s2) = _temp; \ +} + +#define NB_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + +// +// Define our own spinlock routines. +// + +#if DBG + +#define NB_GET_LOCK(_Lock, _LockHandle) { \ + CTEGetLock(&(_Lock)->Lock, _LockHandle); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK(_Lock, _LockHandle) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + CTEFreeLock(&(_Lock)->Lock, _LockHandle); \ +} + +#define NB_GET_LOCK_DPC(_Lock) { \ + ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK_DPC(_Lock) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock); \ +} + +#else + +#define NB_GET_LOCK(_Lock, _LockHandle) CTEGetLock(&(_Lock)->Lock, _LockHandle) +#define NB_FREE_LOCK(_Lock, _LockHandle) CTEFreeLock(&(_Lock)->Lock, _LockHandle) +#define NB_GET_LOCK_DPC(_Lock) ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock) +#define NB_FREE_LOCK_DPC(_Lock) ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock) + +#endif + + +#define NB_GET_CANCEL_LOCK( _LockHandle ) IoAcquireCancelSpinLock( _LockHandle ) + +#define NB_FREE_CANCEL_LOCK( _LockHandle ) IoReleaseCancelSpinLock( _LockHandle ) + + +// +// Routines to optimize for a uni-processor environment. +// + + +#define NB_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define NB_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define NB_ADD_ULONG(_Pulong, _Ulong, _Lock) ExInterlockedAddUlong(_Pulong, _Ulong, &(_Lock)->Lock) + +#define NB_DEFINE_SYNC_CONTEXT(_SyncContext) +#define NB_BEGIN_SYNC(_SyncContext) +#define NB_END_SYNC(_SyncContext) + +#define NB_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; + +// +// BUGBUG: Make these be NB_XXX_LOCK_DPC calls -- then the definitions +// of the NB_SYNC_XXX_LOCK calls can be changed to not need _LockHandle +// and many of the functions won't need that as a parameter. +// + +#define NB_SYNC_GET_LOCK(_Lock, _LockHandle) NB_GET_LOCK(_Lock, _LockHandle) +#define NB_SYNC_FREE_LOCK(_Lock, _LockHandle) NB_FREE_LOCK(_Lock, _LockHandle) + +#define NB_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, &(_Lock)->Lock) +#define NB_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define NB_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, &(_Lock)->Lock) +#define NB_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntryList(_Queue, &(_Lock)->Lock) +#define NB_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntryList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_LOCK_HANDLE_PARAM(_LockHandle) , IN CTELockHandle _LockHandle +#define NB_LOCK_HANDLE_ARG(_LockHandle) , (_LockHandle) + +#define NB_SYNC_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + + +// +// This macro adds a ULONG to a LARGE_INTEGER (should be +// called with a spinlock held). +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define NB_DEBUG_DEVICE 0x00000001 +#define NB_DEBUG_ADDRESS 0x00000004 +#define NB_DEBUG_SEND 0x00000008 +#define NB_DEBUG_RECEIVE 0x00000020 +#define NB_DEBUG_CONFIG 0x00000040 +#define NB_DEBUG_PACKET 0x00000080 +#define NB_DEBUG_BIND 0x00000200 +#define NB_DEBUG_ADDRESS_FRAME 0x00000400 +#define NB_DEBUG_CONNECTION 0x00000800 +#define NB_DEBUG_QUERY 0x00001000 +#define NB_DEBUG_DRIVER 0x00002000 +#define NB_DEBUG_CACHE 0x00004000 +#define NB_DEBUG_DATAGRAM 0x00008000 +#define NB_DEBUG_TIMER 0x00010000 +#define NB_DEBUG_SEND_WINDOW 0x00020000 + + + +// +// NB_GET_NBHDR_BUFF - gets the nb header in the packet. It is always the +// second buffer. +// +#define NB_GET_NBHDR_BUFF(Packet) (NDIS_BUFFER_LINKAGE((Packet)->Private.Head)) + + diff --git a/private/ntos/tdi/isnp/nb/mp/makefile b/private/ntos/tdi/isnp/nb/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/mp/sources b/private/ntos/tdi/isnp/nb/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/nb/nbcount/makefile b/private/ntos/tdi/isnp/nb/nbcount/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.c b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c new file mode 100644 index 000000000..74a656f16 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c @@ -0,0 +1,177 @@ +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Compatible Source Routing Daemon for Windows NT +* +* Module: ipx/route/ipxroute.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "errno.h" +#include "tdi.h" +#include "isnkrnl.h" + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + +HANDLE isnnbfd; +wchar_t isnnbname[] = L"\\Device\\NwlnkNb"; +char pgmname[] = "NBCOUNT"; + +/** **/ + +#define INVALID_HANDLE (HANDLE)(-1) + +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen); + +/*page************************************************************* + m a i n + + This is the main routine that gets executed when a NET START + happens. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void _CRTAPI1 main(int argc, char **argv) +{ + UNICODE_STRING FileString; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + NB_ACTION_GET_COUNTS GetCounts; + int rc; + int i; + + /** Open the nwlnknb driver **/ + + RtlInitUnicodeString (&FileString, isnnbname); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnnbfd, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + isnnbfd = INVALID_HANDLE; + printf("Could not open transport\n"); + } + + if (isnnbfd == INVALID_HANDLE) { + exit(1); + } + + rc = do_isnnbioctl(isnnbfd, (I_MIPX | 351), (char *)&GetCounts, sizeof(NB_ACTION_GET_COUNTS)); + if (rc == 0) { + + printf("NB NIC count: %d\n", GetCounts.MaximumNicId); + for (i = 1; i <= GetCounts.MaximumNicId; i++) { + printf("NIC %d: %d sessions\n", i, GetCounts.NicIdCounts[i]); + } + } +} + + +/*page*************************************************************** + d o _ i s n i p x i o c t l + + Do the equivalent of a stream ioctl to isnnb + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen) +{ + NTSTATUS Status; + UCHAR buffer[300]; + PNWLINK_ACTION action; + IO_STATUS_BLOCK IoStatusBlock; + int rc; + + /** Fill out the structure **/ + + action = (PNWLINK_ACTION)buffer; + + action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; + action->OptionType = NWLINK_OPTION_CONTROL; + action->BufferLength = sizeof(ULONG) + dlen; + action->Option = cmd; + RtlMoveMemory(action->Data, datap, dlen); + + /** Issue the ioctl **/ + + Status = NtDeviceIoControlFile( + fd, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_TDI_ACTION, + NULL, + 0, + action, + FIELD_OFFSET(NWLINK_ACTION,Data) + dlen); + + if (Status != STATUS_SUCCESS) { + if (Status == STATUS_INVALID_PARAMETER) { + rc = ERANGE; + } else { + rc = EINVAL; + } + } else { + if (dlen > 0) { + RtlMoveMemory (datap, action->Data, dlen); + } + rc = 0; + } + + return rc; + +} + diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc new file mode 100644 index 000000000..ada219b24 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLink Netbios Session Count Application" +#define VER_INTERNALNAME_STR "nbcount.exe" +#define VER_ORIGINALFILENAME_STR "nbcount.exe" + +#include "common.ver" diff --git a/private/ntos/tdi/isnp/nb/nbcount/sources b/private/ntos/tdi/isnp/nb/nbcount/sources new file mode 100644 index 000000000..f9dfe3561 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc. + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=nbcount + +TARGETNAME=nbcount +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=UMAPPL_NOLIB + +USE_CRTDLL=1 + +C_DEFINES=$(C_DEFINES) + +!IF 0 +INCLUDES=..\h;..\..\..\..\..\inc;..\..\..\..\inc;..\..\..\inc +!ELSE +INCLUDES=..\h;$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;$(BASEDIR)\private\ntos\streams\inc +!ENDIF + +SOURCES= nbcount.c nbcount.rc + +UMTYPE=console +UMAPPL=$(TARGETNAME) +UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + diff --git a/private/ntos/tdi/isnp/nb/nbiprocs.h b/private/ntos/tdi/isnp/nb/nbiprocs.h new file mode 100644 index 000000000..dff399019 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbiprocs.h @@ -0,0 +1,1530 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbiprocs.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbiPrint0(fmt) DbgPrint(fmt) +#define NbiPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbiPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbiPrint0(fmt) +#define NbiPrint1(fmt,v0) +#define NbiPrint2(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define NB_PACKET_LOG 1 +#endif + +#ifdef NB_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _NB_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER NbiHeader; + UCHAR Data[14]; +} NB_PACKET_LOG_ENTRY, *PNB_PACKET_LOG_ENTRY; + +#define NB_PACKET_LOG_LENGTH 128 +extern ULONG NbiPacketLogDebug; +extern USHORT NbiPacketLogSocket; +EXTERNAL_LOCK(NbiPacketLogLock); +extern NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogLoc; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogEnd; + +// +// Bit fields in NbiPacketLogDebug +// + +#define NB_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to NbiPacketLogSocket +#define NB_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-NB) + +#define NB_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from NbiPacketLogSocket + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (NbiPacketLogDebug & (_Bit)) + +#else // NB_PACKET_LOG + +#define NbiLogPacket(_MacHeader,_Length,_NbiHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // NB_PACKET_LOG + + +#if DBG + +#define NbiReferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefDevice (_Device) + +#define NbiDereferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefDevice (_Device) + + +#define NbiReferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddress (_Address) + +#define NbiReferenceAddressLock(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressLock (_Address) + +#define NbiDereferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFile (_AddressFile) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFileLock (_AddressFile) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddressFile (_AddressFile) + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + + +#define NbiReferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnection (_Connection) + +#define NbiReferenceConnectionLock(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionLock (_Connection) + +#define NbiReferenceConnectionSync(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionSync (_Connection) + +#define NbiDereferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefConnection (_Connection) + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + +#else // DBG + +#define NbiReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define NbiDereferenceDevice(_Device, _Type) \ + NbiDerefDevice (_Device) + + + +#define NbiReferenceAddress(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiDereferenceAddress(_Address, _Type) \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + if ( !InterlockedDecrement(&(_AddressFile)->ReferenceCount )) { \ + NbiDestroyAddressFile (_AddressFile); \ + } + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + + +#define NbiReferenceConnection(_Connection, _Type) { \ + (VOID)ExInterlockedAddUlong( \ + &(_Connection)->ReferenceCount, \ + 1, \ + &(_Connection)->DeviceLock->Lock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionLock(_Connection, _Type) { \ + ++(_Connection)->ReferenceCount; \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionSync(_Connection, _Type) { \ + (VOID)NB_ADD_ULONG( \ + &(_Connection)->ReferenceCount, \ + 1, \ + (_Connection)->DeviceLock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiDereferenceConnection(_Connection, _Type) { \ + CTELockHandle _LockHandle; \ + NB_GET_LOCK( (_Connection)->DeviceLock, &_LockHandle ); \ + if ( !(--(_Connection)->ReferenceCount) ) { \ + (_Connection)->ThreadsInHandleConnectionZero++; \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + NbiHandleConnectionZero (_Connection); \ + } else { \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + } \ +} + + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// Definition of the callback routine where an NdisTransferData +// call is not needed. +// + +typedef VOID +(*NB_CALLBACK_NO_TRANSFER) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + + +// +// This routine compares two node addresses. +// + +#define NB_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define NB_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + + +// +// Definition of the routine to handler a particular minor +// code for an IOCTL_MJ_INTERNAL_DEVICE_CONTROL IRP. +// + +typedef NTSTATUS +(*NB_TDI_DISPATCH_ROUTINE) ( + IN PDEVICE Device, + IN PREQUEST Request + ); + + + +// +// Routines in action.c +// + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in address.c +// + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ); + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStartRegistration( + IN PADDRESS Address + ); + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ); + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +NbiRefAddress( + IN PADDRESS Address + ); + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +NbiDerefAddress( + IN PADDRESS Address + ); + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +#endif + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if !defined(_PNP_POWER) +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); +#endif !_PNP_POWER + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ); + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ); + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + +#if defined(_PNP_POWER) +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ); + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ); + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ); +#endif _PNP_POWER + + +// +// Routines in bind.c +// + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ); + +VOID +NbiUnbind( + IN PDEVICE Device + ); + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ); + +VOID +NbiLineDown( + IN USHORT NicId + ); + + +// +// Routines in cache.c +// + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +); + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ); + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ); + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ); + +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ); + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ); + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ); + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ); + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +// +// Routines in connect.c +// + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ); + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ); + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ); + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ); + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnectionSync( + IN PCONNECTION Connection + ); +#endif + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ); + + +// +// Routines in datagram.c +// + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ); + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ); + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ); + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in device.c +// + +VOID +NbiRefDevice( + IN PDEVICE Device + ); + +VOID +NbiDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ); + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + + +// +// Routines in event.c +// + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in frame.c +// + +VOID +NbiSendNameFrame( + IN PADDRESS Address, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, +#if defined(_PNP_POWER) + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL +#else + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL +#endif _PNP_POWER + ); + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ); + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ); + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ); + + +// +// Routines in packet.c +// + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ); + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ); + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ); + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ); + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ); + +#if defined(_PNP_POWER) +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ); + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ); + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ); + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); +#else +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ); +#endif _PNP_POWER + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ); + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ); + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ); + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ); + + +// +// Routines in query.c +// + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ); + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ); + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ); + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + + +// +// Routines in receive.c +// + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiReceiveComplete( + IN USHORT NicId + ); + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in send.c +// + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ); + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ); + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ); + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ); + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ); + + +// +// Routines in session.c +// + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + +// +// Routines in timer.c +// + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ); + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ); + +#else + +#define NbiStopRetransmit(_Connection) \ + (_Connection)->Retransmit = 0; + +#define NbiStopWatchdog(_Connection) \ + (_Connection)->Watchdog = 0; + +#endif + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ); + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ); + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ); + diff --git a/private/ntos/tdi/isnp/nb/nbitypes.h b/private/ntos/tdi/isnp/nb/nbitypes.h new file mode 100644 index 000000000..604df5fb5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbitypes.h @@ -0,0 +1,1511 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbitypes.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// For find name requests, defines the current status (SR_FN.Status). +// + +typedef enum { + FNStatusNoResponse, // no response has been received + FNStatusResponseUnique, // response received, is a unique name + FNStatusResponseGroup // response received, is a group name +}; + +// +// Defines the results we can get from sending a series of find +// names to locate a netbios name. +// + +typedef enum _NETBIOS_NAME_RESULT { + NetbiosNameFound, // name was located + NetbiosNameNotFoundNormal, // name not found, no response received + NetbiosNameNotFoundWanDown // name not found, all lines were down +} NETBIOS_NAME_RESULT, *PNETBIOS_NAME_RESULT; + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _NB_SEND_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN SendInProgress; // used in an NdisSend + UCHAR Type; // what to do on completion + BOOLEAN OwnedByConnection; // if this is a connection's one packet +#if defined(_PNP_POWER) + PVOID Reserved[SEND_RESERVED_COMMON_SIZE]; // used by ipx for even-padding and local target etc. +#else + PVOID Reserved[2]; // used by ipx for even-padding +#endif _PNP_POWER + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY WaitLinkage; // when waiting on other queues +#ifdef NB_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + union { + struct { + UCHAR NetbiosName[16]; // name being searched for + UCHAR StatusAndSentOnUpLine; // low nibble: look at FNStatusXXX enum + // high nibble: TRUE if while sending, found lan or up wan line + UCHAR RetryCount; // number of times sent + USHORT SendTime; // based on Device->FindNameTime +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // current nic id it is being sent on + USHORT MaximumNicId; // highest one it will be sent on +#endif !_PNP_POWER + struct _NETBIOS_CACHE * NewCache; // new cache entry for group names + } SR_FN; + struct { + struct _ADDRESS * Address; // that owns this packet, if one does + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // non-zero for frames that go to all +#endif !_PNP_POWER + UCHAR NameTypeFlag; // save these two values for frames + UCHAR DataStreamType; // that need to be sent to all nic id's + } SR_NF; + struct { + PREQUEST DatagramRequest; // holds the passed-in request + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; // will be -1 for broadcast + struct _ADDRESS_FILE * AddressFile; // that the datagram was sent on + struct _NETBIOS_CACHE * Cache; // how to route to the netbios address + ULONG CurrentNetwork; // within the cache entry + } SR_DG; + struct { + struct _CONNECTION * Connection; // that this frame was sent on. + PREQUEST Request; // that this frame was sent for. + ULONG PacketLength; // total packet length. + BOOLEAN NoNdisBuffer; // none allocate for send + } SR_CO; + struct { + ULONG ActualBufferLength; // real length of allocated buffer. + } SR_AS; + } u; + PUCHAR Header; // points to the MAC/IPX/NB header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header +} NB_SEND_RESERVED, *PNB_SEND_RESERVED; + +// +// Values for Type. +// + +#define SEND_TYPE_NAME_FRAME 1 +#define SEND_TYPE_SESSION_INIT 2 +#define SEND_TYPE_FIND_NAME 3 +#define SEND_TYPE_DATAGRAM 4 +#define SEND_TYPE_SESSION_NO_DATA 5 +#define SEND_TYPE_SESSION_DATA 6 +#define SEND_TYPE_STATUS_QUERY 7 +#define SEND_TYPE_STATUS_RESPONSE 8 + +#ifdef RSRC_TIMEOUT_DBG +#define SEND_TYPE_DEATH_PACKET 9 +#endif //RSRC_TIMEOUT_DBG + +// +// Macros to access StatusAndSentOnUpLine. +// + +#define NB_GET_SR_FN_STATUS(_Reserved) \ + ((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) + +#define NB_SET_SR_FN_STATUS(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) | (_Value)); + +#define NB_GET_SR_FN_SENT_ON_UP_LINE(_Reserved) \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) != 0) + +#define NB_SET_SR_FN_SENT_ON_UP_LINE(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) | ((_Value) << 4)); + + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _NB_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + UCHAR Type; // what to do on completion +#if defined(_PNP_POWER) + PVOID Pool; // send pool it was allocated from +#else + +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + +#endif _PNP_POWER + union { + struct { + struct _CONNECTION * Connection; // that the transfer is for + BOOLEAN EndOfMessage; // this was the last part of a message + BOOLEAN CompleteReceive; // receive should be completed + BOOLEAN NoNdisBuffer; // user's mdl chain was used + BOOLEAN PartialReceive; // (new nb) don't ack this packet + } RR_CO; + struct { + struct _NB_RECEIVE_BUFFER * ReceiveBuffer; // datagram receive buffer + } RR_DG; + struct { + PREQUEST Request; // for this request + } RR_AS; + } u; + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +} NB_RECEIVE_RESERVED, *PNB_RECEIVE_RESERVED; + +// +// Values for Type. +// + +#define RECEIVE_TYPE_DATAGRAM 1 +#define RECEIVE_TYPE_DATA 2 +#define RECEIVE_TYPE_ADAPTER_STATUS 3 + + + +typedef struct _NB_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#if defined(_PNP_POWER) + PVOID Pool; // receive buffer pool was allocated from +#else +#ifdef NB_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif +#endif _PNP_POWER + struct _ADDRESS * Address; // that the datagram is for + SINGLE_LIST_ENTRY PoolLinkage; // when in free pool + LIST_ENTRY WaitLinkage; // when in ReceiveDatagrams queue + PNDIS_BUFFER NdisBuffer; // describes the data + UCHAR RemoteName[16]; // datagram was received from + ULONG DataLength; // length for current one, not allocated + PUCHAR Data; // points to data to hold packet +} NB_RECEIVE_BUFFER, *PNB_RECEIVE_BUFFER; + + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +//#define NB_OWN_PACKETS 1 + +#ifdef NB_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _NB_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_SEND_RESERVED)]; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_RECEIVE_RESERVED)]; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_SendPacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiAllocateReceivePacket(_Device,_PoolHandle, _ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_ReceivePacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiFreeSendPacket(_Device,_Packet) + +#define NbiFreeReceivePacket(_Device,_Packet) + + +#else // NB_OWN_PACKETS + +typedef struct _NB_SEND_PACKET { + PNDIS_PACKET Packet; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + PNDIS_PACKET Packet; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_PACKET_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, _PoolHandle); \ +} + +#define NbiAllocateReceivePacket(_Device, _PoolHandle, _ReceivePacket,_Status) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, _PoolHandle); \ +} + +#define NbiFreeSendPacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#define NbiFreeReceivePacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#endif // NB_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PNB_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PNB_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + + +typedef struct _NB_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; +#if defined(_PNP_POWER) + UINT BufferDataSize; // allocation size of each buffer data +#endif _PNP_POWER + NB_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} NB_RECEIVE_BUFFER_POOL, *PNB_RECEIVE_BUFFER_POOL; + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_CACHE 4 +#define MEMORY_CONNECTION 5 +#define MEMORY_STATUS 6 +#define MEMORY_QUERY 7 +#if defined(_PNP_POWER) +#define MEMORY_WORK_ITEM 8 +#define MEMORY_ADAPTER_ADDRESS 9 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +#define MEMORY_MAX 10 +#else +#define MEMORY_MAX 8 +#endif _PNP_POWER + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(NbiMemoryInterlock); +extern MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif + + + +// +// This structure holds a single remote network which a +// Netbios name exists on. +// + +typedef struct _NETBIOS_NETWORK { + ULONG Network; + IPX_LOCAL_TARGET LocalTarget; +} NETBIOS_NETWORK, *PNETBIOS_NETWORK; + +// +// This defines a netbios cache entry for a given name. +// + +typedef struct _NETBIOS_CACHE { + UCHAR NetbiosName[16]; + BOOLEAN Unique; + BOOLEAN FailedOnDownWan; // if NetworksUsed == 0, was it due to down wan lines? + USHORT TimeStamp; // in seconds - CacheTimeStamp when inserted + ULONG ReferenceCount; + LIST_ENTRY Linkage; + TDI_ADDRESS_IPX FirstResponse; + USHORT NetworksAllocated; + USHORT NetworksUsed; + NETBIOS_NETWORK Networks[1]; // may be more than one of these +} NETBIOS_CACHE, *PNETBIOS_CACHE; + +typedef struct _NETBIOS_CACHE_TABLE { + USHORT MaxHashIndex; + USHORT CurrentEntries; + LIST_ENTRY Bucket[1]; +} NETBIOS_CACHE_TABLE, *PNETBIOS_CACHE_TABLE; + +#define NB_NETBIOS_CACHE_TABLE_LARGE 26 // for server +#define NB_NETBIOS_CACHE_TABLE_SMALL 8 // for workstation +#define NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET 8 + +// +// This defines the different kind of requests that can be made +// to CacheFindName(). +// + +typedef enum _FIND_NAME_TYPE { + FindNameConnect, + FindNameNetbiosFindName, + FindNameOther +} FIND_NAME_TYPE, *PFIND_NAME_TYPE; + + +// +// The number of hash entries in the non-inactive connection +// database. +// + +#define CONNECTION_HASH_COUNT 8 + +// +// Mask and shift to retrieve the hash number from a connection +// ID. +// + +#define CONNECTION_HASH_MASK 0xe000 +#define CONNECTION_HASH_SHIFT 13 + +// +// The maximum connection ID we can assign, not counting the +// shifted-over hash id (which occupies the top 3 bits of the +// real id we use on the wire). We can use all the bits except +// the top one, to prevent an ID of 0xffff being used. +// + +#define CONNECTION_MAXIMUM_ID (USHORT)(~CONNECTION_HASH_MASK & ~1) + +// +// A single connection hash bucket. +// + +typedef struct _CONNECTION_HASH { + struct _CONNECTION * Connections; + USHORT ConnectionCount; + USHORT NextConnectionId; +} CONNECTION_HASH, *PCONNECTION_HASH; + + +// +// These are queued in the ConnectIndicationInProgress +// queue to track indications to TDI clients. +// + +typedef struct _CONNECT_INDICATION { + LIST_ENTRY Linkage; + UCHAR NetbiosName[16]; + TDI_ADDRESS_IPX RemoteAddress; + USHORT ConnectionId; +} CONNECT_INDICATION, *PCONNECT_INDICATION; + +// +// This structure defines the per-device structure for NB +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_CONNECTION 4 +#define DREF_FN_TIMER 5 +#define DREF_FIND_NAME 6 +#define DREF_SESSION_INIT 7 +#define DREF_NAME_FRAME 8 +#define DREF_FRAME 9 +#define DREF_SHORT_TIMER 10 +#define DREF_LONG_TIMER 11 +#define DREF_STATUS_QUERY 12 +#define DREF_STATUS_RESPONSE 13 +#define DREF_STATUS_FRAME 14 +#define DREF_NB_FIND_NAME 15 + +#define DREF_TOTAL 16 + +typedef struct _DEVICE { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + NB_LOCK Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + NB_LOCK Lock; + LONG ReferenceCount; // activity count/this provider. + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; +#if defined(_PNP_POWER) + USHORT DeviceNameLength; +#else + ULONG DeviceNameLength; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + + // + // All send packet pools are chained on this list. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + LIST_ENTRY ReceiveBufferPoolList; + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + SINGLE_LIST_ENTRY ReceiveBufferList; + + // + // Receive requests waiting to be completed. + // + + LIST_ENTRY ReceiveCompletionQueue; + + // + // Connections waiting for send packets. + // + + LIST_ENTRY WaitPacketConnections; + + // + // Connections waiting to packetize. + // + + LIST_ENTRY PacketizeConnections; + + // + // Connections waiting to send a data ack. + // + + LIST_ENTRY DataAckConnections; + + // + // The list changed while we were processing it. + // + + BOOLEAN DataAckQueueChanged; + + // + // Information to manage the Netbios name cache. + // + + LIST_ENTRY WaitingConnects; // connect requests waiting for a name + LIST_ENTRY WaitingDatagrams; // datagram requests waiting for a name + LIST_ENTRY WaitingAdapterStatus; // adapter status requests waiting for a name + LIST_ENTRY WaitingNetbiosFindName; // netbios find name requests waiting for a name + + // + // Holds adapter status request which have a name and + // are waiting for a response. The long timeout aborts + // these after a couple of expirations (BUGBUG: currently we + // do not do resends). + // + + LIST_ENTRY ActiveAdapterStatus; + + // + // Receive datagrams waiting to be indicated. + // + + LIST_ENTRY ReceiveDatagrams; + + // + // In-progress connect indications (used to make + // sure we don't indicate the same packet twice). + // + + LIST_ENTRY ConnectIndicationInProgress; + + // + // Listens that have been posted to connections. + // + + LIST_ENTRY ListenQueue; + + UCHAR State; + + // + // The following fields control the timer system. + // The short timer is used for retransmission and + // delayed acks, and the long timer is used for + // watchdog timeouts. + // + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckActive; // DataAckConnections is not empty. + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. +#if defined(_PNP_POWER) + BOOLEAN LongTimerRunning; // True if the long timer is running. +#endif _PNP_POWER + LARGE_INTEGER ShortTimerStart; // tick count when the short timer was set. + CTETimer ShortTimer; // controls the short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + CTETimer LongTimer; // kernel DPC object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + NB_LOCK TimerLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of waiting connections + LIST_ENTRY LongList; // list of waiting connections + + + // + // Hash table of non-inactive connections. + // + + CONNECTION_HASH ConnectionHash[CONNECTION_HASH_COUNT]; + + // + // Control the queue of waiting find names. + // + + USHORT FindNameTime; // incremented each time the timer runs + BOOLEAN FindNameTimerActive; // TRUE if the timer is queued + CTETimer FindNameTimer; // runs every FIND_NAME_GRANULARITY + ULONG FindNameTimeout; // the retry count in timer ticks + + ULONG FindNamePacketCount; // Count of packets on the queue + LIST_ENTRY WaitingFindNames; // FIND_NAME frames waiting to go out + + // + // The cache of NETBIOS_CACHE entries. + // + + PNETBIOS_CACHE_TABLE NameCache; + + // + // The current time stamp, incremented every second. + // + + USHORT CacheTimeStamp; + + // + // Maximum valid NIC ID we can use. + // + + USHORT MaximumNicId; + + + // + // Handle for our binding to the IPX driver. + // + + HANDLE BindHandle; + + // + // Holds the output from binding to IPX. + // + union { + IPX_INTERNAL_BIND_OUTPUT Bind; + IPX_INTERNAL_BIND_INPUT BindInput; + }; + + // + // Holds our reserved netbios name, which is 10 bytes + // of zeros followed by our node address. + // +#if !defined(_PNP_POWER) + UCHAR ReservedNetbiosName[16]; +#endif !_PNP_POWER + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many packets have been allocated. + // + + ULONG AllocatedSendPackets; + ULONG AllocatedReceivePackets; + ULONG AllocatedReceiveBuffers; + +#if defined(_PNP_POWER) + // + // This is the size of each buffer in the receive buffer pool. + // We reallocate buffer pool when the LineInfo.MaxPacketSize changes(increases) + // from IPX because of a new adapter. The LineInfo.MaxPacketSize could + // also change(decrease) when a adapter disappears but our buffer pool size + // will stay at this value. + // + ULONG CurMaxReceiveBufferSize; +#endif _PNP_POWER + + // + // Other configuration parameters. + // + + ULONG AckDelayTime; // converted to short timeouts, rounded up + ULONG AckWindow; + ULONG AckWindowThreshold; + ULONG EnablePiggyBackAck; + ULONG Extensions; + ULONG RcvWindowMax; + ULONG BroadcastCount; + ULONG BroadcastTimeout; + ULONG ConnectionCount; + ULONG ConnectionTimeout; + ULONG InitPackets; + ULONG MaxPackets; + ULONG InitialRetransmissionTime; + ULONG Internet; + ULONG KeepAliveCount; + ULONG KeepAliveTimeout; + ULONG RetransmitMax; + ULONG RouterMtu; + + ULONG MaxReceiveBuffers; + + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // The following field is a head of a list of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. +#if defined(_PNP_POWER) + LIST_ENTRY AdapterAddressDatabase; // list of netbios names made from adapter addresses. +#endif _PNP_POWER + + ULONG AddressCount; // number of addresses in the database. + + NDIS_HANDLE NdisBufferPoolHandle; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // This structure holds a pre-built IPX header which is used + // to quickly fill in common fields of outgoing connectionless + // frames. + // + + IPX_HEADER ConnectionlessHeader; + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + +#if defined(_PNP_POWER) + HANDLE TdiRegistrationHandle; +#endif _PNP_POWER + + // + // Counters for most of the statistics that NB maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempFrameBytesSent; + ULONG TempFramesSent; + ULONG TempFrameBytesReceived; + ULONG TempFramesReceived; + + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbiStartTime; + + // + // This array is used to quickly dismiss connectionless frames + // that are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + +} DEVICE, * PDEVICE; + + +extern PDEVICE NbiDevice; +EXTERNAL_LOCK(NbiGlobalPoolInterlock); + +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// + +EXTERNAL_LOCK(NbiGlobalInterlock); + + +// +// device state definitions +// +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +#define NB_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 +#define AFREF_TIMEOUT 5 +#define AFREF_CONNECTION 6 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + PNB_LOCK AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST OpenRequest; // the request used for open + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + LIST_ENTRY ConnectionDatabase; // associated with this address. + + LIST_ENTRY ReceiveDatagramQueue; // posted by the client. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // Handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredHandler[6]; + + // + // This is a list of handlers for a given event. They can be + // accessed using the explicit names for type-checking, or the + // array (indexed by the event type) for speed. + // + + union { + struct { + PTDI_IND_CONNECT ConnectionHandler; + PTDI_IND_DISCONNECT DisconnectHandler; + PTDI_IND_ERROR ErrorHandler; + PTDI_IND_RECEIVE ReceiveHandler; + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + }; + PVOID Handlers[6]; + }; + + PVOID HandlerContexts[6]; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +typedef struct _NBI_NETBIOS_ADDRESS { + UCHAR NetbiosName[16]; + USHORT NetbiosNameType; + BOOLEAN Broadcast; +} NBI_NETBIOS_ADDRESS, *PNBI_NETBIOS_ADDRESS; + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 +#define AREF_NAME_FRAME 3 +#define AREF_TIMER 4 +#define AREF_FIND 5 + +#define AREF_TOTAL 8 + + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + NB_LOCK Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + UCHAR NameTypeFlag; // NB_NAME_UNIQUE or NB_NAME_GROUP + + NBI_NETBIOS_ADDRESS NetbiosAddress; // our netbios name. + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + ULONG State; // current state of the address. + struct _DEVICE *Device; // device context to which we are attached. + PNB_LOCK DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + UCHAR SendPacketHeader[NB_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + + // + // This timer is used for registering the name. + // + + CTETimer RegistrationTimer; + + // + // Number of times an add name frame has been sent. + // + + ULONG RegistrationCount; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbiDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +// +// Values for Flags +// + +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000002 +#if defined(_PNP_POWER) +#define ADDRESS_FLAGS_CONFLICT 0x00000010 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// this booleans are passed to nbiverifyaddressfile calls. +// +#define CONFLICT_IS_OK TRUE +#define CONFLICT_IS_NOT_OK FALSE +#endif _PNP_POWER + +// +// Values for State +// + +#define ADDRESS_STATE_REGISTERING 1 +#define ADDRESS_STATE_OPEN 2 +#define ADDRESS_STATE_STOPPING 3 + +#if defined(_PNP_POWER) +// +// This holds the adapters names i.e netbios names which are +// created from adater node address to support adapter status +// queries using adapter node addresses. +// +typedef struct _ADAPTER_ADDRESS { + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + NIC_HANDLE NicHandle; // NicHandle corresponding to this address. + UCHAR NetbiosName[16]; +} ADAPTER_ADDRESS, *PADAPTER_ADDRESS; +#endif _PNP_POWER + +// +// This defines the types of probe packets we can send. +// + +typedef enum _NB_ACK_TYPE { + NbiAckQuery, + NbiAckResponse, + NbiAckResend +} NB_ACK_TYPE, *PNB_ACK_TYPE; + + +// +// This defines the a packetizing location in a +// send. +// + +typedef struct _SEND_POINTER { + ULONG MessageOffset; // up count, bytes sent this message. + PREQUEST Request; // current send request in chain. + PNDIS_BUFFER Buffer; // current buffer in send chain. + ULONG BufferOffset; // current byte offset in current buffer. + USHORT SendSequence; +} SEND_POINTER, *PSEND_POINTER; + +// +// This defines the current location in a receive. +// + +typedef struct _RECEIVE_POINTER { + ULONG MessageOffset; // up count, bytes received this message. + ULONG Offset; // up count, bytes received this request. + PNDIS_BUFFER Buffer; // current buffer in receive request. + ULONG BufferOffset; // current byte offset in current buffer. +} RECEIVE_POINTER, *PRECEIVE_POINTER; + + +// +// This structure defines a connection, which controls a +// session with a remote. +// + +#define CREF_VERIFY 0 +#define CREF_LISTEN 1 +#define CREF_CONNECT 2 +#define CREF_WAIT_CACHE 3 +#define CREF_TIMER 4 +#define CREF_INDICATE 5 +#define CREF_ACTIVE 6 +#define CREF_FRAME 7 +#define CREF_BY_CONTEXT 8 +#define CREF_W_ACCEPT 9 +#define CREF_SEND 10 +#define CREF_RECEIVE 11 +#define CREF_PACKETIZE 12 +#define CREF_DISASSOC 13 +#define CREF_W_PACKET 14 +#define CREF_CANCEL 15 +#define CREF_NDIS_SEND 16 +#define CREF_SHORT_D_ACK 17 +#define CREF_LONG_D_ACK 18 +#define CREF_FIND_ROUTE 19 +#define CREF_ACCEPT 20 + +#define CREF_TOTAL 24 + +typedef struct _CONNECTION { + +#if DBG + ULONG RefTypes[CREF_TOTAL]; +#endif + + CSHORT Type; + USHORT Size; + + NB_LOCK Lock; + PNB_LOCK DeviceLock; + + ULONG ReferenceCount; // number of references to this object. + + CONNECTION_CONTEXT Context; // client-specified value. + + ULONG State; + ULONG SubState; + ULONG ReceiveState; // SubState tracks sends when active. + ULONG NewNetbios; // 1 if we negotiated this. + + REQUEST_LIST_HEAD SendQueue; + REQUEST_LIST_HEAD ReceiveQueue; + + USHORT ReceiveSequence; + + USHORT LocalRcvSequenceMax; // we advertise to him (will be near SendSequence) + USHORT RemoteRcvSequenceMax; // he advertises to us (will be near ReceiveSequence) + USHORT SendWindowSequenceLimit; // when this send window ends (may send past it however) + + // + // RemoteRcvSequenceMax is the largest frame number that he expects to + // receive, while SendWindowSequenceLimit is one more than the max + // we can send. I.e. if he is advertising a window of 4 and we think + // the window should be 2, and the current send sequence is 7, + // RemoteRcvSequenceMax is 10 and SendWindowSequenceLimit is 9. + // + + USHORT ReceiveWindowSize; // when it is open, how big to make it + USHORT SendWindowSize; // what we'll send, may be less than what he advertises + USHORT MaxSendWindowSize; // maximum we allow it to grow to + + USHORT IncreaseWindowFailures; // how many windows after increase have had retransmits + BOOLEAN RetransmitThisWindow; // we had to retransmit in this send window + BOOLEAN SendWindowIncrease; // send window was just increased. + BOOLEAN ResponseTimeout; // we hit timeout in SEND_W or REMOTE_W + + BOOLEAN SendBufferInUse; // current send's already queued on packet + + ULONG Retries; + + // + // Tracks the current send. + // + + SEND_POINTER CurrentSend; + + // + // Tracks the unacked point in the send. + // + + SEND_POINTER UnAckedSend; + + PREQUEST FirstMessageRequest; // first one in the message. + PREQUEST LastMessageRequest; // last one in the message. + + ULONG CurrentMessageLength; // total length of current message. + + // + // Tracks the current receive. + // + + RECEIVE_POINTER CurrentReceive; // where to receive next data + RECEIVE_POINTER PreviousReceive; // stores it while transfer in progress + + PREQUEST ReceiveRequest; // current one; not in ReceiveQueue + ULONG ReceiveLength; // length of ReceiveRequest + + ULONG ReceiveUnaccepted; // by client...only indicate when == 0 + + ULONG CurrentIndicateOffset; // if previous frame was partially accepted. + + IPX_LINE_INFO LineInfo; // for the adapter this connect is on. + ULONG MaximumPacketSize; // as negotiated during session init/ack + + // + // Links us in the non-inactive connection hash bucket. + // + + struct _CONNECTION * NextConnection; + + // + // These are used to determine when to piggyback and when not to. + // + + BOOLEAN NoPiggybackHeuristic; // we have reason to assume it would be bad. + BOOLEAN PiggybackAckTimeout; // we got a timeout last time we tried. + ULONG ReceivesWithoutAck; // used to do an auto ack. + + // + // The following field is used as linkage in the device's + // PacketizeConnections queue. + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device's + // WaitPacketConnections queue. + // + + LIST_ENTRY WaitPacketLinkage; + + // + // The following field is used as linkage in the device's + // DataAckConnections queue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if we are on these queues. + // + + BOOLEAN OnPacketizeQueue; + BOOLEAN OnWaitPacketQueue; + BOOLEAN OnDataAckQueue; + + // + // TRUE if we have a piggyback ack pending. + // + + BOOLEAN DataAckPending; + + // + // TRUE if the current receive does not allow piggyback acks. + // + + BOOLEAN CurrentReceiveNoPiggyback; + + // + // Number of short timer expirations with the data ack queued. + // + + ULONG DataAckTimeouts; + + // + // Used to queue sends so that no two are outstanding at once. + // + + ULONG NdisSendsInProgress; + LIST_ENTRY NdisSendQueue; + + // + // This pointer is valid when NdisSendsInProgress is non-zero; + // it holds a pointer to a location on the stack of the thread + // which is inside NbiAssignSequenceAndSend. If this location + // is set to TRUE, it means the connection was stopped by another + // thread and a reference was added to keep the connection around. + // + + PBOOLEAN NdisSendReference; + + // + // These are used for timeouts. + // + + ULONG BaseRetransmitTimeout; // config # of short timeouts we wait. + ULONG CurrentRetransmitTimeout; // possibly backed-off number + ULONG WatchdogTimeout; // how many long timeouts we wait. + ULONG Retransmit; // timer; based on Device->ShortAbsoluteTime + ULONG Watchdog; // timer; based on Device->LongAbsoluteTime + USHORT TickCount; // 18.21/second, # for 576-byte packet. + USHORT HopCount; // As returned by ipx on find route. + BOOLEAN OnShortList; // are we inserted in the list + BOOLEAN OnLongList; // are we inserted in the list + LIST_ENTRY ShortList; // queues us on Device->ShortList + LIST_ENTRY LongList; // queues us on Device->LongList + + // + // These are valid when we have a connection established; + // + + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + + PREQUEST DisassociatePending; // guarded by device lock. + PREQUEST ClosePending; + + PREQUEST ConnectRequest; + PREQUEST ListenRequest; + PREQUEST AcceptRequest; + PREQUEST DisconnectRequest; + PREQUEST DisconnectWaitRequest; + + ULONG CanBeDestroyed; // FALSE if reference is non-zero + ULONG ThreadsInHandleConnectionZero; // # of threads in HandleConnectionZero + + // + // These are used to hold extra data that was sent on a session + // init, for use in sending the ack. Generally will be NULL and 0. + // + + PUCHAR SessionInitAckData; + ULONG SessionInitAckDataLength; + + IPX_LOCAL_TARGET LocalTarget; // for the remote when active. + IPX_HEADER RemoteHeader; + + CTETimer Timer; + + PADDRESS_FILE AddressFile; // guarded by device lock if associated. + LIST_ENTRY AddressFileLinkage; // guarded by device lock + ULONG AddressFileLinked; // TRUE if queued using AddressFileLinkage + + PDEVICE Device; +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + + CHAR RemoteName[16]; // for an active connection. + + IPX_FIND_ROUTE_REQUEST FindRouteRequest; // use this to verify route. + + TDI_CONNECTION_INFO ConnectionInfo; // can be queried from above. + + BOOLEAN FindRouteInProgress; // we have a request pending. + + BOOLEAN SendPacketInUse; // put this here to align packet/header. + BOOLEAN IgnoreNextDosProbe; + + NTSTATUS Status; // status code for connection rundown. + +#ifdef RSRC_TIMEOUT_DBG + LARGE_INTEGER FirstMessageRequestTime; +#endif //RSRC_TIMEOUT_DBG + + NDIS_HANDLE SendPacketPoolHandle; // poolhandle for sendpacket below when + // the packet is allocated from ndis pool. + + NB_SEND_PACKET SendPacket; // try to use this first for sends + + ULONG Flags; // miscellaneous connection flags + + UCHAR SendPacketHeader[1]; // connection is extended to include this + + // + // NOTE: This is variable length structure! + // Do not add fields below this comment. + // +} CONNECTION, *PCONNECTION; + + +#define CONNECTION_STATE_INACTIVE 1 +#define CONNECTION_STATE_CONNECTING 2 +#define CONNECTION_STATE_LISTENING 3 +#define CONNECTION_STATE_ACTIVE 4 +#define CONNECTION_STATE_DISCONNECT 5 +#define CONNECTION_STATE_CLOSING 6 + + +#define CONNECTION_SUBSTATE_L_WAITING 1 // queued by a listen +#define CONNECTION_SUBSTATE_L_W_ACCEPT 2 // waiting for user to accept +#define CONNECTION_SUBSTATE_L_W_ROUTE 3 // waiting for rip response + +#define CONNECTION_SUBSTATE_C_FIND_NAME 1 // waiting for cache response +#define CONNECTION_SUBSTATE_C_W_ACK 2 // waiting for session init ack +#define CONNECTION_SUBSTATE_C_W_ROUTE 3 // waiting for rip response +#define CONNECTION_SUBSTATE_C_DISCONN 4 // disconnect was issued + +#define CONNECTION_SUBSTATE_A_IDLE 1 // no sends in progress +#define CONNECTION_SUBSTATE_A_PACKETIZE 2 // packetizing a send +#define CONNECTION_SUBSTATE_A_W_ACK 3 // waiting for an ack +#define CONNECTION_SUBSTATE_A_W_PACKET 4 // waiting for a packet +#define CONNECTION_SUBSTATE_A_W_EOR 5 // waiting for eor to start packetizing +#define CONNECTION_SUBSTATE_A_W_PROBE 6 // waiting for a keep-alive response +#define CONNECTION_SUBSTATE_A_REMOTE_W 7 // remote shut down our window + +#define CONNECTION_RECEIVE_IDLE 1 // no receives queued +#define CONNECTION_RECEIVE_ACTIVE 2 // receive is queued +#define CONNECTION_RECEIVE_W_RCV 3 // waiting for receive to be posted +#define CONNECTION_RECEIVE_INDICATE 4 // indication in progress +#define CONNECTION_RECEIVE_TRANSFER 5 // transfer is in progress +#define CONNECTION_RECEIVE_PENDING 6 // last request is queued for completion + +#define CONNECTION_SUBSTATE_D_W_ACK 1 +#define CONNECTION_SUBSTATE_D_GOT_ACK 2 + +// +// Bit values for Flags field in +// the CONNECTION structure. +// +#define CONNECTION_FLAGS_AUTOCONNECTING 0x00000001 // RAS autodial in progress +#define CONNECTION_FLAGS_AUTOCONNECTED 0x00000002 // RAS autodial connected + +#ifdef RSRC_TIMEOUT_DBG +extern ULONG NbiGlobalDebugResTimeout; +extern LARGE_INTEGER NbiGlobalMaxResTimeout; +extern NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +#endif //RSRC_TIMEOUT_DBG diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.ini b/private/ntos/tdi/isnp/nb/nwlnknb.ini new file mode 100644 index 000000000..e2df88f54 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.ini @@ -0,0 +1,43 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkNb + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnknb.sys + DisplayName = NWLINK2 IPX Netbios Protocol + Group = TDI + DependOnService = REG_MULTI_SZ "NwlnkIpx" + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\NwlnkIpx" + Export = REG_MULTI_SZ "\Device\NwlnkNb" + Route = REG_MULTI_SZ ""NwlnkIpx"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + Parameters + AckDelayTime = REG_DWORD 0x000000fa + AckWindow = REG_DWORD 0x00000002 + AckWindowThreshold = REG_DWORD 0x000001f4 + EnablePiggyBackAck = REG_DWORD 0x00000001 + Extensions = REG_DWORD 0x00000001 + RcvWindowMax = REG_DWORD 0x00000004 + BroadcastCount = REG_DWORD 0x00000003 + BroadcastTimeout = REG_DWORD 0x00000001 + ConnectionCount = REG_DWORD 0x00000005 + ConnectionTimeout = REG_DWORD 0x00000002 + InitPackets= REG_DWORD 0x00000005 + MaxPackets = REG_DWORD 0x0000001e + InitialRetransmissionTime = REG_DWORD 0x000001f4 + Internet = REG_DWORD 0x00000001 + KeepAliveCount = REG_DWORD 0x00000008 + KeepAliveTimeout = REG_DWORD 0x0000003c + RetransmitMax = REG_DWORD 0x00000008 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkNb + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.rc b/private/ntos/tdi/isnp/nb/nwlnknb.rc new file mode 100644 index 000000000..1b0163cf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Netbios Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnknb.sys" +#define VER_ORIGINALFILENAME_STR "nwlnknb.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/nb/packet.c b/private/ntos/tdi/isnp/nb/packet.c new file mode 100644 index 000000000..7e22534a8 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/packet.c @@ -0,0 +1,1482 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local Function Protos +// +#if defined(_PNP_POWER) +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ); +#endif _PNP_POWER + + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + + Header - Points to storage for the header. + + HeaderLength - The length of the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisNbBuffer; + PNB_SEND_RESERVED Reserved; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + NbiAllocateSendPacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } +// DbgPrint("NbiInitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + + // + // allocate the mac header. + // + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + Header, + MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisBuffer); + +// DbgPrint("NbiInitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + // + // Allocate the nb header + // + NdisAllocateBuffer( + &NdisStatus, + &NdisNbBuffer, + Device->NdisBufferPoolHandle, + Header + MacHeaderNeeded, + HeaderLength - MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + // DbgPrint("NbiInitializeSendPacket: IPX header address is (%x)\n", NdisNbBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisNbBuffer); + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->SendInProgress = FALSE; + Reserved->OwnedByConnection = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisBuffer; + + Reserved->Reserved[0] = NULL; + Reserved->Reserved[1] = NULL; + + InsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeSendPacket */ + + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PNB_RECEIVE_RESERVED Reserved; + + NbiAllocateReceivePacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->TransferInProgress = FALSE; + + InsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceivePacket */ + + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + InsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceiveBuffer */ + + + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + + HeaderLength - The length of the first buffer on the packet. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNB_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + CTEAssert(HeaderLength > MacHeaderNeeded); + Reserved = SEND_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Free the mac header + // + // DbgPrint("NbiDeinitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // Free the nb header + // + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: IPX header address is (%x)\n", NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, HeaderLength - MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // free the packet + // + NbiFreeSendPacket (Device, Packet); + +} /* NbiDeinitializeSendPacket */ + + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PNB_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeReceivePacket (Device, Packet); + +} /* NbiDeinitializeReceivePacket */ + + + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + +Return Value: + + None. + + THIS ROUTINE SHOULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ + +{ +#if defined(_PNP_POWER) + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); +#else + CTELockHandle LockHandle; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER + + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* NbiDeinitializeReceiveBuffer */ + + + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PNB_SEND_PACKET Packet; + PNB_SEND_RESERVED Reserved; + PUCHAR Header; + ULONG HeaderLength; + NTSTATUS Status; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + SendPool = (PNB_SEND_POOL)NbiAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + RtlZeroMemory (SendPool, SendPoolSize); + + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &SendPool->PoolHandle, Device->InitPackets, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( SendPool, SendPoolSize, MEMORY_PACKET, "Send Pool Freed"); + return; + } +#endif + + NB_DEBUG2 (PACKET, ("Initializing send pool %lx, %d packets, header %d\n", + SendPool, Device->InitPackets, HeaderLength)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitPackets]); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (NbiInitializeSendPacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + SendPool->PoolHandle, +#endif + Packet, + Header, + HeaderLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->u.SR_NF.Address = NULL; +#ifdef NB_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += HeaderLength; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedSendPackets += SendPool->PacketCount; + +} /* NbiAllocateSendPool */ + + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 5 receive packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PNB_RECEIVE_PACKET Packet; + PNB_RECEIVE_RESERVED Reserved; + NTSTATUS Status; + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + ReceivePool = (PNB_RECEIVE_POOL)NbiAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + RtlZeroMemory (ReceivePool, ReceivePoolSize); + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &ReceivePool->PoolHandle, Device->InitPackets, sizeof(NB_RECEIVE_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( ReceivePool, ReceivePoolSize, MEMORY_PACKET, "Receive Pool Freed"); + return; + } +#endif NB_OWN_PACKETS + + NB_DEBUG2 (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitPackets)); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (NbiInitializeReceivePacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + ReceivePool->PoolHandle, +#endif + Packet) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); +#ifdef NB_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); +// PushEntryList (&Device->ReceivePacketList, &Reserved->PoolLinkage); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + +} /* NbiAllocateReceivePool */ + + +#if defined(_PNP_POWER) + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + DataLength - Max length of the data in each buffer. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PUCHAR Data; + + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + + ReceiveBuffer->Pool = ReceiveBufferPool; + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + ReceiveBufferPool->BufferDataSize = DataLength; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + Device->CurMaxReceiveBufferSize = DataLength; + +} /* NbiAllocateReceiveBufferPool */ +#else + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + UINT DataLength; + PUCHAR Data; + + DataLength = Device->Bind.LineInfo.MaximumPacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef NB_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + +} /* NbiAllocateReceiveBufferPool */ +#endif _PNP_POWER + +#if defined(_PNP_POWER) + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ) + +/*++ + +Routine Description: + + This routines destroys all the existing Buffer Pools and creates + new one using the larger packet size given to us by IPX because + a new card was inserted with a larger packet size. + +Arguments: + + WorkItem - The work item that was allocated for this. + +Return Value: + + None. + +--*/ +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + + NB_GET_LOCK ( &Device->Lock, &LockHandle ); + + if ( Device->Bind.LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("Reallocating new pools due to new maxpacketsize\n"); +#endif + NbiDestroyReceiveBufferPools( Device ); + NbiAllocateReceiveBufferPool( Device, Device->Bind.LineInfo.MaximumPacketSize ); + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + NbiFreeMemory( WorkItem, sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buff Work Item freed"); +} + +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ) + +/*++ + +Routine Description: + + This routine frees the +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ +{ + PDEVICE Device = NbiDevice; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize,i; + + CTEAssert( ReceiveBufferPool->BufferDataSize ); + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (ReceiveBufferPool->BufferDataSize * Device->InitPackets); + + // + // Check if we can free this pool + // + CTEAssert(ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + RemoveEntryList( &ReceiveBufferPool->Linkage ); + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + +} + + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routines walks the ReceiveBufferPoolList and destroys the + pool which does not have any buffer in use. + +Arguments: + +Return Value: + + None. + + THIS ROUTINE COULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine is also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ +{ + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY Unused; + + + // + // Clean up this list before we call NbiFreeReceiveBufferPool bcoz that will + // simply destroy all the buffer which might be queue here on this list. + // At the end of this routine we must start with a fresh ReceiveBufferList. + // + do { + Unused = PopEntryList( &Device->ReceiveBufferList ); + } while( Unused ); + + // + // Now destroy each individual ReceiveBufferPool. + // + for ( p = Device->ReceiveBufferPoolList.Flink; + p != &Device->ReceiveBufferPoolList; + ) { + + + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + p = p->Flink; + + // + // This will destroy and unlink this Pool if none of its buffer is + // in use currently. + // + + if ( ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } else { + // + // When the device is stopping we must succeed in freeing the pool. + CTEAssert( Device->State != DEVICE_STATE_STOPPING ); + } + + } + +} + + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine returns the receive buffer back to the free list. + It checks the size of this buffer. If it is smaller than the + the CurMaxReceiveBufferSize, then it does not return this back + to the free list, instead it destroys it and possibly also + destroys the pool associated with it. O/w it simply returns this + to the free list. + +Arguments: + + ReceiveBuffer - Pointer to the buffer to be returned to the free list. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; +#if defined(DBG) + ULONG BufLen = 0; +#endif + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + +#if defined(DBG) + NdisQueryBuffer( ReceiveBuffer->NdisBuffer, NULL, &BufLen ); + CTEAssert( BufLen == ReceiveBufferPool->BufferDataSize ); +#endif + + // + // This is an old buffer which was in use when we changed + // the CurMaxReceiveBufferSize due to new adapter. We must not + // return this buffer back to free list. Infact, if the pool + // associated with this buffer does not have any other buffers + // in use, we should free the pool also. + CTEAssert( ReceiveBufferPool->BufferFree < ReceiveBufferPool->BufferCount ); + ReceiveBufferPool->BufferFree++; + + if ( ReceiveBufferPool->BufferDataSize < Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("ReceiveBuffer %lx, not returned to pool %lx( Free %d)\n", ReceiveBuffer, ReceiveBufferPool, ReceiveBufferPool->BufferFree); +#endif + + + if ( ReceiveBufferPool->BufferFree == ReceiveBufferPool->BufferCount ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } + } else { + + PushEntryList( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage ); + + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); +} +#endif _PNP_POWER + + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + + LockAcquired - TRUE if Device->Lock is acquired. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (!LockAcquired) { + NB_GET_LOCK (&Device->Lock, &LockHandle); + } + + if (Device->AllocatedSendPackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateSendPool (Device); + + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + return s; + } else { + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return NULL; + } + +} /* NbiPopSendPacket */ + + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine frees a packet back to the device context's pool. + If there are connections waiting for packets, it removes + one from the list and inserts it on the packetize queue. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // BUGBUG: Make this a function. Optimize for + // UP by not doing two checks? + // + + if (!IsListEmpty (&Device->WaitPacketConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = RemoveHeadList (&Device->WaitPacketConnections); + + // + // Take a connection off the WaitPacketQueue and put it + // on the PacketizeQueue. We don't worry about if the + // connection has stopped, that will get checked when + // the PacketizeQueue is run down. + // + // Since this is in send completion, we may not get + // a receive complete. We guard against this by calling + // NbiReceiveComplete from the long timer timeout. + // + + if (p != &Device->WaitPacketConnections) { + + Connection = CONTAINING_RECORD (p, CONNECTION, WaitPacketLinkage); + + CTEAssert (Connection->OnWaitPacketQueue); + Connection->OnWaitPacketQueue = FALSE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } + +} /* NbiPushSendPacket */ + + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine checks if a connection is on the wait packet + queue and if so takes it off and queues it to be packetized. + It is meant to be called when the connection's packet has + been freed. + +Arguments: + + Connection - The connection to check. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (Connection->OnWaitPacketQueue) { + + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + InsertTailList( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage); + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + return; + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiCheckForWaitPacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceivePool (Device); + NB_FREE_LOCK (&Device->Lock, LockHandle); + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + + return s; + + } else { + + return NULL; + + } + +} /* NbiPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the device context's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ +#if defined(_PNP_POWER) + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + CTELockHandle LockHandle; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + s = PopEntryList( &Device->ReceiveBufferList ); + + + if ( !s ) { + + // + // No buffer in the pool, see if we can allocate more. + // + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateReceiveBufferPool (Device, Device->CurMaxReceiveBufferSize ); + s = PopEntryList(&Device->ReceiveBufferList); + } + } + + if ( s ) { + + + // + // Decrement the BufferFree count on the corresponding ReceiveBufferPool. + // so that we know that + ReceiveBuffer = CONTAINING_RECORD( s, NB_RECEIVE_BUFFER, PoolLinkage ); + + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + + CTEAssert( ReceiveBufferPool->BufferFree && ( ReceiveBufferPool->BufferFree <= ReceiveBufferPool->BufferCount ) ); + CTEAssert( ReceiveBufferPool->BufferDataSize == Device->CurMaxReceiveBufferSize ); + + ReceiveBufferPool->BufferFree--; + + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; +#else + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntryList( + &Device->ReceiveBufferList, + &Device->Lock.Lock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceiveBufferPool (Device); + s = PopEntryList(&Device->ReceiveBufferList); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; + + } else { + + return NULL; + + } +#endif _PNP_POWER +} /* NbiPopReceiveBuffer */ + diff --git a/private/ntos/tdi/isnp/nb/precomp.h b/private/ntos/tdi/isnp/nb/precomp.h new file mode 100644 index 000000000..a024f2d3d --- /dev/null +++ b/private/ntos/tdi/isnp/nb/precomp.h @@ -0,0 +1,42 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include +#include +#include +#include +#include +#include "isnnb.h" +#include "config.h" +#include "nbitypes.h" +#include "nbiprocs.h" +#include "zwapi.h" diff --git a/private/ntos/tdi/isnp/nb/query.c b/private/ntos/tdi/isnp/nb/query.c new file mode 100644 index 000000000..6ee33adf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/query.c @@ -0,0 +1,1817 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Remove the warning -- this is defined in windef also. +// + +#ifdef FAR +#undef FAR +#endif + +#include +#include + + +// +// Useful macro to obtain the total length of a buffer chain. +// BUGBUG: Make this use NDIS macros. +// + +#define NbiGetBufferChainLength(Buffer, Length) { \ + PNDIS_BUFFER _Buffer = (Buffer); \ + *(Length) = 0; \ + while (_Buffer) { \ + *(Length) += MmGetMdlByteCount(_Buffer); \ + _Buffer = _Buffer->Next; \ + } \ +} + + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + The status of operation. + +--*/ + +{ + NTSTATUS Status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PCONNECTION Connection; + union { + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS NbiAddress; + } AddressInfo; + TA_NETBIOS_ADDRESS BroadcastAddress; + TDI_ADDRESS_IPX IpxAddress; + TDI_DATAGRAM_INFO DatagramInfo; + struct { + FIND_NAME_HEADER Header; + FIND_NAME_BUFFER Buffer; + } FindNameInfo; + } TempBuffer; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + PADAPTER_STATUS AdapterStatus; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG TargetBufferLength; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PNETBIOS_CACHE CacheName; + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + NTSTATUS QueryStatus; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN UsedConnection; + UINT i; + + + // + // what type of status do we want? + // + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (Query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = FALSE; + + } else if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE) { + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = TRUE; + + AddressFile = Connection->AddressFile; + + } else { + + Status = STATUS_INVALID_ADDRESS; + break; + + } + + Address = AddressFile->Address; + + NB_DEBUG2 (QUERY, ("Query address info on %lx\n", AddressFile)); + + TempBuffer.AddressInfo.ActivityCount = 0; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + if (CONTAINING_RECORD (p, ADDRESS_FILE, Linkage)->State == ADDRESSFILE_STATE_OPEN) { + ++TempBuffer.AddressInfo.ActivityCount; + } + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + TdiBuildNetbiosAddress( + AddressFile->Address->NetbiosAddress.NetbiosName, + (BOOLEAN)(AddressFile->Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempBuffer.AddressInfo.NbiAddress); + + Status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + if (UsedConnection) { + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + } else { + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Status = STATUS_INVALID_CONNECTION; + + } else { + + // + // Assume 50 ms of delay for every hop after the + // first. The delay is returned as a negative number. + // + + if (Connection->HopCount > 1) { + Connection->ConnectionInfo.Delay.HighPart = (ULONG)-1; + Connection->ConnectionInfo.Delay.LowPart = + -((Connection->HopCount-1) * 50 * MILLISECONDS); + } else { + Connection->ConnectionInfo.Delay.HighPart = 0; + Connection->ConnectionInfo.Delay.LowPart = 0; + } + + // + // We have tick count; to convert to bytes/second we do: + // + // packet 576 bytes 18.21 ticks + // ---------------- * --------- * ----------- + // tick_count ticks packet seconds + // + // to get 10489/tick_count = bytes/second. We + // double this because routers tend to + // overestimate it. + // + // Since tick_count has such a low granularity, + // a tick count of 1 gives us a throughput of + // only 84 kbps, which is much too low. In + // that case we return twice the link speed + // which is in 100 bps units; that corresponds + // to about 1/6 of our bandwidth in bytes/sec. + // + + if (Connection->TickCount <= Connection->HopCount) { + + Connection->ConnectionInfo.Throughput.QuadPart = + UInt32x32To64 (Connection->LineInfo.LinkSpeed, 2); + + } else { + + Connection->ConnectionInfo.Throughput.HighPart = 0; + Connection->ConnectionInfo.Throughput.LowPart = + 20978 / (Connection->TickCount - Connection->HopCount); + + } + + Connection->ConnectionInfo.Unreliable = FALSE; + + Status = TdiCopyBufferToMdl ( + &Connection->ConnectionInfo, + 0, + sizeof(TDI_CONNECTION_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + break; + + case TDI_QUERY_PROVIDER_INFO: + + NB_DEBUG2 (QUERY, ("Query provider info\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Information, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + NB_DEBUG2 (QUERY, ("Query broadcast address\n")); + + TempBuffer.BroadcastAddress.TAAddressCount = 1; + TempBuffer.BroadcastAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + TempBuffer.BroadcastAddress.Address[0].AddressLength = 0; + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.BroadcastAddress, + 0L, + sizeof (TempBuffer.BroadcastAddress.TAAddressCount) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressType) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressLength), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // Determine if this is a local or remote query. + // + + RemoteAdapterStatus = FALSE; + + if (Query->RequestConnectionInformation != NULL) { + + RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (RemoteAddress == NULL) { + return STATUS_BAD_NETWORK_PATH; + } + +#if defined(_PNP_POWER) + if ( !NbiFindAdapterAddress( + RemoteAddress->NetbiosName, + LOCK_NOT_ACQUIRED ) ) { + + RemoteAdapterStatus = TRUE; + } +#else + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + Device->ReservedNetbiosName, + 16)) { + + RemoteAdapterStatus = TRUE; + + } +#endif _PNP_POWER + + } + + if (RemoteAdapterStatus) { + + // + // See if we have this name cached. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this status + // request and processing will be resumed when + // we get a response. + // + // The status field in the request will hold + // the cache entry for the remote. The information + // field will hold the remote netbios name while + // it is in the WaitingAdapterStatus queue, and + // will hold a timeout value while we it is in + // the ActiveAdapterStatus queue. + // + + NB_DEBUG2 (QUERY, ("Queueing up adapter status %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + InsertTailList( + &Device->WaitingAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found adapter status cached %lx\n", Request)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + REQUEST_STATUS(Request) = (NTSTATUS)CacheName; + ++CacheName->ReferenceCount; + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = 0; + + InsertTailList( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiSendStatusQuery (Request); + + Status = STATUS_PENDING; + + } else { + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // Local adapter status. + // + + NbiGetBufferChainLength (REQUEST_NDIS_BUFFER(Request), &TargetBufferLength); + + Status = NbiStoreAdapterStatus( + TargetBufferLength, + 1, // NIC ID, was 0, changed to 1 for Bug #18026 + // because for NicId = 0, Ipx returns virtual + // address. Netbios uses that to register the + // name (00...01) and fails. + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + + // + // This should succeed since we know the length + // will fit. + // + + (VOID)TdiCopyBufferToMdl( + AdapterStatus, + 0, + ValidStatusLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + + } + + } + + break; + + case TDI_QUERY_FIND_NAME: + + // + // Check that there is a valid Netbios remote address. + // + + if ((Query->RequestConnectionInformation == NULL) || + ((RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE)) == NULL)) { + + return STATUS_BAD_NETWORK_PATH; + } + + // + // We assume the entire request buffer is in the first + // piece of the MDL chain (BUGBUG: Can we do this?). + // Make sure there is room for at least the header. + // + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER)) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // See if we have this name cached. We specify that this is + // a netbios name query, so this will only succeed if this is a + // unique name -- for a group name it will queue up a find + // name query and when we get the response we will fill in + // the request's buffer based on it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameNetbiosFindName, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this find + // name request and processing will be resumed when + // we get a response. + // + // The information field will hold the remote + // netbios name while it is in the WaitingNetbiosFindName + // queue. The status will hold the current status -- + // initially failure, then success, then overflow + // if the buffer is too small. + // + + NB_DEBUG2 (QUERY, ("Queueing up find name %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_NB_FIND_NAME); + + FindNameHeader->node_count = 0; + FindNameHeader->reserved = 0; + FindNameHeader->unique_group = 0; + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + // + // Assume it fails, we update the status to + // SUCCESS or BUFFER_OVERFLOW if needed. + // + + REQUEST_STATUS (Request) = STATUS_IO_TIMEOUT; + + InsertTailList( + &Device->WaitingNetbiosFindName, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found find name cached %lx\n", Request)); + + // + // We don't need to reference the cache entry since + // we only use it here with the lock still held. + // + + // + // Query the local address, which we will return as + // the destination address of this query. Since we + // use TempBuffer.IpxAddress for this query, we have + // to immediately copy it to its correct place in + // TempBuffer.FindNameInfo.Buffer. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + &CacheName->Networks[0].LocalTarget.NicHandle, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicHandle.NicId )); + + goto QueryFindNameFailed; + } +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicId, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + RtlMoveMemory (TempBuffer.FindNameInfo.Buffer.destination_addr, TempBuffer.IpxAddress.NodeAddress, 6); + TempBuffer.FindNameInfo.Buffer.access_control = 0x10; // standard token-ring values + TempBuffer.FindNameInfo.Buffer.frame_control = 0x40; + RtlCopyMemory (TempBuffer.FindNameInfo.Buffer.source_addr, CacheName->FirstResponse.NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, CacheName->FirstResponse.NodeAddress, 6); + + QueryStatus = (*Device->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + &CacheName->Networks[0].LocalTarget.NicHandle, +#else + CacheName->Networks[0].LocalTarget.NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(TempBuffer.FindNameInfo.Buffer.routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + TempBuffer.FindNameInfo.Buffer.routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + TempBuffer.FindNameInfo.Buffer.length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + TempBuffer.FindNameInfo.Header.node_count = 1; + TempBuffer.FindNameInfo.Header.reserved = 0; + TempBuffer.FindNameInfo.Header.unique_group = 0; // unique + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.FindNameInfo, + 0, + sizeof(FIND_NAME_HEADER) + 33, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + +#if defined(_PNP_POWER) +QueryFindNameFailed: +#endif _PNP_POWER + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BETABUGBUG: Keep track of more of these. + // + + NB_DEBUG2 (QUERY, ("Query provider statistics\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + NB_DEBUG2 (QUERY, ("Query datagram info\n")); + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + Status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS:{ +#if defined(_PNP_POWER) + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS + ? IPX_QUERY_DATA_LINK_ADDRESS + : IPX_QUERY_NETWORK_ADDRESS ), + NULL, + Request, + 0, + NULL); +#else + ULONG TransportAddressAllocSize; + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + +// TransportAddress = CTEAllocMem(sizeof(int) + (ElementSize * Device->MaximumNicId)); + TransportAddressAllocSize = sizeof(int) + ( ElementSize * Device->MaximumNicId); + TransportAddress = NbiAllocateMemory( TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + if (TransportAddress == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; + + for (i = 1; i <= Device->MaximumNicId; i++) { + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + (USHORT)i, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + if (Status != STATUS_SUCCESS) { + continue; + } + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = TDI_ADDRESS_TYPE_UNSPEC; + RtlCopyMemory (CurAddress->Address, TempBuffer.IpxAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + + Status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + +// CTEFreeMem (TransportAddress); + NbiFreeMemory( TransportAddress, TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + } +#endif _PNP_POWER + break; + } + default: + + NB_DEBUG (QUERY, ("Invalid query type %d\n", Query->QueryType)); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; + +} /* NbiTdiQueryInformation */ + + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ) + +/*++ + +Routine Description: + + This routine allocates an ADAPTER_STATUS buffer and + fills it in. The buffer will be allocated at most + MaximumLength size. The caller is responsible for + freeing the buffer. + +Arguments: + + MaximumLength - The maximum length to allocate. + + NicId - The NIC ID the query was received on, or 0 for + a local query. + + StatusBuffer - Returns the allocated buffer. + + StatusBufferLength - Returns the length of the buffer. + + ValidBufferLength - Returns the length of the buffer which + contains valid adapter status data. + +Return Value: + + STATUS_SUCCESS - The buffer was written successfully. + STATUS_BUFFER_OVERFLOW - The buffer was written but not all + data could fit in MaximumLength bytes. + STATUS_INSUFFICIENT_RESOURCES - The buffer could not be allocated. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus; + PNAME_BUFFER NameBuffer; + ADAPTER_STATUS TempAdapterStatus; +#if !defined(_PNP_POWER) + TDI_ADDRESS_IPX IpxAddress; +#endif !_PNP_POWER + PDEVICE Device = NbiDevice; + PADDRESS Address; + UCHAR NameCount; + ULONG LengthNeeded; + ULONG BytesWritten; + NTSTATUS Status; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + // + // First fill in the basic adapter status structure, to make + // it easier to copy over if the target buffer is really short. + // + + RtlZeroMemory ((PVOID)&TempAdapterStatus, sizeof(ADAPTER_STATUS)); + +#if defined(_PNP_POWER) + RtlCopyMemory (TempAdapterStatus.adapter_address, Device->Bind.Node, 6); +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + RtlCopyMemory (TempAdapterStatus.adapter_address, IpxAddress.NodeAddress, 6); +#endif _PNP_POWER + + + // + // Some of the fields mean different things for Novell Netbios, + // as described in the comments. + // + + TempAdapterStatus.rev_major = 0; // Jumpers + TempAdapterStatus.reserved0 = 0; // SelfTest + TempAdapterStatus.adapter_type = 0; // MajorVersion + TempAdapterStatus.rev_minor = 0; // MinorVersion + + TempAdapterStatus.duration = 0; // ReportingPeriod + TempAdapterStatus.frmr_recv = 0; // ReceiveCRCErrors + TempAdapterStatus.frmr_xmit = 0; // ReceiveAlignErrors + + TempAdapterStatus.iframe_recv_err = 0; // XmitCollisions + TempAdapterStatus.xmit_aborts = 0; // XmitAbort + + TempAdapterStatus.xmit_success = Device->Statistics.DataFramesSent; // SuccessfulXmits + TempAdapterStatus.recv_success = Device->Statistics.DataFramesReceived; // SuccessfulReceive + + TempAdapterStatus.iframe_xmit_err = (WORD)Device->Statistics.DataFramesResent; // XmitRetries + TempAdapterStatus.recv_buff_unavail = (WORD)Device->Statistics.DataFramesRejected; // ExhaustedResource + + // t1_timeouts, ti_timeouts, and reserved1 are unused. + + TempAdapterStatus.free_ncbs = 0xffff; // FreeBlocks + TempAdapterStatus.max_cfg_ncbs = 0xffff; // ConfiguredNCB + TempAdapterStatus.max_ncbs = 0xffff; // MaxNCB + + // xmit_bug_unavail and max_dgram_size are unused. + + TempAdapterStatus.pending_sess = (WORD)Device->Statistics.OpenConnections; // CurrentSessions + TempAdapterStatus.max_cfg_sess = 0xffff; // MaxSessionConfigured + TempAdapterStatus.max_sess = 0xffff; // MaxSessionPossible + TempAdapterStatus.max_sess_pkt_size = + Device->Bind.LineInfo.MaximumSendSize - sizeof(NB_CONNECTION); // MaxSessionPacketSize + + TempAdapterStatus.name_count = 0; + + + // + // Do a quick estimate of how many names we need room for. + // This includes stopping addresses and the broadcast + // address, for the moment. BUGBUG: Fix this? + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LengthNeeded = sizeof(ADAPTER_STATUS) + (Device->AddressCount * sizeof(NAME_BUFFER)); + + if (LengthNeeded > MaximumLength) { + LengthNeeded = MaximumLength; + } + + AdapterStatus = NbiAllocateMemory(LengthNeeded, MEMORY_STATUS, "Adapter Status"); + if (AdapterStatus == NULL) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + *StatusBuffer = AdapterStatus; + *StatusBufferLength = LengthNeeded; + + if (LengthNeeded < sizeof(ADAPTER_STATUS)) { + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, LengthNeeded); + *ValidBufferLength = LengthNeeded; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, sizeof(ADAPTER_STATUS)); + + BytesWritten = sizeof(ADAPTER_STATUS); + NameBuffer = (PNAME_BUFFER)(AdapterStatus+1); + NameCount = 0; + + // + // Scan through the device's address database, filling in + // the NAME_BUFFERs. + // + + Status = STATUS_SUCCESS; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + +#if defined(_PNP_POWER) + if ((Address->State != ADDRESS_STATE_OPEN) || + (Address->Flags & ADDRESS_FLAGS_CONFLICT)) { + continue; + } +#else + if ((Address->State != ADDRESS_STATE_OPEN) != 0) { + continue; + } +#endif _PNP_POWER + + // + // Ignore the broadcast address. + // + + if (Address->NetbiosAddress.Broadcast) { + continue; + } + + // + // Ignore our reserved address. + // +#if defined(_PNP_POWER) + if ( NbiFindAdapterAddress( + Address->NetbiosAddress.NetbiosName, + LOCK_ACQUIRED + )) { + continue; + } +#else + if (RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + Device->ReservedNetbiosName, + 16)) { + continue; + } + +#endif _PNP_POWER + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > LengthNeeded) { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory( + NameBuffer->name, + Address->NetbiosAddress.NetbiosName, + 16); + + ++NameCount; + NameBuffer->name_num = NameCount; + + NameBuffer->name_flags = REGISTERED; + if (Address->NameTypeFlag == NB_NAME_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // + // BUGBUG: name_flags should be done more accurately. + // + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + AdapterStatus->name_count = (WORD)NameCount; + *ValidBufferLength = BytesWritten; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Status; + +} /* NbiStoreAdapterStatus */ + + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ) + +/*++ + +Routine Description: + + This routine updates the find name request with the + new information received. It updates the status in + the request if needed. + +Arguments: + + Request - The netbios find name request. + + NicId - The NIC ID the response was received on. + + RemoteIpxAddress - The IPX address of the remote. + + Unique - TRUE if the name is unique. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + FIND_NAME_BUFFER UNALIGNED * FindNameBuffer; + UINT FindNameBufferLength; + TDI_ADDRESS_IPX LocalIpxAddress; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + NTSTATUS QueryStatus; + UINT i; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // Scan through the names saved so far and see if this one + // is there. + // + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *)(FindNameHeader+1); + + for (i = 0; i < FindNameHeader->node_count; i++) { + + if (RtlEqualMemory( + FindNameBuffer->source_addr, + RemoteIpxAddress->NodeAddress, + 6)) { + + // + // This remote already responded, ignore it. + // + + return; + + } + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *) + (((PUCHAR)FindNameBuffer) + 33); + + } + + // + // Make sure there is room for this new node. 33 is + // sizeof(FIND_NAME_BUFFER) without padding. + // + + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER) + ((FindNameHeader->node_count+1) * 33)) { + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + return; + } + + // + // Query the local address, which we will return as + // the destination address of this query. + // + +#if defined(_PNP_POWER) + if( (*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicHandle, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + // + // Ignore this response if the query fails. maybe the NicHandle + // is bad or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + NicHandle->NicId )); + return; + } +#else + (VOID)(*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + FindNameBuffer->access_control = 0x10; // standard token-ring values + FindNameBuffer->frame_control = 0x40; + RtlMoveMemory (FindNameBuffer->destination_addr, LocalIpxAddress.NodeAddress, 6); + RtlCopyMemory (FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, RemoteIpxAddress->NodeAddress, 6); + + QueryStatus = (*NbiDevice->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + NicHandle, +#else + NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(FindNameBuffer->routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + FindNameBuffer->routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + FindNameBuffer->length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + ++FindNameHeader->node_count; + if (!Unique) { + FindNameHeader->unique_group = 1; // group + } + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + +} /* NbiUpdateNetbiosFindName */ + + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sets the REQUEST_INFORMATION field to the right + value based on the number of responses recorded in the netbios + find name request's buffer. + +Arguments: + + Request - The netbios find name request. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + REQUEST_INFORMATION(Request) = sizeof(FIND_NAME_HEADER) + (FindNameHeader->node_count * 33); + +} /* NbiSetNetbiosFindNameInformation */ + + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* NbiTdiSetInformation */ + + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_QUERY frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LINE_INFO LineInfo; + ULONG ResponseSize; + NTSTATUS Status; + PNDIS_BUFFER AdapterStatusBuffer; + PADAPTER_STATUS AdapterStatus; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + PDEVICE Device = NbiDevice; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + + + // + // The old stack does not include the 14 bytes of padding in + // the 802.3 or IPX length of the packet. + // + + if (PacketSize < (sizeof(IPX_HEADER) + 2)) { + return; + } + + // + // Get the maximum size we can send. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + &RemoteAddress->NicHandle, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL) != STATUS_SUCCESS ) { + // + // Bad NicHandle or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_LINE_INFO, + RemoteAddress->NicHandle.NicId )); + + return; + } + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } +#else + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + // + // Get the maximum size we can send. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + RemoteAddress->NicId, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL); +#endif _PNP_POWER + + ResponseSize = LineInfo.MaximumSendSize - sizeof(IPX_HEADER) - sizeof(NB_STATUS_RESPONSE); + + // + // Get the local adapter status (this allocates a buffer). + // + + Status = NbiStoreAdapterStatus( + ResponseSize, +#if defined(_PNP_POWER) + RemoteAddress->NicHandle.NicId, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &AdapterStatusBuffer, + Device->NdisBufferPoolHandle, + AdapterStatus, + ValidStatusLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + NB_DEBUG2 (QUERY, ("Reply to AdapterStatus from %lx %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(UNALIGNED ULONG *)Connectionless->IpxHeader.SourceNetwork, + Connectionless->IpxHeader.SourceNode[0], + Connectionless->IpxHeader.SourceNode[1], + Connectionless->IpxHeader.SourceNode[2], + Connectionless->IpxHeader.SourceNode[3], + Connectionless->IpxHeader.SourceNode[4], + Connectionless->IpxHeader.SourceNode[5])); + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_RESPONSE; + Reserved->u.SR_AS.ActualBufferLength = AdapterStatusLength; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, Connectionless->IpxHeader.SourceNetwork, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_RESPONSE; + + NbiReferenceDevice (Device, DREF_STATUS_RESPONSE); + + NdisChainBufferAtBack (Packet, AdapterStatusBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + RemoteAddress, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + ValidStatusLength, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends NB_CMD_STATUS_QUERY frames. + +Arguments: + + Request - Holds the request describing the remote adapter + status query. REQUEST_STATUS(Request) points + to the netbios cache entry for the remote name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNETBIOS_CACHE CacheName; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_QUERY; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(Request); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + LocalTarget = &CacheName->Networks[0].LocalTarget; + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_QUERY; + + NbiReferenceDevice (Device, DREF_STATUS_FRAME); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY), + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_RESPONSE frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + PREQUEST AdapterStatusRequest; + PNETBIOS_CACHE CacheName; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNDIS_BUFFER TargetBuffer; + ULONG TargetBufferLength, BytesToTransfer; + ULONG BytesTransferred; + NDIS_STATUS NdisStatus; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + BOOLEAN Found; + PNAME_BUFFER NameBuffer; + UINT i,NameCount = 0; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) { + return; + } + + // + // Find out how many names are there. + // + NameBuffer = (PNAME_BUFFER)(LookaheadBuffer + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)); + if ( LookaheadBufferSize > sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS) ) { + NameCount = (LookaheadBufferSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)) ) / + sizeof(NAME_BUFFER); + } + // + // Find a request queued to this remote. If there are + // multiple requests outstanding for the same name we + // should get multiple responses, so we only need to + // find one. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Found = FALSE; + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if ( CacheName->Unique ) { + if (RtlEqualMemory( + &CacheName->FirstResponse, + Connectionless->IpxHeader.SourceNetwork, + 12)) { + Found = TRUE; + break; + } + } else if ( RtlEqualMemory( CacheName->NetbiosName,NetbiosBroadcastName,16)){ + // + // It's a broadcast name. Any response is fine. + // + Found = TRUE; + break; + } else { + // + // It's group name. Make sure that this remote + // has this group name registered with him. + // + for (i =0;iNetbiosName, + NameBuffer[i].name, + 16)) && + + (NameBuffer[i].name_flags & GROUP_NAME) ) { + + Found = TRUE; + break; + } + } + } + + } + + if (!Found) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (QUERY, ("Got response to AdapterStatus %lx\n", AdapterStatusRequest)); + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + REQUEST_STATUS (AdapterStatusRequest) = STATUS_INSUFFICIENT_RESOURCES; + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->Type = RECEIVE_TYPE_ADAPTER_STATUS; + ReceiveReserved->u.RR_AS.Request = AdapterStatusRequest; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_SUCCESS; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // Now that we have a packet and a buffer, set up the transfer. + // We will complete the request when the transfer completes. + // + + TargetBuffer = REQUEST_NDIS_BUFFER (AdapterStatusRequest); + + NdisChainBufferAtFront (Packet, TargetBuffer); + + NbiGetBufferChainLength (TargetBuffer, &TargetBufferLength); + BytesToTransfer = PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if (TargetBufferLength < BytesToTransfer) { + BytesToTransfer = TargetBufferLength; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_BUFFER_OVERFLOW; + } + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)), + BytesToTransfer, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessStatusResponse */ + diff --git a/private/ntos/tdi/isnp/nb/receive.c b/private/ntos/tdi/isnp/nb/receive.c new file mode 100644 index 000000000..54ce78944 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/receive.c @@ -0,0 +1,1303 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains the code to handle receive indication + and posted receives for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This routine is a no-op to put in the NbiCallbacks table so +// we can avoid checking for runt session frames (this is because +// of how the if is structure below). +// + +VOID +NbiProcessSessionRunt( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) +{ + return; +} + +NB_CALLBACK_NO_TRANSFER NbiCallbacksNoTransfer[] = { + NbiProcessFindName, + NbiProcessNameRecognized, + NbiProcessAddName, + NbiProcessAddName, // processes name in use frames also + NbiProcessDeleteName, + NbiProcessSessionRunt, // in case get a short session packet + NbiProcessSessionEnd, + NbiProcessSessionEndAck, + NbiProcessStatusQuery + }; + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiProcessDeathPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + DbgPrint("******Received death packet - connid %x\n",Sess->DestConnectionId); + + if ( !NbiGlobalDebugResTimeout ) { + return; + } + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + DbgPrint("********No Connection found with %x id\n",Sess->DestConnectionId); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + DbgPrint("******Received death packet on conn %lx from <%.16s>\n",Connection,Connection->RemoteName); + DbgBreakPoint(); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles receive indications from IPX. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PNB_FRAME NbFrame = (PNB_FRAME)LookaheadBuffer; + UCHAR DataStreamType; + + // + // We know that this is a frame with a valid IPX header + // because IPX would not give it to use otherwise. However, + // it does not check the source socket. + // + + if (NbFrame->Connectionless.IpxHeader.SourceSocket != NB_SOCKET) { + return; + } + + ++NbiDevice->Statistics.PacketsReceived; + + // First assume that the DataStreamType is at the normal place i.e 2nd byte + // + + // Now see if this is a name frame. + // + if ( PacketSize == sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME) ) { + // In the internet mode, the DataStreamType2 becomes DataStreamType + if (NbFrame->Connectionless.IpxHeader.PacketType == 0x14 ) { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType2; + } else { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + } + + // Is this a name frame? + // NB_CMD_FIND_NAME = 1 .... NB_CMD_DELETE_NAME = 5 + // + if ((DataStreamType >= NB_CMD_FIND_NAME) && (DataStreamType <= NB_CMD_DELETE_NAME)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + return; + } + + } + +#ifdef RSRC_TIMEOUT_DBG + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_DEATH_PACKET)) { + + NbiProcessDeathPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } +#endif //RSRC_TIMEOUT_DBG + + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_SESSION_DATA)) { + + NbiProcessSessionData( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else { + + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + // Handle NB_CMD_SESSION_END = 7 ... NB_CMD_STATUS_QUERY = 9 + // + if ((DataStreamType >= NB_CMD_SESSION_END ) && (DataStreamType <= NB_CMD_STATUS_QUERY)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + + } else if (DataStreamType == NB_CMD_STATUS_RESPONSE) { + + NbiProcessStatusResponse( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else if ((DataStreamType == NB_CMD_DATAGRAM) || + (DataStreamType == NB_CMD_BROADCAST_DATAGRAM)) { + + NbiProcessDatagram( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize, + (BOOLEAN)(DataStreamType == NB_CMD_BROADCAST_DATAGRAM)); + + } + + } + +} /* NbiReceive */ + + +VOID +NbiReceiveComplete( + IN USHORT NicId + ) + +/*++ + +Routine Description: + + This routine handles receive complete indications from IPX. + +Arguments: + + NicId - The NIC ID on which a receive was previously indicated. + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS Address; + PREQUEST Request; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PDEVICE Device = NbiDevice; + LIST_ENTRY LocalList; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + // + // Complete any pending receive requests. + // + + + if (!IsListEmpty (&Device->ReceiveCompletionQueue)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveCompletionQueue, p)) { + + Request = LIST_ENTRY_TO_REQUEST (p); + + // + // BUGBUG: Cache the connection somewhere easier + // to retrieve? + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + } + + } + + + // + // Indicate any datagrams to clients. + // + + if (!IsListEmpty (&Device->ReceiveDatagrams)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveDatagrams, p)) { + + ReceiveBuffer = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER, WaitLinkage); + Address = ReceiveBuffer->Address; + + NbiIndicateDatagram( + Address, + ReceiveBuffer->RemoteName, + ReceiveBuffer->Data, + ReceiveBuffer->DataLength); + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + } + } + + + // + // Start packetizing connections. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Check again because it may just have become + // empty, and the code below depends on it being + // non-empty. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + // + // We copy the list locally, in case someone gets + // put back on it. We have to hack the end so + // it points to LocalList instead of PacketizeConnections. + // + + LocalList = Device->PacketizeConnections; + LocalList.Flink->Blink = &LocalList; + LocalList.Blink->Flink = &LocalList; + + InitializeListHead (&Device->PacketizeConnections); + + // + // Set all these connections to not be on the list, so + // NbiStopConnection won't try to take them off. + // + + for (p = LocalList.Flink; p != &LocalList; p = p->Flink) { + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + CTEAssert (Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = FALSE; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + while (TRUE) { + + p = RemoveHeadList (&LocalList); + if (p == &LocalList) { + break; + } + + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + } + } + +} /* NbiReceiveComplete */ + + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine handles a transfer data complete indication from + IPX, indicating that a previously issued NdisTransferData + call has completed. + +Arguments: + + Packet - The packet associated with the transfer. + + Status - The status of the transfer. + + BytesTransferred - The number of bytes transferred. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PADDRESS Address; + PCONNECTION Connection; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PREQUEST AdapterStatusRequest; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + ReceiveReserved = (PNB_RECEIVE_RESERVED)(Packet->ProtocolReserved); + + switch (ReceiveReserved->Type) { + + case RECEIVE_TYPE_DATA: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + Connection = ReceiveReserved->u.RR_CO.Connection; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Status != NDIS_STATUS_SUCCESS) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive = Connection->PreviousReceive; + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // BUGBUG: Send a resend ack? + // + + } else { + + // + // This aborts the current receive and + // releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } else { + + + Connection->CurrentReceive.Offset += BytesTransferred; + Connection->CurrentReceive.MessageOffset += BytesTransferred; + + if (ReceiveReserved->u.RR_CO.CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (ReceiveReserved->u.RR_CO.EndOfMessage) { + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (ReceiveReserved->u.RR_CO.PartialReceive) { + Connection->CurrentIndicateOffset += BytesTransferred; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + } + + // + // Free the NDIS buffer chain if we allocated one. + // + + if (!ReceiveReserved->u.RR_CO.NoNdisBuffer) { + + NdisQueryPacket (Packet, NULL, NULL, &CurBuffer, NULL); + + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + break; + + case RECEIVE_TYPE_DATAGRAM: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + ReceiveBuffer = ReceiveReserved->u.RR_DG.ReceiveBuffer; + + // + // Free the packet used for the transfer. + // + + ReceiveReserved->u.RR_DG.ReceiveBuffer = NULL; + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // If it succeeded then queue it for indication, + // otherwise free the receive buffer also. + // + + if (Status == STATUS_SUCCESS) { + + ReceiveBuffer->DataLength = BytesTransferred; + NB_INSERT_HEAD_LIST( + &Device->ReceiveDatagrams, + &ReceiveBuffer->WaitLinkage, + &Device->Lock); + + } else { + + Address = ReceiveBuffer->Address; + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + break; + + case RECEIVE_TYPE_ADAPTER_STATUS: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + AdapterStatusRequest = ReceiveReserved->u.RR_AS.Request; + + // + // Free the packet used for the transfer. + // + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // Complete the request. + // + + if (Status == STATUS_SUCCESS) { + + // + // REQUEST_STATUS() is already to set to SUCCESS or + // BUFFER_OVERFLOW based on whether the buffer was + // big enough. + // + + REQUEST_INFORMATION(AdapterStatusRequest) = BytesTransferred; + + } else { + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_UNEXPECTED_NETWORK_ERROR; + + } + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + break; + + } + +} /* NbiTransferDataComplete */ + + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when a receive needs to be acked to + the remote. It either sends a data ack or queues up a piggyback + ack request. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - Pointer to the connection. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + + if (Connection->NewNetbios) { + + // + // CurrentReceiveNoPiggyback is based on the bits he + // set in his frame, NoPiggybackHeuristic is based on + // guesses about the traffic pattern, it is set to + // TRUE if we think we should not piggyback. + // + + if ((!Device->EnablePiggyBackAck) || + (Connection->CurrentReceiveNoPiggyback) || + (Connection->PiggybackAckTimeout) || + (Connection->NoPiggybackHeuristic)) { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (!Connection->DataAckPending) { + + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + // + // Some stacks can have multiple messages + // outstanding, so we may already have an + // ack queued. + // + + Connection->DataAckTimeouts = 0; + Connection->DataAckPending = TRUE; + + ++Device->Statistics.PiggybackAckQueued; + + if (!Connection->OnDataAckQueue) { + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle1); + + if (!Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = TRUE; + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + } + + if (!Device->DataAckActive) { + NbiStartShortTimer (Device); + Device->DataAckActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle1); + } + + // + // Clear this, since a message ack resets the count. + // + + Connection->ReceivesWithoutAck = 0; + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + +} + + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have filled up a receive request + and need to complete it. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + + THIS ROUTINE ALSO HOLDS CANCEL SPIN LOCK WHEN IT IS CALLED + AND RELEASES IT WHEN IT RETURNS. +Arguments: + + Connection - Pointer to the connection. + + EndOfMessage - BOOLEAN set to true if the message end was received. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PDEVICE Device = NbiDevice; + + // + // Complete the current receive request. If the connection + // has shut down then we complete it right here, otherwise + // we queue it for completion in the receive complete + // handler. + // + + Request = Connection->ReceiveRequest; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveRequest = NULL; // StopConnection won't do this + + REQUEST_STATUS(Request) = Connection->Status; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + ++Connection->ConnectionInfo.ReceiveErrors; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } else { + + REQUEST_INFORMATION (Request) = Connection->CurrentReceive.Offset; + + if (EndOfMessage) { + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveUnaccepted) { + NB_DEBUG2 (RECEIVE, ("Moving Unaccepted %d down by %d\n", + Connection->ReceiveUnaccepted, Connection->CurrentReceive.Offset)); + if (Connection->CurrentReceive.Offset >= Connection->ReceiveUnaccepted) { + Connection->ReceiveUnaccepted = 0; + } else { + Connection->ReceiveUnaccepted -= Connection->CurrentReceive.Offset; + } + } + + // + // BUGBUG: Check whether to activate another receive? + // + + Connection->ReceiveState = CONNECTION_RECEIVE_PENDING; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + if (EndOfMessage) { + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (Connection->CurrentIndicateOffset != 0) { + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + } + + } else { + + NbiSendDataAck( + Connection, + EndOfMessage ? NbiAckResponse : NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // This will complete the request inside ReceiveComplete, + // dereference the connection, and set the state to IDLE. + // + + NB_INSERT_TAIL_LIST( + &Device->ReceiveCompletionQueue, + REQUEST_LINKAGE (Request), + &Device->Lock); + + } + +} /* NbiCompleteReceive */ + + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a receive on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the receive. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PCONNECTION Connection; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelReceive); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + // + // Insert this in our queue, then see if we need + // to wake up the remote. + // + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->ReceiveQueue, Request); + + if (Connection->ReceiveState != CONNECTION_RECEIVE_W_RCV) { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx idle\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx awakened\n", Request, Connection)); + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = (USHORT) + (Connection->ReceiveSequence + Connection->ReceiveWindowSize - 1); + + } + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (RECEIVE, ("Receive connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiReceive */ + + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The request is found on the connection's receive queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + + // + // Just stop the connection, that will tear down any + // receives. + // + // BUGBUG: Do we care about cancelling non-active + // receives without stopping the connection?? + // + // BUGBUG: This routine is the same as NbiCancelSend, + // so if we don't make it more specific, merge the two. + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelReceive */ + diff --git a/private/ntos/tdi/isnp/nb/send.c b/private/ntos/tdi/isnp/nb/send.c new file mode 100644 index 000000000..a4443c73c --- /dev/null +++ b/private/ntos/tdi/isnp/nb/send.c @@ -0,0 +1,2886 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains the send routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +) + +/*++ + +Routine Description: + + This routine handles a send completion call from IPX. + +Arguments: + + Packet - The packet which has been completed. + + Status - The status of the send. + +Return Value: + + None. + +--*/ + + + +{ + PDEVICE Device = NbiDevice; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST DatagramRequest; + PREQUEST SendRequest, TmpRequest; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PNETBIOS_CACHE CacheName; + PNDIS_BUFFER SecondBuffer; + PVOID SecondBufferMemory; + UINT SecondBufferLength; + ULONG oldvalue; + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + CTELockHandle CancelLH; +#if defined(_PNP_POWER) + CTELockHandle LockHandle; +#endif _PNP_POWER + + // + // We jump back here if we re-call send from inside this + // function and it doesn't pend (to avoid stack overflow). + // + +FunctionStart:; + + ++Device->Statistics.PacketsSent; + + switch (Reserved->Type) { + + case SEND_TYPE_SESSION_DATA: + + // + // This was a send on a session. This references the + // IRP. + // + + NB_DEBUG2 (SEND, ("Complete NDIS packet %lx\n", Reserved)); + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + SendRequest = Reserved->u.SR_CO.Request; + + if (!Reserved->u.SR_CO.NoNdisBuffer) { + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + // + // If NoNdisBuffer is TRUE, then we could set + // Connection->SendBufferInUse to FALSE here. The + // problem is that a new send might be in progress + // by the time this completes and it may have + // used the user buffer, then if we need to + // retransmit that packet we would use the buffer + // twice. We instead rely on the fact that whenever + // we make a new send active we set SendBufferInUse + // to FALSE. The net effect is that the user's buffer + // can be used the first time a send is packetize + // but not on resends. + // + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + +#if DBG + if (REQUEST_REFCOUNT(SendRequest) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, SendRequest); + DbgBreakPoint(); + } +#endif + +#if defined(__PNP) + NB_GET_LOCK( &Connection->Lock, &LockHandle ); + oldvalue = REQUEST_REFCOUNT(SendRequest)--; + if ( DEVICE_NETWORK_PATH_NOT_FOUND == Status ) { + Connection->LocalTarget = Reserved->LocalTarget; + } + NB_FREE_LOCK( &Connection->Lock, LockHandle ); +#else + oldvalue = NB_ADD_ULONG( + &REQUEST_REFCOUNT (SendRequest), + (ULONG)-1, + &Connection->Lock); +#endif __PNP + + if (oldvalue == 1) { + + // + // If the refcount on this request is now zero then + // we already got the ack for it, which means + // that the ack-processing code has unlinked the + // request from Connection->SendQueue. So we + // can just run the queue of connections here + // and complete them. + // + // We dereference the connection for all but one + // of the requests, we hang on to that until a bit + // later so everything stays around. + // + + while (TRUE) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + NB_DEBUG2 (SEND, ("Completing request %lx from send complete\n", SendRequest)); + REQUEST_STATUS (SendRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + ++Connection->ConnectionInfo.TransmittedTsdus; + SendRequest = TmpRequest; + + if (SendRequest == NULL) { + break; + } + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } + + if (Reserved->OwnedByConnection) { + + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (oldvalue == 1) { + NbiDereferenceConnection (Connection, CREF_SEND); + } + + break; + + case SEND_TYPE_NAME_FRAME: + + // + // The frame is an add name/delete name; put it back in + // the pool and deref the address. + // + + CTEAssert (Reserved->SendInProgress); + + Address = Reserved->u.SR_NF.Address; + +#if !defined(_PNP_POWER) + if ((Reserved->u.SR_NF.CurrentNicId) && + (Reserved->u.SR_NF.CurrentNicId < Device->MaximumNicId)) { + + NB_CONNECTIONLESS UNALIGNED * Header; + IPX_LOCAL_TARGET TempLocalTarget; + + // + // This is a name frame being sent to every address, so + // resent it to the next NIC ID. We hold the address + // reference through this send. + // + + CTEAssert (Address != NULL); + + ++Reserved->u.SR_NF.CurrentNicId; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = Reserved->u.SR_NF.DataStreamType; + Header->NameFrame.NameTypeFlag = Reserved->u.SR_NF.NameTypeFlag; + + // + // This is not a name in use frame so DataStreamType2 + // is the same as DataStreamType. + // + + Header->NameFrame.DataStreamType2 = Reserved->u.SR_NF.DataStreamType; + + RtlCopyMemory( + Header->NameFrame.Name, + Address->NetbiosAddress.NetbiosName, + 16); + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + TempLocalTarget.NicId = Reserved->u.SR_NF.CurrentNicId; + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)); + if ((Status = + (*Device->Bind.SendHandler)( + &TempLocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + goto FunctionStart; + + } + + return; + + } +#endif !_PNP_POWER + + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + if (Address) { + NbiDereferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_NAME_FRAME); + } + + break; + + case SEND_TYPE_SESSION_INIT: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT)); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_SESSION_INIT); + + break; + + case SEND_TYPE_SESSION_NO_DATA: + + // + // This is a frame which was sent on a connection but + // has no data (ack, session end, session end ack). + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + + if (Reserved->OwnedByConnection) { + + CTEAssert (Connection != NULL); + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (Connection != NULL) { + NbiDereferenceConnection (Connection, CREF_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_FRAME); + } + + break; + + case SEND_TYPE_FIND_NAME: + + // + // The frame is a find name; just set SendInProgress to + // FALSE and FindNameTimeout will clean it up. + // +#if defined(_PNP_POWER) + NB_GET_LOCK( &Device->Lock, &LockHandle); + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + if ( STATUS_SUCCESS == Status ) { + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } else { + NB_DEBUG( CACHE, ("Send complete of find name with failure %lx\n",Status )); + } + NB_FREE_LOCK(&Device->Lock, LockHandle); +#else + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +#endif _PNP_POWER + break; + + case SEND_TYPE_DATAGRAM: + + // + // If there are any more networks to send this on then + // do so, otherwise put it back in the pool and complete + // the request. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + if ((Reserved->u.SR_DG.Cache == NULL) || + (++Reserved->u.SR_DG.CurrentNetwork >= + Reserved->u.SR_DG.Cache->NetworksUsed)) { + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Completing datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // Remove any user buffers chained on this packet. + // + + NdisReinitializePacket (Packet); + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisChainBufferAtFront (Packet, Reserved->HeaderBuffer); + + // + // Complete the request. + // + + REQUEST_STATUS(DatagramRequest) = Status; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + CacheName = Reserved->u.SR_DG.Cache; + + NbiPushSendPacket (Reserved); + + // + // Since we are no longer referencing the cache + // name, see if we should delete it (this will + // happen if the cache entry was aged out while + // the datagram was being processed). + // + + if (CacheName != NULL) { + + oldvalue = NB_ADD_ULONG( + &CacheName->ReferenceCount, + (ULONG)-1, + &Device->Lock); + + if (oldvalue == 1) { + + NB_DEBUG2 (CACHE, ("Free aged cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free old cache"); + + } + } + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + } else { + + NB_CONNECTIONLESS UNALIGNED * Header; + PIPX_LOCAL_TARGET LocalTarget; + ULONG HeaderLength; + ULONG PacketLength; + + // send the datagram on the next net. + CTEAssert (!Reserved->u.SR_DG.Cache->Unique); + Reserved->SendInProgress = TRUE; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + if ((Status = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + goto FunctionStart; + } + + } + + break; + + case SEND_TYPE_STATUS_QUERY: + + // + // This is an adapter status query, which is a simple + // packet. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_FRAME); + + break; + + case SEND_TYPE_STATUS_RESPONSE: + + // + // This is an adapter status response, we have to free the + // second buffer. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS, "Adapter Status"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_RESPONSE); + + break; + +#ifdef RSRC_TIMEOUT_DBG + case SEND_TYPE_DEATH_PACKET: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + DbgPrint("********Death packet send completed status %lx\n",Status); + DbgBreakPoint(); + break; +#endif //RSRC_TIMEOUT_DBG + + default: + + CTEAssert (FALSE); + break; + + } + +} /* NbiSendComplete */ + +#if 0 +ULONG NbiLoudSendQueue = 1; +#endif + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of the lower-level + send handler; after assigning the receive sequence number it locks out + other sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD, AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection the send is on. + + Packet - The packet to send. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNB_SEND_RESERVED Reserved; + PLIST_ENTRY p; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + BOOLEAN NdisSendReference; + ULONG result; + + + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + CTEAssert (Connection->State == CONNECTION_STATE_ACTIVE); + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Connection->NdisSendsInProgress > 0) { + + NB_DEBUG2 (SEND, ("Queueing send packet %lx on %lx\n", Reserved, Connection)); + InsertTailList (&Connection->NdisSendQueue, &Reserved->WaitLinkage); + ++Connection->NdisSendsInProgress; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence fields in the packet. + // + + Connection->NdisSendsInProgress = 1; + NdisSendReference = FALSE; + Connection->NdisSendReference = &NdisSendReference; + + while (TRUE) { + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; + } + + // + // Since we are acking as much as we know, we can clear + // this flag. The connection will eventually get removed + // from the queue by the long timeout. + // + + Connection->DataAckPending = FALSE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + NdisStatus = (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + Reserved->u.SR_CO.PacketLength, + sizeof(NB_CONNECTION)); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = NB_ADD_ULONG( + &Connection->NdisSendsInProgress, + (ULONG)-1, + &Connection->Lock); + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. If the connection was stopped while we were sending, + // a special reference was added which we remove (NbiStopConnection + // sets NdisSendReference to TRUE, using the pointer saved + // in Connection->NdisSendReference). + // + + if (result == 1) { + if (NdisSendReference) { + NB_DEBUG2 (SEND, ("Remove CREF_NDIS_SEND from %lx\n", Connection)); + NbiDereferenceConnection (Connection, CREF_NDIS_SEND); + } + return; + } + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + p = RemoveHeadList(&Connection->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Connection->NdisSendQueue); + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } // while loop + + // + // We should never reach here. + // + + CTEAssert (FALSE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiAssignSequenceAndSend */ + + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + PTDI_REQUEST_KERNEL_SEND Parameters; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + + // + // For old netbios, don't allow sends greater than 64K-1. + // + + if ((Connection->NewNetbios) || + (Parameters->SendLength <= 0xffff)) { + + IoSetCancelRoutine (Request, NbiCancelSend); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_INFORMATION (Request) = Parameters->SendLength; // assume it succeeds. + + REQUEST_REFCOUNT (Request) = 1; // refcount starts at 1. + NbiReferenceConnectionSync (Connection, CREF_SEND); + + // + // NOTE: The connection send queue is managed such + // that the current send being packetized is not on + // the queue. For multiple-request messages, the + // first one is not on the queue, but its linkage + // field points to the next request in the message + // (which will be on the head of the queue). + // + + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a final send. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx idle\n", Request, Connection)); + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have been collecting partial sends waiting + // for a final one, which we have now received, + // so start packetizing. + // + // We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx got eor\n", Request, Connection)); + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + + Connection->UnAckedSend = Connection->CurrentSend; +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + // + // The state is PACKETIZE, W_ACK, or W_PACKET. + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx busy\n", Request, Connection)); + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // This is a partial send. We queue them up without + // packetizing until we get a final (this is because + // we have to put a correct Connection->CurrentMessageLength + // in the frames. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + // + // Start collecting partial sends. NOTE: Partial sends + // are always inserted in the send queue + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have got another partial send to add to our + // list. We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } else { + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, too long for connection %lx (%d)\n", Request, Connection, Parameters->SendLength)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_PARAMETER; + + } + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiSend */ + + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + PNB_SEND_RESERVED Reserved; + PDEVICE Device = NbiDevice; + NB_CONNECTION UNALIGNED * Header; + ULONG PacketLength; + ULONG PacketSize; + ULONG DesiredLength; + ULONG ActualLength; + NTSTATUS Status; + PSINGLE_LIST_ENTRY s; + USHORT ThisSendSequence; + USHORT ThisOffset; + BOOLEAN ExitAfterSend; + UCHAR ConnectionControlFlag; + CTELockHandle DeviceLockHandle; + + // + // We jump back here if we are talking new Netbios and it + // is OK to packetize another send. + // + +SendAnotherPacket: + + // + // If we decide to packetize another send after this, we + // change ExitAfterSend to FALSE and SubState to PACKETIZE. + // Right now we don't change SubState in case it is W_PACKET. + // + + ExitAfterSend = TRUE; + + CTEAssert (Connection->CurrentSend.Request != NULL); + + if (Connection->NewNetbios) { + + // + // Check that we have send window, both that advertised + // by the remote and our own locally-decided window which + // may be smaller. + // + + if (((USHORT)(Connection->CurrentSend.SendSequence-1) == Connection->RemoteRcvSequenceMax) || + (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize)) { + + // + // Keep track of whether we are waiting because of his window + // or because of our local window. If it is because of our local + // window then we may want to adjust it after this window + // is acked. + // + + if ((USHORT)(Connection->CurrentSend.SendSequence-1) != Connection->RemoteRcvSequenceMax) { + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + NB_DEBUG2 (SEND, ("Connection %lx local shut down at %lx, %lx\n", Connection, Connection->CurrentSend.SendSequence, Connection->UnAckedSend.SendSequence)); + } else { + Connection->SubState = CONNECTION_SUBSTATE_A_REMOTE_W; + NB_DEBUG2 (SEND, ("Connection %lx remote shut down at %lx\n", Connection, Connection->CurrentSend.SendSequence)); + } + + // + // Start the timer so we will keep bugging him about + // this. BUGBUG: What if he doesn't get a receive down + // quickly -- but this is better than losing his ack + // and then dying. We won't really back off our timer + // because we will keep getting acks, and resetting it. + // + + NbiStartRetransmit (Connection); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + + } + + Request = Connection->CurrentSend.Request; + + // + // If we are in this routine then we know that + // we are coming out of IDLE, W_ACK, or W_PACKET + // and we still have the lock held. We also know + // that there is a send request in progress. If + // an ack for none or part of the last packet was + // received, then our send pointers have been + // adjusted to reflect that. + // + + // + // First get a packet for the current send. + // + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s == NULL) { + + // + // This function tries to allocate another packet pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + if (s == NULL) { + + // + // It is possible to come in here and already be in + // W_PACKET state -- this is because we may packetize + // when in that state, and rather than always be + // checking that we weren't in W_PACKET, we go + // ahead and check again here. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PACKET) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_PACKET; + + NB_GET_LOCK (&Device->Lock, &DeviceLockHandle); + if (!Connection->OnWaitPacketQueue) { + + NbiReferenceConnectionLock (Connection, CREF_W_PACKET); + + Connection->OnWaitPacketQueue = TRUE; + + InsertTailList( + &Device->WaitPacketConnections, + &Connection->WaitPacketLinkage + ); + +// NB_INSERT_TAIL_LIST( +// &Device->WaitPacketConnections, +// &Connection->WaitPacketLinkage, +// &Device->Lock); + + } + NB_FREE_LOCK (&Device->Lock, DeviceLockHandle); + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + // + // Set this now, we will change it later if needed. + // + + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + + + // + // Save these since they go in this next packet. + // + + ThisSendSequence = Connection->CurrentSend.SendSequence; + ThisOffset = (USHORT)Connection->CurrentSend.MessageOffset; + + + // + // Now see if we need to copy the buffer chain. + // + + PacketSize = Connection->MaximumPacketSize; + + if (Connection->CurrentSend.MessageOffset + PacketSize >= Connection->CurrentMessageLength) { + + PacketSize = Connection->CurrentMessageLength - Connection->CurrentSend.MessageOffset; + + if ((Connection->CurrentSend.MessageOffset == 0) && + (!Connection->SendBufferInUse)) { + + // + // If the entire send remaining fits in one packet, + // and this is also the first packet in the send, + // then the entire send fits in one packet and + // we don't need to build a duplicate buffer chain. + // + + BufferChain = Connection->CurrentSend.Buffer; + Reserved->u.SR_CO.NoNdisBuffer = TRUE; + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + Connection->CurrentSend.MessageOffset = Connection->CurrentMessageLength; + Connection->CurrentSend.Request = NULL; + ++Connection->CurrentSend.SendSequence; + Connection->SendBufferInUse = TRUE; + if (Connection->NewNetbios) { + if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + } else { + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + } else { + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + + if (BufferChain != NULL) { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), user buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + NdisChainBufferAtBack (Packet, BufferChain); + } else { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + } + + goto GotBufferChain; + + } + + } + + // + // We need to build a partial buffer chain. In the case + // where the current request is a partial one, we may + // build this from the ndis buffer chains of several + // requests. + // + + if (PacketSize > 0) { + + DesiredLength = PacketSize; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), allocate buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + while (TRUE) { + + Status = NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentSend.Buffer, + Connection->CurrentSend.BufferOffset, + DesiredLength, + &BufferChain, + &Connection->CurrentSend.Buffer, + &Connection->CurrentSend.BufferOffset, + &ActualLength); + + if (Status != STATUS_SUCCESS) { + + PNDIS_BUFFER CurBuffer, TmpBuffer; + + NB_DEBUG2 (SEND, ("Allocate buffer chain failed for packet %lx\n", Reserved)); + + // + // We could not allocate resources for this send. + // We'll put the connection on the packetize + // queue and hope we get more resources later. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + // + // Connection->CurrentSend can stay where it is. + // + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Free any buffers we have allocated on previous calls + // to BuildBufferChain inside this same while(TRUE) loop, + // then free the packet. + // + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + + if (Reserved->OwnedByConnection) { + Connection->SendPacketInUse = FALSE; + } else { + NbiPushSendPacket(Reserved); + } + + return; + + } + + NdisChainBufferAtBack (Packet, BufferChain); + Connection->CurrentSend.MessageOffset += ActualLength; + + DesiredLength -= ActualLength; + + if (DesiredLength == 0) { + + // + // We have gotten enough data for our packet. + // + + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + Connection->CurrentSend.Request = NULL; + } + break; + } + + // + // We ran out of buffer chain on this send, which means + // that we must have another one behind it (since we + // don't start packetizing partial sends until all of + // them are queued). + // + + Request = REQUEST_SINGLE_LINKAGE(Request); + if (Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + + } + + } else { + + // + // This is a zero-length send (in general we will go + // through the code before the if that uses the user's + // buffer, but not on a resend). + // + + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + CTEAssert (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength); + Connection->CurrentSend.Request = NULL; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no alloc buf\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + } + + Reserved->u.SR_CO.NoNdisBuffer = FALSE; + + if (Connection->NewNetbios) { + + ++Connection->CurrentSend.SendSequence; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + + if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + + } else if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else if (ThisSendSequence == Connection->RemoteRcvSequenceMax) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = 0; + ExitAfterSend = FALSE; + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } + + } else { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + ++Connection->CurrentSend.SendSequence; + } + } + +GotBufferChain: + + // + // We have a packet and a buffer chain, there are + // no other resources required for a send so we can + // fill in the header and go. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.Request = Connection->FirstMessageRequest; + + PacketLength = PacketSize + sizeof(NB_CONNECTION); + Reserved->u.SR_CO.PacketLength = PacketLength; + + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. BUGBUG: Put this in + // a contiguous buffer in the connection. + // + + Header->Session.ConnectionControlFlag = ConnectionControlFlag; + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = ThisSendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentMessageLength; + Header->Session.Offset = ThisOffset; + Header->Session.DataLength = (USHORT)PacketSize; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + // + // Reference the request to account for this send. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + ++REQUEST_REFCOUNT (Request); + + ++Device->TempFramesSent; + Device->TempFrameBytesSent += PacketSize; + + // + // Start the timer. + // + + NbiStartRetransmit (Connection); + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + + if (!ExitAfterSend) { + + // + // BUGBUG: Did we need to reference the connection until we + // get the lock back?? + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + // + // Jump back to the beginning of the function to + // repacketize. + + goto SendAnotherPacket; + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + +} /* NbiPacketizeSend */ + + +VOID +NbiAdjustSendWindow( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine adjusts a connection's send window if needed. It is + assumed that we just got an ack for a full send window. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + + if (Connection->RetransmitThisWindow) { + + // + // Move it down. Check if this keeps happening. + // + + if (Connection->SendWindowSize > 2) { + --Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lower window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + if (Connection->SendWindowIncrease) { + + // + // We just increased the window. + // + + ++Connection->IncreaseWindowFailures; + NB_DEBUG2 (SEND_WINDOW, ("%d consecutive increase failues on %lx (%lx)\n", Connection->IncreaseWindowFailures, Connection, Connection->CurrentSend.SendSequence)); + + if (Connection->IncreaseWindowFailures >= 2) { + + if (Connection->MaxSendWindowSize > 2) { + + // + // Lock ourselves at a smaller window. + // + + Connection->MaxSendWindowSize = Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lock send window at %d on %lx (%lx)\n", Connection->MaxSendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + Connection->IncreaseWindowFailures = 0; + } + + Connection->SendWindowIncrease = FALSE; + } + + } else { + + // + // Increase it if allowed, and make a note + // in case this increase causes problems in + // the next window. + // + + if (Connection->SendWindowSize < Connection->MaxSendWindowSize) { + + ++Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Raise window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + Connection->SendWindowIncrease = TRUE; + + } else { + + if (Connection->SendWindowIncrease) { + + // + // We just increased it and nothing failed, + // which is good. + // + + Connection->SendWindowIncrease = FALSE; + Connection->IncreaseWindowFailures = 0; + NB_DEBUG2 (SEND_WINDOW, ("Raised window OK on %lx (%lx)\n", Connection, Connection->CurrentSend.SendSequence)); + } + } + } + + + // + // This controls when we'll check this again. + // + + Connection->SendWindowSequenceLimit += Connection->SendWindowSize; + +} /* NbiAdjustSendWindow */ + + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have gotten an ack + for some data. It completes any sends that have + been acked, and if needed modifies the current send + pointer and queues the connection for repacketizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + ReceiveSequence - The receive sequence from the remote. + + BytesReceived - The number of bytes received in this message. + + Resend - If it is OK to resend based on this packet. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + PREQUEST RequestToComplete; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + + + // + // BUGBUG: We should change to stop the timer + // only if we go idle, since otherwise we still + // want it running, or will restart it when we + // packetize. + // + + // + // See how much is acked here. + // + + if ((Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) && + (ReceiveSequence == (USHORT)(Connection->CurrentSend.SendSequence)) && + (Connection->FirstMessageRequest != NULL)) { + + // Special check for 0 length send which was not accepted by the remote. + // In this case it will pass the above 3 conditions yet, nothing + // is acked. BUG#10395 + if (!Connection->CurrentSend.MessageOffset && Connection->CurrentSend.SendSequence == Connection->UnAckedSend.SendSequence ) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // This acks the entire message. + // + + NB_DEBUG2 (SEND, ("Got ack for entire message on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + Connection->CurrentSend.MessageOffset = 0; // BUGBUG: Needed? + Connection->UnAckedSend.MessageOffset = 0; + + // + // We don't adjust the send window since we likely stopped + // packetizing before we hit it. + // + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + if (Connection->NewNetbios) { + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + + } + + Connection->RetransmitThisWindow = FALSE; + + Request = Connection->FirstMessageRequest; + + // + // We dequeue these requests from the connection's + // send queue. + // + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + RequestToComplete = Request; + + } else { + + // + // There are still sends pending, this will get + // completed when the last send completes. Since + // we have already unlinked the request from the + // connection's send queue we can do this without + // any locks. + // + + RequestToComplete = NULL; + + } + + // + // Now see if there is a send to activate. + // + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Now complete any requests we need to. + // + + while (RequestToComplete != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (RequestToComplete); + REQUEST_STATUS (RequestToComplete) = STATUS_SUCCESS; + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + ++Connection->ConnectionInfo.TransmittedTsdus; + RequestToComplete = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } else if ((ReceiveSequence == Connection->CurrentSend.SendSequence) && + (Connection->NewNetbios || (BytesReceived == Connection->CurrentSend.MessageOffset)) && + (Connection->CurrentSend.Request != NULL)) { + + // + // This acks whatever we sent last time, and we are + // not done packetizing this send, so we can repacketize. + // BUGBUG: With SendSequence changing as it does now, + // don't need the CurrentSend.Request check??? + // + + NB_DEBUG2 (SEND, ("Got full ack on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + if (Connection->NewNetbios) { + + // + // If we are waiting for a window, and this does not open it + // anymore, then we don't reset our timers/retries. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) { + + if (Connection->RemoteRcvSequenceMax != BytesReceived) { + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + } + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + Connection->RetransmitThisWindow = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + // + // We may be on if this ack is duplicated. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if( Connection->FirstMessageRequest ) { + + // + // This acked part of the current send. If the + // remote is requesting a resend then we advance + // the current send location by the amount + // acked and resend from there. If he does + // not want a resend, just ignore this. + // + // We repacketize immediately because we have + // backed up the pointer, and this would + // cause us to ignore an ack for the amount + // sent. Since we don't release the lock + // until we have packetized, the current + // pointer will be advanced past there. + // + // BUGBUG: If he is acking more than we sent, we + // ignore this -- the remote is confused and there + // is nothing much we can do. + // + + if (Resend) { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RetransmitThisWindow = TRUE; + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else if (!Connection->NewNetbios && + ((ReceiveSequence == Connection->UnAckedSend.SendSequence) && + (BytesReceived <= Connection->CurrentSend.MessageOffset))) { + + ULONG BytesAcked = + BytesReceived - Connection->UnAckedSend.MessageOffset; + + // + // Old netbios. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedByBytes( + Connection, + BytesAcked); + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, reset the retry count + // + if ( BytesAcked ) { + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. We advance + // the back of our send window, but we don't repacketize. + // + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if ((Connection->CurrentSend.Request != NULL) && + (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } + } + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +} /* NbiReframeConnection */ + + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when have gotten an ack for + a full message, or received a response to a watchdog + probe, and need to check if the connection should + start packetizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + ULONG TempCount; + PTDI_REQUEST_KERNEL_SEND Parameters; + PDEVICE Device = NbiDevice; + + // + // See if there is a send to activate. + // + + if (Connection->SendQueue.Head != NULL) { + + // + // Take the first send off the queue and make + // it current. + // + + Request = Connection->SendQueue.Head; + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Request); + + // + // BUGBUG: Cache the information about being EOM + // in a more easily accessible location? + // + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a one-request message. + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } else { + + // + // This is a multiple-request message. We scan + // to see if we have the end of message received + // yet. + // + + TempCount = Parameters->SendLength; + TmpRequest = Request; + Request = REQUEST_SINGLE_LINKAGE(Request); + + while (Request != NULL) { + + TempCount += Parameters->SendLength; + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + Connection->CurrentSend.Request = TmpRequest; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (TmpRequest); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = TmpRequest; + Connection->LastMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = TempCount; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + break; + + } + + Request = REQUEST_SINGLE_LINKAGE(Request); + + } + + if (Request == NULL) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } + + } + + } else { + + Connection->FirstMessageRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + + NbiStartWatchdog (Connection); + + } + +} /* NbiRestartConnection */ + + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer by the specified number of bytes. It + assumes that there are enough send requests to + handle the number specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + + BytesAcked - The number of bytes acked. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + ULONG CurSendBufferLength; + ULONG BytesLeft = BytesAcked; + ULONG TempBytes; + + while (BytesLeft > 0) { + + NdisQueryBuffer (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength); + + // + // See if bytes acked ends within the current buffer. + // + + if (Connection->UnAckedSend.BufferOffset + BytesLeft < + CurSendBufferLength) { + + Connection->UnAckedSend.BufferOffset += BytesLeft; + Connection->UnAckedSend.MessageOffset += BytesLeft; + break; + + } else { + + TempBytes = CurSendBufferLength - Connection->UnAckedSend.BufferOffset; + BytesLeft -= TempBytes; + Connection->UnAckedSend.MessageOffset += TempBytes; + + // + // No, so advance the buffer. + // + + Connection->UnAckedSend.BufferOffset = 0; + Connection->UnAckedSend.Buffer = + NDIS_BUFFER_LINKAGE (Connection->UnAckedSend.Buffer); + + // + // Is there a next buffer in this request? + // + + if (Connection->UnAckedSend.Buffer == NULL) { + + // + // No, so advance the request unless we are done. + // + + if (BytesLeft == 0) { + return; + } + + Connection->UnAckedSend.Request = + REQUEST_SINGLE_LINKAGE(Connection->UnAckedSend.Request); + + if (Connection->UnAckedSend.Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->UnAckedSend.Buffer = + REQUEST_NDIS_BUFFER (Connection->UnAckedSend.Request); + + } + } + } + +} /* NbiAdvanceUnAckedByBytes */ + + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer so that the next packet to send will be + the correct one for ReceiveSequence. UnAckedSend + must point to a known valid combination. It + assumes that there are enough send requests to + handle the sequence specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT PacketsAcked; + + // + // BUGBUG: Fix this to account for partial sends, where + // we might not have used the max. for all packets. + // + + PacketsAcked = ReceiveSequence - Connection->UnAckedSend.SendSequence; + + NbiAdvanceUnAckedByBytes( + Connection, + PacketsAcked * Connection->MaximumPacketSize); + + Connection->UnAckedSend.SendSequence += PacketsAcked; + +} /* NbiAdvanceUnAckedBySequence */ + + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send + The request is found on the connection's send queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_SEND)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // Just stop the connection, that will tear down any + // sends. + // + // BUGBUG: Do we care about cancelling non-active + // sends without stopping the connection?? + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelSend */ + + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source + NDIS_BUFFER chain and offset into it. We assume we don't know the + length of the source Mdl chain, and we must allocate the NDIS_BUFFERs + for the destination chain, which we do from the NDIS buffer pool. + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. + +Environment: + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentSourceBuffer - Points to the start of the NDIS_BUFFER chain + from which to draw the packet. + + CurrentByteOffset - Offset within this NDIS_BUFFER to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + DestinationBuffer - returned pointer to the NDIS_BUFFER chain describing + the packet. + + NewSourceBuffer - returned pointer to the NDIS_BUFFER that would + be used for the next byte of packet. NULL if the source NDIS_BUFFER + chain was exhausted. + + NewByteOffset - returned offset into the NewSourceBuffer for the next byte + of packet. NULL if the source NDIS_BUFFER chain was exhausted. + + ActualLength - The actual length of the data copied. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded + and was the correct length. + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while + building the destination chain. + +--*/ +{ + ULONG AvailableBytes; + ULONG CurrentByteCount; + ULONG BytesCopied; + PNDIS_BUFFER OldNdisBuffer; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + + OldNdisBuffer = CurrentSourceBuffer; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + AvailableBytes = CurrentByteCount - CurrentByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + BufferPoolHandle, + OldNdisBuffer, + CurrentByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *DestinationBuffer = NewNdisBuffer; + BytesCopied = AvailableBytes; + + // + // Was the first NDIS_BUFFER enough data. + // + + if (BytesCopied == DesiredLength) { + if (CurrentByteOffset + AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = CurrentSourceBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset + AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + if (CurrentSourceBuffer->Next == NULL) { + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + while (OldNdisBuffer != NULL) { + + AvailableBytes = DesiredLength - BytesCopied; + if (AvailableBytes > CurrentByteCount) { + AvailableBytes = CurrentByteCount; + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + BufferPoolHandle, + OldNdisBuffer, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*DestinationBuffer != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*DestinationBuffer); + NdisFreeBuffer (*DestinationBuffer); + *DestinationBuffer = NewNdisBuffer; + } + + *NewByteOffset = CurrentByteOffset; + *NewSourceBuffer = CurrentSourceBuffer; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + BytesCopied += AvailableBytes; + + if (BytesCopied == DesiredLength) { + if (AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = OldNdisBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = OldNdisBuffer; + *NewByteOffset = AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + } + + // + // We ran out of source buffer chain. + // + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + +} /* NbiBuildBufferChainFromBufferChain */ + diff --git a/private/ntos/tdi/isnp/nb/session.c b/private/ntos/tdi/isnp/nb/session.c new file mode 100644 index 000000000..fe998feb2 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/session.c @@ -0,0 +1,2450 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + session.c + +Abstract: + + This module contains the code to handle session frames + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#ifdef RASAUTODIAL +#include +#include +#endif // RASAUTODIAL +#pragma hdrstop + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +VOID +NbiNoteNewConnection( + PCONNECTION pConnection + ); +#endif + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiSendDeathPacket( + IN PCONNECTION Connection, + IN CTELockHandle LockHandle + ) +{ + PNDIS_PACKET Packet = PACKET(&NbiGlobalDeathPacket); + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + NDIS_STATUS NdisStatus; + + if ( Reserved->SendInProgress ) { + DbgPrint("***Could not send death packet - in use\n"); + NB_FREE_LOCK(&Connection->Lock, LockHandle); + return; + } + + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DEATH_PACKET; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + Header->Session.ConnectionControlFlag = 0; + Header->Session.DataStreamType = NB_CMD_DEATH_PACKET; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + + + NB_FREE_LOCK(&Connection->Lock, LockHandle); + + DbgPrint("*****Death packet is being sent for connection %lx, to <%.16s>\n",Connection, Connection->RemoteName); + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = NbiDevice; + ULONG Hash; + ULONG ReceiveFlags; + ULONG IndicateBytesTransferred; + ULONG DataAvailable, DataIndicated; + ULONG DestBytes, BytesToTransfer; + PUCHAR DataHeader; + BOOLEAN Last, CompleteReceive, EndOfMessage, PartialReceive, CopyLookahead; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + ULONG NdisBytesTransferred; + PIRP ReceiveIrp; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + ULONG BufferChainLength; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + +#ifdef RSRC_TIMEOUT_DBG + if ( Connection->FirstMessageRequest && NbiGlobalDebugResTimeout ) { + LARGE_INTEGER CurrentTime, ElapsedTime; + KeQuerySystemTime(&CurrentTime); + ElapsedTime.QuadPart = CurrentTime.QuadPart - Connection->FirstMessageRequestTime.QuadPart; +// DbgPrint("*****Elapsed %lx.%lx time\n",ElapsedTime.HighPart,ElapsedTime.LowPart); + if ( ElapsedTime.QuadPart > NbiGlobalMaxResTimeout.QuadPart ) { + + DbgPrint("*****Connection %lx is not copleting irp %lx for %lx.%lx time\n",Connection, Connection->FirstMessageRequest, + ElapsedTime.HighPart,ElapsedTime.LowPart); + DbgPrint("************irp arrived at %lx.%lx current time %lx.%lx\n", + Connection->FirstMessageRequestTime.HighPart,Connection->FirstMessageRequestTime.LowPart, + CurrentTime.HighPart, CurrentTime.LowPart); + + NbiSendDeathPacket( Connection, LockHandle ); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + } + } +#endif //RSRC_TIMEOUT_DBG + + // + // The connection is up, see if this is data should + // be received. + // + + if (Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) { + + // + // This is an ack. This call releases + // the lock. BUGBUG: Does this need to + // be a function? + // + + NbiProcessDataAck( + Connection, + Sess, + RemoteAddress + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // See if there is any piggyback ack here. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. Even the old netbios sometimes piggyback + // acks (and doesn't send the explicit ack). + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if ((Connection->NewNetbios) && + (Connection->CurrentSend.SendSequence != Connection->UnAckedSend.SendSequence)) { + + // + // For the new netbios, even if we are not waiting + // for an ack he may have acked something with this + // send and we should check, since it may allow + // us to open our send window. + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } + + // + // This is data on the connection. First make sure + // it is the data we expect next. + // + + if (Connection->NewNetbios) { + + if (Sess->SendSequence != Connection->ReceiveSequence) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp data on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving a packet we have already seen, just + // send a normal ack, otherwise force a resend. This test + // we do is equivalent to + // Sess->SendSequence < Connection->ReceiveSequence + // but rearranged so it works when the numbers wrap. + // + + if ((SHORT)(Sess->SendSequence - Connection->ReceiveSequence) < 0) { + + // + // Since this is a resend, check if the local + // target has changed. + // +#if defined(_PNP_POWER) + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, sizeof(IPX_LOCAL_TARGET))) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx, (%d,%d)\n", Connection, + Connection->LocalTarget.NicHandle.NicId, RemoteAddress->NicHandle.NicId); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#else + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#endif _PNP_POWER + AckType = NbiAckResponse; + + } else { + + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // Old netbios. + // + + if ((Sess->SendSequence != Connection->ReceiveSequence) || + (Sess->Offset != Connection->CurrentReceive.MessageOffset)) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving the last packet again, just + // send a normal ack, otherwise force a resend. + // + + if (((Sess->SendSequence == Connection->ReceiveSequence) && + ((ULONG)(Sess->Offset + Sess->DataLength) == Connection->CurrentReceive.MessageOffset)) || + (Sess->SendSequence == (USHORT)(Connection->ReceiveSequence-1))) { + AckType = NbiAckResponse; + } else { + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + + IndicateBytesTransferred = 0; + DataAvailable = PacketSize - sizeof(NB_CONNECTION); + DataIndicated = LookaheadBufferSize - sizeof(NB_CONNECTION); + DataHeader = LookaheadBuffer + sizeof(NB_CONNECTION); + + ++Device->TempFramesReceived; + Device->TempFrameBytesReceived += DataAvailable; + + if (Connection->CurrentIndicateOffset) { + CTEAssert (DataAvailable >= Connection->CurrentIndicateOffset); + DataAvailable -= Connection->CurrentIndicateOffset; + if (DataIndicated >= Connection->CurrentIndicateOffset) { + DataIndicated -= Connection->CurrentIndicateOffset; + } else { + DataIndicated = 0; + } + DataHeader += Connection->CurrentIndicateOffset; + } + + CopyLookahead = (BOOLEAN)(MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA); + + if (Connection->NewNetbios) { + Last = (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_EOM) != 0); + } else { + Last = (BOOLEAN)(Sess->Offset + Sess->DataLength == Sess->TotalDataLength); + } + + Connection->CurrentReceiveNoPiggyback = + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) != 0); + + if (Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) { + + // + // We don't have a receive posted, so see if we can + // get one from the queue or our client. + // + + if (Connection->ReceiveQueue.Head != NULL) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Request = Connection->ReceiveQueue.Head; + Connection->ReceiveQueue.Head = REQUEST_SINGLE_LINKAGE(Request); + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NB_DEBUG2 (RECEIVE, ("Activated receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + if ((Connection->ReceiveUnaccepted == 0) && + (Connection->AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE])) { + + Connection->ReceiveState = CONNECTION_RECEIVE_INDICATE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + Status = (*Connection->AddressFile->ReceiveHandler)( + Connection->AddressFile->HandlerContexts[TDI_EVENT_RECEIVE], + Connection->Context, + ReceiveFlags, + DataIndicated, + DataAvailable, + &IndicateBytesTransferred, + DataHeader, + &ReceiveIrp); + + if (Status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // We got an IRP, activate it. + // + + Request = NbiAllocateRequest (Device, ReceiveIrp); + + IF_NOT_ALLOCATED(Request) { + + ReceiveIrp->IoStatus.Information = 0; + ReceiveIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (ReceiveIrp, IO_NETWORK_INCREMENT); + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + CTEAssert (REQUEST_OPEN_CONTEXT(Request) == Connection); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + Connection->ReceiveUnaccepted = DataAvailable - IndicateBytesTransferred; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + NB_DEBUG2 (RECEIVE, ("Indicate got receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + // + // The connection has been stopped. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // He accepted some or all of the data. + // + + NB_DEBUG2 (RECEIVE, ("Indicate took receive data %lx (%d)\n", Connection, Connection->ReceiveSequence)); + + if ( (IndicateBytesTransferred >= DataAvailable)) { + + CTEAssert (IndicateBytesTransferred == DataAvailable); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentIndicateOffset = 0; + if ( Last ) { + Connection->CurrentReceive.MessageOffset = 0; + } else { + Connection->CurrentReceive.MessageOffset+= IndicateBytesTransferred; + } + + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + Connection->NoPiggybackHeuristic = (BOOLEAN) + (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // We will do the easiest thing here, which + // is to send an ack for the amount he + // took, and force a retransmit on the + // remote. For net netbios we make a note + // of how many bytes were taken and ask + // for a resend. + // + // BUGBUG: Handle this better?? + // + +#if DBG + DbgPrint ("NBI: Client took partial indicate data\n"); +#endif + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive.MessageOffset += + IndicateBytesTransferred; + Connection->ReceiveUnaccepted = + DataAvailable - IndicateBytesTransferred; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + Connection->CurrentIndicateOffset = IndicateBytesTransferred; + // + // NOTE: We don't advance ReceiveSequence + // + } + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + Connection->NewNetbios ? + NbiAckResend : NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } else { + + // + // No IRP returned. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveUnaccepted = DataAvailable; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_DEBUG (RECEIVE, ("Indicate got no receive on %lx (%lx)\n", Connection, Status)); + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // No receive handler. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + if (Connection->ReceiveUnaccepted == 0) { + NB_DEBUG (RECEIVE, ("No receive, no handler on %lx\n", Connection)); + } else { + NB_DEBUG (RECEIVE, ("No receive, ReceiveUnaccepted %d on %lx\n", + Connection->ReceiveUnaccepted, Connection)); + } + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + } else if (Connection->ReceiveState != CONNECTION_RECEIVE_ACTIVE) { + + // + // If we have a transfer in progress, or are waiting for + // a receive to be posted, then ignore this frame. + // + + NB_DEBUG2 (RECEIVE, ("Got data on %lx, state %d (%d)\n", Connection, Connection->ReceiveState, Connection->ReceiveSequence)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + // + // At this point we have a receive and it is set to + // the correct current location. + // + + DestBytes = Connection->ReceiveLength - Connection->CurrentReceive.Offset; + BytesToTransfer = DataAvailable - IndicateBytesTransferred; + + if (DestBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceive = TRUE; + PartialReceive = TRUE; + BytesToTransfer = DestBytes; + + } else if (DestBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceive = TRUE; + PartialReceive = FALSE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceive = Last; + PartialReceive = FALSE; + + } + + // + // If we can copy the data directly, then update our + // pointers, send an ack, and do the copy. + // + + if ((BytesToTransfer > 0) && + (IndicateBytesTransferred + BytesToTransfer <= DataIndicated)) { + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PNDIS_BUFFER CurBuffer; + ULONG CurByteOffset; + + NB_DEBUG2 (RECEIVE, ("Direct copy of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + CurBuffer = Connection->CurrentReceive.Buffer; + CurByteOffset = Connection->CurrentReceive.BufferOffset; + + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurTarget += CurByteOffset; + CurTargetLen -= CurByteOffset; + + CurSource = DataHeader + IndicateBytesTransferred; + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurBuffer = CurBuffer->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + CTEAssert (BytesLeft == BytesNow); + break; + } + + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->CurrentReceive.Buffer = CurBuffer; + Connection->CurrentReceive.BufferOffset = CurByteOffset; + + Connection->CurrentReceive.Offset += BytesToTransfer; + Connection->CurrentReceive.MessageOffset += BytesToTransfer; + + if (CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (EndOfMessage) { + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (PartialReceive) { + Connection->CurrentIndicateOffset += BytesToTransfer; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // and CANCEL Lock. + // + + NbiCompleteReceive( + Connection, + EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + // + // CompleteReceive is FALSE, so EndOfMessage is FALSE. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + + // + // We have to set up a call to transfer data and send + // the ack after it completes (if it succeeds). + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + DataAvailable); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->u.RR_CO.Connection = Connection; + ReceiveReserved->u.RR_CO.EndOfMessage = EndOfMessage; + ReceiveReserved->u.RR_CO.CompleteReceive = CompleteReceive; + ReceiveReserved->u.RR_CO.PartialReceive = PartialReceive; + + ReceiveReserved->Type = RECEIVE_TYPE_DATA; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("TransferData of 0 bytes %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiTransferDataComplete( + Packet, + NDIS_STATUS_SUCCESS, + 0); + + return; + } + + // + // If needed, build a buffer chain to describe this + // to NDIS. + // + + Connection->PreviousReceive = Connection->CurrentReceive; + + if ((Connection->CurrentReceive.Offset == 0) && + CompleteReceive) { + + BufferChain = Connection->CurrentReceive.Buffer; + BufferChainLength = BytesToTransfer; + Connection->CurrentReceive.Buffer = NULL; + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + + } else { + + if (NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentReceive.Buffer, + Connection->CurrentReceive.BufferOffset, + BytesToTransfer, + &BufferChain, + &Connection->CurrentReceive.Buffer, + &Connection->CurrentReceive.BufferOffset, + &BufferChainLength) != NDIS_STATUS_SUCCESS) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("Could not build receive buffer chain %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + ReceiveReserved->u.RR_CO.NoNdisBuffer = FALSE; + + } + + + NdisChainBufferAtFront (Packet, BufferChain); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("TransferData of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + sizeof(NB_CONNECTION) + + Connection->CurrentIndicateOffset + IndicateBytesTransferred, + BytesToTransfer, + Packet, + (PUINT)&NdisBytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (NdisBytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete ( + Packet, + NdisStatus, + NdisBytesTransferred); + + } + + return; + + } + + } else if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + // + // If this is the ack for the session initialize, then + // complete the pending connects. This routine releases + // the connection lock. + // + + NbiProcessSessionInitAck( + Connection, + Sess + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + // + // This is a session initialize frame. + // + // BUGBUG: If there is more data than in the lookahead + // buffer, we won't be able to echo it back in the + // response. + // + + NbiProcessSessionInitialize( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + + } + +} /* NbiProcessSessionData */ + + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine processes an ack on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The session frame. + + RemoteAddress - The local target this packet was received from. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN Resend; + + // + // Make sure we expect an ack right now. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (((Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W)) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We are waiting for an ack (because we completed + // packetizing a send, or ran out of receive window). + // + // This will complete any sends that are acked by + // this receive, and if necessary readjust the + // send pointer and requeue the connection for + // packetizing. It release the connection lock. + // + + if (Connection->ResponseTimeout) { + Resend = TRUE; + Connection->ResponseTimeout = FALSE; + } else { + Resend = (BOOLEAN) + ((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0); + } + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + Resend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We had a probe outstanding and got a response. Restart + // the connection if needed (a send may have just been + // posted while the probe was outstanding). + // + // BUGBUG: We should check that the response is really + // correct. + // + + if (Connection->NewNetbios) { + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + } + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + if (Connection->NewNetbios) { + + // + // We are packetizing, reframe. In the unlikely + // event that this acks everything we may packetize + // in this call, but that is OK (the other thread + // will exit if we finish up). More normally we + // will just advance UnAcked send a bit. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0) + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +#if 0 + + // + // BUGBUG: Should handle this case (i.e. may be in W_PACKET). + // + + } else if ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0) { + + DbgPrint ("NWLNKNB: Ignoring ack, state is %d\n", Connection->SubState); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); +#endif + + } else { + + // + // We got a probe from the remote. Some old DOS clients + // send probes that do not have the send ack bit on, + // so we respond to any probe if none of the conditions + // above are true. This call releases the lock. + // + // We use the IgnoreNextDosProbe flag to ignore every + // second probe of this nature, to avoid a data ack + // war between two machines who each think they are + // responding to the other. This flag is set to FALSE + // whenever we send an ack or a probe. + // + + if (!Connection->IgnoreNextDosProbe) { + + // + // Since this is a probe, check if the local + // target has changed. + // + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + Connection->IgnoreNextDosProbe = TRUE; + + } else { + + Connection->IgnoreNextDosProbe = FALSE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + +} /* NbiProcessDataAck */ + + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION frames which have + a remote connection ID of 0xffff -- these are session + initialize frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + PacketBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + CONNECT_INDICATION TempConnInd; + PCONNECT_INDICATION ConnInd; + PCONNECTION Connection; + PADDRESS Address; + PREQUEST Request, ListenRequest, AcceptRequest; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + ULONG Hash; + TA_NETBIOS_ADDRESS SourceName; + PIRP AcceptIrp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS AcceptStatus; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_REQUEST_KERNEL_LISTEN ListenParameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + PTDI_CONNECTION_INFORMATION RemoteInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + // + // Verify that the whole packet is there. + // + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT))) { +#if DBG + DbgPrint ("NBI: Got short session initialize, %d/%d\n", PacketSize, + sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); +#endif + return; + } + + // + // Verify that MaximumDataSize that remote can support is > 0 + // Bug # 19405 + // + if ( SessInit->MaximumDataSize == 0 ) { + NB_DEBUG(CONNECTION, ("Connect request with MaximumDataSize == 0\n" +)); + return; + } + + // + // Make sure this is for an address we care about. + // + + if (Device->AddressCounts[SessInit->DestinationName[0]] == 0) { + return; + } + + Address = NbiFindAddress (Device, (PUCHAR)SessInit->DestinationName); + + if (Address == NULL) { + return; + } + + // + // First see if we have a session to this remote. We check + // this in case our ack of the session initialize was dropped, + // we don't want to reindicate our client. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + for (Hash = 0; Hash < CONNECTION_HASH_COUNT; Hash++) { + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if ((RtlEqualMemory (&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12)) && + (Connection->RemoteConnectionId == Sess->SourceConnectionId) && + (Connection->State != CONNECTION_STATE_DISCONNECT)) { + + // + // Yes, we are talking to this remote, if it is active then + // respond, otherwise we are in the process of connecting + // and we will respond eventually. + // + +#if DBG + DbgPrint ("NBI: Got connect request on active connection %lx\n", Connection); +#endif + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + NbiSendSessionInitAck( + Connection, + (PUCHAR)(SessInit+1), + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)), + NULL); // lock is not held + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + Connection = Connection->NextConnection; + } + } + + + TdiBuildNetbiosAddress ((PUCHAR)SessInit->SourceName, FALSE, &SourceName); + + // + // Scan the queue of listens to see if there is one that + // satisfies this request. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ListenQueue.Flink; + p != &Device->ListenQueue; + p = p->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->AddressFile->Address != Address) { + continue; + } + + // + // Check that this listen is not specific to a different + // netbios name. + // + + ListenParameters = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(Request); + ListenInformation = ListenParameters->RequestConnectionInformation; + + if (ListenInformation && + (ListenInformation->RemoteAddress) && + (ListenAddress = NbiParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + SessInit->SourceName, + ListenAddress->NetbiosName, + 16))) { + continue; + } + + // + // This connection is valid, so we use it. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen %lx\n", Connection)); + + RemoveEntryList (REQUEST_LINKAGE(Request)); + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + // + // Save this information now for whenever we complete the listen. + // + + RemoteInformation = ListenParameters->ReturnConnectionInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + if (ListenParameters->RequestFlags & TDI_QUERY_ACCEPT) { + + // + // We have to wait for an accept before sending the + // session init ack, so we complete the listen and wait. + // + + ListenRequest = Request; + Connection->ListenRequest = NULL; + + NB_DEBUG2 (CONNECTION, ("Queued listen on %lx awaiting accept\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ACCEPT; + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_W_ACCEPT); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } else { + + // + // We are ready to go, so we send out the find route request + // for the remote. We keep the listen alive and the CREF_LISTEN + // reference on until this completes. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen on %lx\n", Connection)); + + ListenRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } + + // + // Complete the listen if needed. + // + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION (ListenRequest) = 0; + REQUEST_STATUS (ListenRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK ( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest (Device, ListenRequest); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + + return; + + } + + // + // We could not find a listen, so we indicate to every + // client. Make sure there is no session initialize for this + // remote being indicated. If there is not, we insert + // ourselves in the queue to block others. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ConnectIndicationInProgress.Flink; + p != &Device->ConnectIndicationInProgress; + p = p->Flink) { + + ConnInd = CONTAINING_RECORD (p, CONNECT_INDICATION, Linkage); + + if ((RtlEqualMemory(ConnInd->NetbiosName, SessInit->DestinationName, 16)) && + (RtlEqualMemory(&ConnInd->RemoteAddress, Conn->IpxHeader.SourceNetwork, 12)) && + (ConnInd->ConnectionId == Sess->SourceConnectionId)) { + + // + // We are processing a request from this remote for + // the same ID, to avoid confusion we just exit. + // + +#if DBG + DbgPrint ("NBI: Already processing connect to <%.16s>\n", SessInit->DestinationName); +#endif + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + } + + RtlCopyMemory (&TempConnInd.RemoteAddress, SessInit->DestinationName, 16); + RtlCopyMemory (&TempConnInd.RemoteAddress, Conn->IpxHeader.SourceNetwork, 12); + TempConnInd.ConnectionId = Sess->SourceConnectionId; + + InsertTailList (&Device->ConnectIndicationInProgress, &TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + + // + // Now scan through the address to find someone who has + // an indication routine registed and wants this connection. + // + + + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No posted listen requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_CONNECT]) { + + if ((*AddressFile->ConnectionHandler)( + AddressFile->HandlerContexts[TDI_EVENT_CONNECT], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, // user data + NULL, + 0, // options + NULL, + &ConnectionContext, + &AcceptIrp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + continue; + + } + + AcceptRequest = NbiAllocateRequest (Device, AcceptIrp); + + IF_NOT_ALLOCATED(AcceptRequest) { + + AcceptStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // The client accepted the connect, so activate + // the connection and complete the accept. + // listen. This lookup references the connection + // so we know it will remain valid. + // + + Connection = NbiLookupConnectionByContext ( + AddressFile, + ConnectionContext); + + if (Connection != NULL) { + + ASSERT (Connection->AddressFile == AddressFile); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle2); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + NB_DEBUG2 (CONNECTION, ("Indication on %lx returned connection %lx\n", AddressFile, Connection)); + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + Connection->Retries = Device->KeepAliveCount; + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + NbiReferenceConnectionLock (Connection, CREF_ACCEPT); + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + Connection->AcceptRequest = AcceptRequest; + AcceptStatus = STATUS_PENDING; + + // + // Take us out of this list now, we will jump to + // FoundConnection which is past the removal below. + // + + RemoveEntryList (&TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned invalid connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + + } + + NbiDereferenceConnection (Connection, CREF_BY_CONTEXT); + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned unknown connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + + } + } + + // + // Complete the accept request in the failure case. + // + + if (AcceptStatus != STATUS_PENDING) { + + REQUEST_STATUS (AcceptRequest) = AcceptStatus; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest (Device, AcceptRequest); + + } else { + + // + // We found a connection, so we break; this is + // a jump since the while exit assumes the + // address lock is held. + // + + goto FoundConnection; + + } + + } + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Take us out of the list that blocks other indications + // from this remote to this address. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + RemoveEntryList (&TempConnInd.Linkage); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + +FoundConnection: + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + + NbiDereferenceAddress (Address, AREF_FIND); + +} /* NbiProcessSessionInitialize */ + + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine handles session init ack frames. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The netbios header for the received frame. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + BOOLEAN TimerWasStopped = FALSE; + CTELockHandle CancelLH; + + if ((Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) && + (Sess->SendSequence == 0x0000) && + (Sess->ReceiveSequence == 0x0001)) { + + NB_DEBUG2 (CONNECTION, ("Completing connect on %lx\n", Connection)); + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + if (Connection->Retries == NbiDevice->ConnectionCount) { + ++NbiDevice->Statistics.ConnectionsAfterNoRetry; + } else { + ++NbiDevice->Statistics.ConnectionsAfterRetry; + } + ++NbiDevice->Statistics.OpenConnections; + + Connection->Retries = NbiDevice->KeepAliveCount; + NbiStartWatchdog (Connection); + + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 1; + Connection->UnAckedSend.SendSequence = 1; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 0; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = NbiDevice->KeepAliveCount; + if (NbiDevice->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveWindowSize - 1); + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 3; + } else { + Connection->NewNetbios = FALSE; + } + + if (Connection->MaximumPacketSize > SessInit->MaximumDataSize) { + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + } + + Request = Connection->ConnectRequest; + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify + // the automatic connection driver about + // this connection. + // + if (fAcdLoadedG) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + CTEGetLock(&AcdDriverG.SpinLock, &AcdHandle); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, AcdHandle); + if (fEnabled) + NbiNoteNewConnection(Connection); + } +#endif // RASAUTODIAL + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_SUCCESS; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiTransferReferenceConnection (Connection, CREF_CONNECT, CREF_ACTIVE); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiProcessSessionInitAck */ + + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + + // + // We reply to any session end, even if we don't know the + // connection, to speed up the disconnect on the remote. + // + + if (Connection == NULL) { + + NB_DEBUG (CONNECTION, ("Session end received on unknown id %lx\n", Sess->DestConnectionId)); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. We do this in case a full send has been + // received by the remote but he did not send an + // ack before the session went down -- this will + // prevent us from failing a send which actually + // succeeded. If we are not in W_ACK this may ack + // part of a send, but in that case we don't care + // since StopConnection will abort it anyway and + // the amount successfully received by the remote + // doesn't matter. + // + // This releases the lock. BUGBUG: Fix this. + // + + NB_DEBUG2 (CONNECTION, ("Session end at W_ACK, reframing %lx (%d)\n", Connection, Sess->ReceiveSequence)); + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle1)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on connection %lx\n", Connection)); + + } + + // + // This call sets the state to DISCONNECT and + // releases the connection lock. It will also + // complete a disconnect wait request if one + // is pending, and indicate to our client + // if needed. + // + + NbiStopConnection( + Connection, + STATUS_REMOTE_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on inactive connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEnd */ + + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END_ACK frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // Stop the timer, when the reference goes away it + // will shut down. We set the substate so if the + // timer is running it will not restart (BUGBUG: + // there is a small window here, but it is not + // harmful, we will just have to timeout one + // more time). + // + + NB_DEBUG2 (CONNECTION, ("Got session end ack on %lx\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_D_GOT_ACK; + if (CTEStopTimer (&Connection->Timer)) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/sources.inc b/private/ntos/tdi/isnp/nb/sources.inc new file mode 100644 index 000000000..f54b4918b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/sources.inc @@ -0,0 +1,69 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnknb + +TARGETNAME=nwlnknb +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\inc;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 -DRASAUTODIAL +#-DRSRC_TIMEOUT_DBG + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\address.c \ + ..\autodial.c \ + ..\bind.c \ + ..\cache.c \ + ..\config.c \ + ..\connect.c \ + ..\datagram.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\frame.c \ + ..\nwlnknb.rc \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\send.c \ + ..\session.c \ + ..\timer.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj + \ No newline at end of file diff --git a/private/ntos/tdi/isnp/nb/timer.c b/private/ntos/tdi/isnp/nb/timer.c new file mode 100644 index 000000000..381b120e5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/timer.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code which implements the timers for + netbios. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +ULONG NbiTickIncrement = 0; +ULONG NbiShortTimerDeltaTicks = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiInitializeTimers) +#endif + + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the retransmit timer for the given connection. + The connection is inserted on the short list if it isn't on already. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Insert us in the queue if we aren't in it. + // + + Connection->Retransmit = + Device->ShortAbsoluteTime + Connection->CurrentRetransmitTimeout; + + if (!Connection->OnShortList) { + + CTEAssert (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnShortList) { + Connection->OnShortList = TRUE; + InsertTailList (&Device->ShortList, &Connection->ShortList); + } + + if (!Device->ShortListActive) { + NbiStartShortTimer (Device); + Device->ShortListActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartRetransmit */ + + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the watchdog timer for a connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + Connection->Watchdog = Device->LongAbsoluteTime + Connection->WatchdogTimeout; + + if (!Connection->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnLongList) { + Connection->OnLongList = TRUE; + InsertTailList (&Device->LongList, &Connection->LongList); + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartWatchdog */ + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the retransmit timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Retransmit = 0; + +} /* NbiStopRetransmit */ + + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the watchdog timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Watchdog = 0; + +} /* NbiStopWatchdog */ +#endif + + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's retransmit timer + expires. It is called from NbiShortTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = NbiDevice; + BOOLEAN SendFindRoute; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + SendFindRoute = FALSE; + + ++Device->Statistics.ResponseTimerExpirations; + + if (!(Connection->NewNetbios) && + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Wait for ack timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // Set our current packetize location back to the + // spot of the last ack, and start up again. + // + // BUGBUG: Should we send a probe here? + // + + Connection->CurrentSend = Connection->UnAckedSend; + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NB_DEBUG2 (SEND, ("Connection %lx retransmit timeout\n", Connection)); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // This releases the lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) || + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Probe timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NbiStartRetransmit (Connection); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // Set this so we know to retransmit when the ack + // is received. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PROBE) { + Connection->ResponseTimeout = TRUE; + } + + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + if (SendFindRoute) { + + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_FORCE_RIP; + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireRetansmit */ + + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's watchdog timer + expires. It is called from NbiLongTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // If we are not idle, then something else is happening + // so the watchdog is unnecessary. + // + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE)) { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->SubState = CONNECTION_SUBSTATE_A_W_PROBE; + NbiStartRetransmit (Connection); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireWatchdog */ + + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the short connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE Device = (PDEVICE)Context; + PCONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + Device->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + Device->ShortTimerStart.QuadPart; + + TickDelta = TickDifference.LowPart / NbiShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + Device->ShortAbsoluteTime += TickDelta; + + if (Device->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + Device->ShortAbsoluteTime -= 0xe0000000; + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + Timeout = Connection->Retransmit; + if (Timeout) { + Connection->Retransmit = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + ASSERT (Connection->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Retransmit && + (Device->ShortAbsoluteTime > Connection->Retransmit)) { + + Connection->Retransmit = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireRetransmit (Connection); // no locks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + + break; + + } + + nextp = p->Flink; + + if (Connection->Retransmit == 0) { + + Connection->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Connection->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if (Connection->Retransmit != 0) { + InsertTailList(&Device->ShortList, &Connection->ShortList); + Connection->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&Device->ShortList)) { + Device->ShortListActive = FALSE; + } + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for enough times through here, + // If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (!Connection->DataAckPending) { + continue; + } + + ++Connection->DataAckTimeouts; + + if (Connection->DataAckTimeouts < Device->AckDelayTime) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_SHORT_D_ACK); + + Device->DataAckQueueChanged = FALSE; + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->DataAckPending) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // We set PiggybackAckTimeout to TRUE so that we won't try + // to piggyback a response until we get back traffic. + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = TRUE; + ++Device->Statistics.AckTimerExpirations; + ++Device->Statistics.PiggybackAckTimeouts; + + // + // This call releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_SHORT_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (Device->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&Device->DataAckConnections)) { + Device->DataAckActive = FALSE; + } + + + // + // Update the real counters from the temp ones. We have + // TimerLock here, which is good enough. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesSent, + Device->TempFrameBytesSent); + Device->Statistics.DataFramesSent += Device->TempFramesSent; + + Device->TempFrameBytesSent = 0; + Device->TempFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesReceived, + Device->TempFrameBytesReceived); + Device->Statistics.DataFramesReceived += Device->TempFramesReceived; + + Device->TempFrameBytesReceived = 0; + Device->TempFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + Device->ProcessingShortTimer = FALSE; + + if ((Device->ShortListActive || Device->DataAckActive) && + (Device->State != DEVICE_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } else { + + NbiDereferenceDevice (Device, DREF_SHORT_TIMER); + + } + +} /* NbiShortTimeout */ + + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the long connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, nextp; + LIST_ENTRY AdapterStatusList; + PREQUEST AdapterStatusRequest; + PCONNECTION Connection; + PNETBIOS_CACHE CacheName; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (++Device->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + Device->LongAbsoluteTime = 0x10000000; + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + Timeout = Connection->Watchdog; + if (Timeout) { + Connection->Watchdog = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + + if ((Device->LongAbsoluteTime % 4) == 0) { + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + ASSERT (Connection->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Watchdog && (Device->LongAbsoluteTime > Connection->Watchdog)) { + + Connection->Watchdog = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireWatchdog (Connection); // no spinlocks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBI: Stop processing LongList, %lx removed\n", Connection); +#endif + break; + + } + + nextp = p->Flink; + + if (Connection->Watchdog == 0) { + + Connection->OnLongList = FALSE; + RemoveEntryList(p); + + if (Connection->Watchdog != 0) { + InsertTailList(&Device->LongList, &Connection->LongList); + Connection->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + if (Connection->DataAckPending) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_LONG_D_ACK); + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Have to check again, because the connection might + // just have been stopped, and it also might just have + // had a data ack queued. + // + + if (Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if (Connection->DataAckPending) { + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + Device->DataAckQueueChanged = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiDereferenceConnection (Connection, CREF_LONG_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbiReceiveComplete((USHORT)0); + + + // + // Check if any adapter status queries are getting old. + // + + InitializeListHead (&AdapterStatusList); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + if (REQUEST_INFORMATION(AdapterStatusRequest) == 1) { + + // + // BUGBUG: We should resend a certain number of times. + // + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // We are going to abort this request, so dereference + // the cache entry it used. + // + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + } else { + + ++REQUEST_INFORMATION(AdapterStatusRequest); + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + NB_DEBUG2 (QUERY, ("AdapterStatus %lx got name but no response\n", AdapterStatusRequest)); + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + // + // See if a minute has passed and we need to check for empty + // cache entries to age out. We check for 64 seconds to make + // the mod operation faster. + // + +#if defined(_PNP_POWER) + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); +#endif _PNP_POWER + + ++Device->CacheTimeStamp; + + if ((Device->CacheTimeStamp % 64) == 0) { + + + // + // flush all the entries which have been around for ten minutes + // (LONG_TIMER_DELTA is in milliseconds). + // + + FlushOldFromNetbiosCacheTable( Device->NameCache, (600000 / LONG_TIMER_DELTA) ); + + } + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (Device->State != DEVICE_STATE_STOPPING) { + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } else { +#if defined(_PNP_POWER) + Device->LongTimerRunning = FALSE; +#endif _PNP_POWER + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + +#if defined(_PNP_POWER) + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER +} /* NbiLongTimeout */ + + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + Device - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + + if ((!Device->ProcessingShortTimer) && + (!(Device->ShortListActive)) && + (!(Device->DataAckActive))) { + + NbiReferenceDevice (Device, DREF_SHORT_TIMER); + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } + +} /* NbiStartShortTimer */ + + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + Device - Pointer to our device. + +Return Value: + + none. + +--*/ + +{ + + // + // NbiTickIncrement is the number of NT time increments + // which pass between each tick. NbiShortTimerDeltaTicks + // is the number of ticks which should happen in + // SHORT_TIMER_DELTA milliseconds (i.e. between each + // expiration of the short timer). + // + + NbiTickIncrement = KeQueryTimeIncrement(); + + if (NbiTickIncrement > (SHORT_TIMER_DELTA * MILLISECONDS)) { + NbiShortTimerDeltaTicks = 1; + } else { + NbiShortTimerDeltaTicks = (SHORT_TIMER_DELTA * MILLISECONDS) / NbiTickIncrement; + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + Device->ShortAbsoluteTime = 0x10000000; + Device->LongAbsoluteTime = 0x10000000; + + CTEInitTimer (&Device->ShortTimer); + CTEInitTimer (&Device->LongTimer); + +#if !defined(_PNP_POWER) + // + // One reference for the long timer. + // + + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + +#endif !_PNP_POWER + + Device->TimersInitialized = TRUE; + Device->ShortListActive = FALSE; + Device->ProcessingShortTimer = FALSE; + + InitializeListHead (&Device->ShortList); + InitializeListHead (&Device->LongList); + + CTEInitLock (&Device->TimerLock.Lock); + +} /* NbiInitializeTimers */ + diff --git a/private/ntos/tdi/isnp/nb/up/makefile b/private/ntos/tdi/isnp/nb/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/up/sources b/private/ntos/tdi/isnp/nb/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/spx/dirs b/private/ntos/tdi/isnp/spx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/spx/globals.c b/private/ntos/tdi/isnp/spx/globals.c new file mode 100644 index 000000000..51fc80803 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/globals.c @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Global values +PDEVICE SpxDevice = NULL; +UNICODE_STRING IpxDeviceName = {0}; +HANDLE IpxHandle = NULL; + +LARGE_INTEGER Magic100000 = { + 0x1b478424, + 0xa7c5ac47 + }; +// Line info +IPX_LINE_INFO IpxLineInfo = {0}; +USHORT IpxMacHdrNeeded = 0; +USHORT IpxInclHdrOffset= 0; + +// Entry Points into the IPX stack +IPX_INTERNAL_SEND IpxSendPacket = NULL; +IPX_INTERNAL_FIND_ROUTE IpxFindRoute = NULL; +IPX_INTERNAL_QUERY IpxQuery = NULL; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData = NULL; + +#if DBG +ULONG SpxDebugDump = 0; +LONG SpxDumpInterval = DBG_DUMP_DEF_INTERVAL; +ULONG SpxDebugLevel = DBG_LEVEL_ERR; +ULONG SpxDebugSystems = DBG_COMP_MOST; +#endif + +// Unload event triggered when ref count on device goes to zero. +KEVENT SpxUnloadEvent = {0}; + +// Maximum packet size quanta used during packet size negotiation +ULONG SpxMaxPktSize[] = { + 576 - MIN_IPXSPX2_HDRSIZE, + 1024 - MIN_IPXSPX2_HDRSIZE, + 1474 - MIN_IPXSPX2_HDRSIZE, + 1492 - MIN_IPXSPX2_HDRSIZE, + 1500 - MIN_IPXSPX2_HDRSIZE, + 1954 - MIN_IPXSPX2_HDRSIZE, + 4002 - MIN_IPXSPX2_HDRSIZE, + 8192 - MIN_IPXSPX2_HDRSIZE, + 17314 - MIN_IPXSPX2_HDRSIZE, + 65535 - MIN_IPXSPX2_HDRSIZE + }; + +ULONG SpxMaxPktSizeIndex = sizeof(SpxMaxPktSize)/sizeof(ULONG); + + +// Global interlock +CTELock SpxGlobalInterlock = {0}; + +// Another one, used only for global queues for addr/conn +CTELock SpxGlobalQInterlock = {0}; +PSPX_CONN_FILE SpxGlobalConnList = NULL; +PSPX_ADDR_FILE SpxGlobalAddrList = NULL; + +SPX_CONNFILE_LIST SpxPktConnList = {NULL, NULL}; +SPX_CONNFILE_LIST SpxRecvConnList = {NULL, NULL}; + +// Timer globals +LONG SpxTimerCurrentTime = 0; diff --git a/private/ntos/tdi/isnp/spx/h/fwddecls.h b/private/ntos/tdi/isnp/spx/h/fwddecls.h new file mode 100644 index 000000000..feda4e76b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/fwddecls.h @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + fwddecls.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +struct _SPX_ADDR ; +struct _SPX_ADDR_FILE ; +struct _SPX_CONN_FILE ; +struct _SPX_SEND_RESD ; diff --git a/private/ntos/tdi/isnp/spx/h/globals.h b/private/ntos/tdi/isnp/spx/h/globals.h new file mode 100644 index 000000000..e4fcf39a8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/globals.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +extern PDEVICE SpxDevice; +extern UNICODE_STRING IpxDeviceName; +extern HANDLE IpxHandle; + +extern LARGE_INTEGER Magic100000; + +#if 1 // DBG +extern ULONG SpxDebugDump; +extern LONG SpxDumpInterval; +extern ULONG SpxDebugLevel; +extern ULONG SpxDebugSystems; + +#endif + +// More IPX info. +extern IPX_LINE_INFO IpxLineInfo; +extern USHORT IpxMacHdrNeeded; +extern USHORT IpxInclHdrOffset; + +// Entry Points into the IPX stack +extern IPX_INTERNAL_SEND IpxSendPacket; +extern IPX_INTERNAL_FIND_ROUTE IpxFindRoute; +extern IPX_INTERNAL_QUERY IpxQuery; +extern IPX_INTERNAL_TRANSFER_DATA IpxTransferData; + +// Unload event +extern KEVENT SpxUnloadEvent; + +extern ULONG SpxMaxPktSize[]; +extern ULONG SpxMaxPktSizeIndex; + +extern CTELock SpxGlobalInterlock; + + +extern CTELock SpxGlobalQInterlock; +extern PSPX_CONN_FILE SpxGlobalConnList; +extern PSPX_ADDR_FILE SpxGlobalAddrList; + +extern SPX_CONNFILE_LIST SpxPktConnList; +extern SPX_CONNFILE_LIST SpxRecvConnList; + +extern LONG SpxTimerCurrentTime; diff --git a/private/ntos/tdi/isnp/spx/h/isnspx.h b/private/ntos/tdi/isnp/spx/h/isnspx.h new file mode 100644 index 000000000..6080b0423 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/isnspx.h @@ -0,0 +1,363 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnspx.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include +#include +#include +#ifndef CTE_TYPEDEFS_DEFINED +#include +#endif +#include + +#include "wsnwlink.h" + +#define SPX_DEVICE_SIGNATURE (USHORT)(*(PUSHORT)"SD") +#define SPX_ADDRESS_SIGNATURE (USHORT)(*(PUSHORT)"AD") +#define SPX_ADDRESSFILE_SIGNATURE (USHORT)(*(PUSHORT)"AF") +#define SPX_CONNFILE_SIGNATURE (USHORT)(*(PUSHORT)"CF") + +#define SPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + +#define SPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +typedef UCHAR BYTE, *PBYTE; +typedef ULONG DWORD, *PDWORD; + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + +// +// PREQUEST +// SpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define SpxAllocateRequest(_Device,_Irp) \ + (_Irp) + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// SpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define SpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_TDI_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the TDI buffer chain associated with a request. +// + +#define REQUEST_TDI_BUFFER(_Request) \ + ((PVOID)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// PUNICODE_STRING +// REQUEST_OPEN_NAME( +// IN PREQUEST Request +// ); +// +// Used to access the RemainingName field of a request. +// + +#define REQUEST_OPEN_NAME(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->FileObject->FileName)) + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// SpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define SpxCompleteRequest(_Request) \ + { \ + CTELockHandle _CancelIrql; \ + DBGPRINT(TDI, INFO, \ + ("SpxCompleteRequest: Completing %lx with %lx\n", \ + (_Request), REQUEST_STATUS(_Request))); \ + \ + IoAcquireCancelSpinLock( &_CancelIrql ); \ + (_Request)->CancelRoutine = NULL; \ + IoReleaseCancelSpinLock( _CancelIrql ); \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT); \ + } + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +#include "fwddecls.h" + +// BUGBUG: This should go in ntddk.h? +#ifndef _NTIOAPI_ +#include "spxntdef.h" +#endif + +#include "spxreg.h" +#include "spxdev.h" +#include "spxbind.h" +#include "spxtimer.h" +#include "spxpkt.h" +#include "spxerror.h" +#include "spxaddr.h" +#include "spxconn.h" +#include "spxrecv.h" +#include "spxsend.h" +#include "spxquery.h" +#include "spxmem.h" +#include "spxutils.h" + + +// Globals +#include "globals.h" + + + + diff --git a/private/ntos/tdi/isnp/spx/h/spxaddr.h b/private/ntos/tdi/isnp/spx/h/spxaddr.h new file mode 100644 index 000000000..b49a4791e --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxaddr.h @@ -0,0 +1,426 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.h + +Abstract: + + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define DYNSKT_RANGE_START 0x4000 +#define DYNSKT_RANGE_END 0x7FFF +#define SOCKET_UNIQUENESS 1 + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. + +#define AFREF_CREATE 0 +#define AFREF_VERIFY 1 +#define AFREF_INDICATION 2 +#define AFREF_CONN_ASSOC 3 + +#define AFREF_TOTAL 4 + +typedef struct _SPX_ADDR_FILE { + +#if DBG + ULONG saf_RefTypes[AFREF_TOTAL]; +#endif + + CSHORT saf_Type; + CSHORT saf_Size; + + // number of references to this object. + ULONG saf_RefCount; + + // Linkage in address list. + struct _SPX_ADDR_FILE * saf_Next; + struct _SPX_ADDR_FILE * saf_GlobalNext; + + // List of associated connection/active or otherwise + struct _SPX_CONN_FILE * saf_AssocConnList; + + // the current state of the address file structure; this is either open or + // closing + USHORT saf_Flags; + + // address to which we are bound, pointer to its lock. + struct _SPX_ADDR * saf_Addr; + CTELock * saf_AddrLock; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT saf_FileObject; +#endif + + // device to which we are attached. + struct _DEVICE * saf_Device; + + // This holds the request used to close this address file, + // for pended completion. + PREQUEST saf_CloseReq; + + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + PTDI_IND_CONNECT saf_ConnHandler; + PVOID saf_ConnHandlerCtx; + + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. + PTDI_IND_DISCONNECT saf_DiscHandler; + PVOID saf_DiscHandlerCtx; + + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. + PTDI_IND_RECEIVE saf_RecvHandler; + PVOID saf_RecvHandlerCtx; + + // Send possible handler + PTDI_IND_SEND_POSSIBLE saf_SendPossibleHandler; + PVOID saf_SendPossibleHandlerCtx; + + // !!!We do not do datagrams or expedited data!!! + + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. + PTDI_IND_ERROR saf_ErrHandler; + PVOID saf_ErrHandlerCtx; + PVOID saf_ErrHandlerOwner; + + +} SPX_ADDR_FILE, *PSPX_ADDR_FILE; + +#define SPX_ADDRFILE_OPENING 0x0000 // not yet open for business +#define SPX_ADDRFILE_OPEN 0x0001 // open for business +#define SPX_ADDRFILE_CLOSING 0x0002 // closing +#define SPX_ADDRFILE_STREAM 0x0004 // Opened for stream mode operation +#define SPX_ADDRFILE_CONNIND 0x0008 // Connect ind in progress +#define SPX_ADDRFILE_SPX2 0x0010 // Attempt SPX2 address file +#define SPX_ADDRFILE_NOACKWAIT 0x0020 // Dont delay acks on assoc connections +#define SPX_ADDRFILE_IPXHDR 0x0040 // Pass ipx hdr on all assoc connections +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** +// If you are adding any more states to this beyond 0x0080, MAKE SURE to go +// in code and change statements like (Flags & SPX_***) to +// ((Flags & SPX_**) != 0)!!! I dont want to make that change that at this stage. +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** + +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. + +#define AREF_ADDR_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _SPX_ADDR { + +#if DBG + ULONG sa_RefTypes[AREF_TOTAL]; +#endif + + USHORT sa_Size; + CSHORT sa_Type; + + // number of references to this object. + ULONG sa_RefCount; + + // next address/this device object. + struct _SPX_ADDR * sa_Next; + + // The following fields are used to maintain state about this address. + // attributes of the address. + ULONG sa_Flags; + + // Next addressfile for this address + struct _SPX_ADDR_FILE * sa_AddrFileList; + + // List of inactive connections and active connections on this address file. + struct _SPX_CONN_FILE * sa_InactiveConnList; + struct _SPX_CONN_FILE * sa_ActiveConnList; + + // This is the list of connections which have a POST_LISTEN on them. They + // do not have a local connection id at this point. But will, when they move + // from here to the ActiveConnList, when the listen is satisfied (no matter + // if the accept has not been posted yet, in the case of non-autoaccept listens) + struct _SPX_CONN_FILE * sa_ListenConnList; + + CTELock sa_Lock; + + // the socket this address corresponds to. + USHORT sa_Socket; + + // device context to which we are attached. + struct _DEVICE * sa_Device; + CTELock * sa_DeviceLock; + +#ifdef ISN_NT + + // These two can be a union because they are not used + // concurrently. + union { + + // This structure is used for checking share access. + SHARE_ACCESS sa_ShareAccess; + + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + WORK_QUEUE_ITEM sa_DestroyAddrQueueItem; + + } u; + + // This structure is used to hold ACLs on the address. + PSECURITY_DESCRIPTOR sa_SecurityDescriptor; + +#endif + +} SPX_ADDR, *PSPX_ADDR; + +#define SPX_ADDR_CLOSING 0x00000001 + + +// ROUTINE PROTOTYPES + +VOID +SpxAddrRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address); + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile); + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile); + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest); + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile); + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address); + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request); + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT struct _SPX_CONN_FILE **ppSpxConnFile); + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrDestroy( + IN PVOID Parameter); + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device); + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +#if DBG +#define SpxAddrReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type],\ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrRef (_Address); \ + } + +#define SpxAddrLockReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrLockRef (_Address); \ + } + +#define SpxAddrDereference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + }\ + } + + +#define SpxAddrFileReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileRef (_AddressFile); \ + } + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileLockRef (_AddressFile); \ + } + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxAddrFileDeref (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxAddrReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock) + +#define SpxAddrLockReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock); + +#define SpxAddrDereference(_Address, _Type) \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + } + +#define SpxAddrFileReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock) + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock); + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + if (SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + (ULONG)-1, \ + (_AddressFile)->saf_AddrLock) == 1) { \ + SpxAddrFileDestroy (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) + +#endif // DBG diff --git a/private/ntos/tdi/isnp/spx/h/spxbind.h b/private/ntos/tdi/isnp/spx/h/spxbind.h new file mode 100644 index 000000000..81ad6ac58 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxbind.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +NTSTATUS +SpxInitBindToIpx( + VOID); + +VOID +SpxUnbindFromIpx( + VOID); + diff --git a/private/ntos/tdi/isnp/spx/h/spxconn.h b/private/ntos/tdi/isnp/spx/h/spxconn.h new file mode 100644 index 000000000..bb1173432 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxconn.h @@ -0,0 +1,1666 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +// Minimum value for RTT in ms. +// BUGBUG: Have these be a derivate of registry values. +#define SPX_T1_MIN 200 +#define MAX_RETRY_DELAY 5000 // 5 seconds +#define SPX_DEF_RENEG_RETRYCOUNT 1 // All reneg pkts except min sent once + +// Some types +typedef enum +{ + SPX_CALL_RECVLEVEL, + SPX_CALL_TDILEVEL +} SPX_CALL_LEVEL; + +typedef enum +{ + SPX_REQ_DATA, + SPX_REQ_ORDREL, + SPX_REQ_DISC + +} SPX_SENDREQ_TYPE; + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Connection. + +#define CFREF_CREATE 0 +#define CFREF_VERIFY 1 +#define CFREF_INDICATION 2 +#define CFREF_BYCTX 3 +#define CFREF_BYID 4 +#define CFREF_ADDR 5 +#define CFREF_REQ 6 +#define CFREF_TIMER 7 +#define CFREF_PKTIZE 8 +#define CFREF_RECV 9 +#define CFREF_ABORTPKT 10 +#define CFREF_ERRORSTATE 11 +#define CFREF_FINDROUTE 12 + +// +// New state added to reflect an SPXI connection which is waiting for +// a local disconnect after having indicated a RELEASE to AFD. +// +#define CFREF_DISCWAITSPX 13 + +#define CFREF_TOTAL 14 + +#define CFMAX_STATES 20 + +typedef struct _SPX_CONN_FILE +{ + +#if DBG + ULONG scf_RefTypes[CFREF_TOTAL]; + +#if 0 +// +// Disabled for now - to enable logging of states, move this array *after* the Type/Size; +// a change in their offset can cause problems since we assume the offset to be less than +// the size of an AddressFile structure. (see SpxTdiQueryInformation) +// + ULONG scf_StateBuffer[CFMAX_STATES]; + ULONG scf_NextStatePtr; +#endif + +#endif + + CSHORT scf_Type; + CSHORT scf_Size; + + // number of references to this object. + ULONG scf_RefCount; + + // Linkage in device address file list. The connection can be on the device + // connection list, address inactive/listen/active list. + struct _SPX_CONN_FILE * scf_Next; + struct _SPX_CONN_FILE * scf_AssocNext; + struct _SPX_CONN_FILE * scf_GlobalActiveNext; + + // Queued in a global list, stays here from creation to destroy. + struct _SPX_CONN_FILE * scf_GlobalNext; + struct _SPX_CONN_FILE * scf_PktNext; + struct _SPX_CONN_FILE * scf_ProcessRecvNext; + + // the current state of the connection. One main state and multiple substates. + ULONG scf_Flags; + + // More information + ULONG scf_Flags2; + +#if DBG + // Save the state of flags/flags2 before reinit. Overwritten every reinit. + ULONG scf_GhostFlags; + ULONG scf_GhostFlags2; + ULONG scf_GhostRefCount; + PREQUEST scf_GhostDiscReq; +#endif + + // Connection retry counts, or watchdog timer count when the connection goes + // active + union + { + LONG scf_CRetryCount; + LONG scf_WRetryCount; + }; + LONG scf_RRetryCount; + USHORT scf_RRetrySeqNum; + + union + { + ULONG scf_CTimerId; + ULONG scf_RTimerId; // Only after we turn active + }; + + ULONG scf_WTimerId; // Watchdog timer + ULONG scf_TTimerId; // TDI Connect/Disconnect timer + ULONG scf_ATimerId; // Ack timer id + + // Variables used to manage the Retry timer tick value + // Note our timer subsytem fires at 100ms granularity. + int scf_BaseT1; + int scf_AveT1; + int scf_DevT1; + + // Stored in HOST-ORDER + // LOCAL variables + USHORT scf_LocalConnId; + USHORT scf_SendSeqNum; // Debug dw +9a + USHORT scf_SentAllocNum; // dw +9c + + // REMOTE variables + USHORT scf_RecvSeqNum; // dw +9e + USHORT scf_RecdAckNum; // dw +a0 + USHORT scf_RecdAllocNum; // dw +a2 + + // RETRY sequence number + USHORT scf_RetrySeqNum; + + // Saved ack number to be used in building the reneg ack packet. + // Note that our RecvSeqNum which we normally use is overwritten + // when we receive a renegotiate request. + USHORT scf_RenegAckAckNum; + + // Stored in NETWORK-ORDER. scf_RemAckAddr contains the remote address + // for a data packet that had the ack bit set, buildAck will use this + // address. + BYTE scf_RemAddr[12]; + BYTE scf_RemAckAddr[12]; + USHORT scf_RemConnId; // Debug dw +be + + // Maximum packet size (or size of first) reneg packet. + USHORT scf_RenegMaxPktSize; + + // Local target to use in when sending acks. This is set to received + // data's indicated local target. + IPX_LOCAL_TARGET scf_AckLocalTarget; + + // Maximum packet size to use for this connection + USHORT scf_MaxPktSize; + UCHAR scf_DataType; + + // Local target to use in sends, initialized upon connect indication + // or when find_route completes + IPX_LOCAL_TARGET scf_LocalTarget; + + // Connection lock + CTELock scf_Lock; + + // address to which we are bound + struct _SPX_ADDR_FILE * scf_AddrFile; + + // Connection context + CONNECTION_CONTEXT scf_ConnCtx; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT scf_FileObject; +#endif + + // LIST_ENTRY of disconnect irps waiting for completion. There could be + // multiple disconnect inform irps. + LIST_ENTRY scf_DiscLinkage; + + // LIST_ENTRY of send requests (intially contains connect/listen/accept also) + // on this connection. + LIST_ENTRY scf_ReqLinkage; + + // Queue for completed requests awaiting completion + LIST_ENTRY scf_ReqDoneLinkage; + LIST_ENTRY scf_RecvDoneLinkage; + + // Queue for pending receives + LIST_ENTRY scf_RecvLinkage; + PREQUEST scf_CurRecvReq; + ULONG scf_CurRecvOffset; + ULONG scf_CurRecvSize; + + // Current request packetize info + PREQUEST scf_ReqPkt; + ULONG scf_ReqPktOffset; + ULONG scf_ReqPktSize; + ULONG scf_ReqPktFlags; + SPX_SENDREQ_TYPE scf_ReqPktType; + + // Single linked list of sequenced send/disc packets + PSPX_SEND_RESD scf_SendSeqListHead; + PSPX_SEND_RESD scf_SendSeqListTail; + + // Single linked list of send (unsequenced) packets + PSPX_SEND_RESD scf_SendListHead; + PSPX_SEND_RESD scf_SendListTail; + + // Single linked list of buffered recv packets. + PSPX_RECV_RESD scf_RecvListHead; + PSPX_RECV_RESD scf_RecvListTail; + + // Connect request + PREQUEST scf_ConnectReq; + + // This holds the request used to close this address file, + // for pended completion. We also pend cleanup requests for connections. + PREQUEST scf_CleanupReq; + PREQUEST scf_CloseReq; + +#if DBG + + // Packet being indicated, seq num, flags/flags2 + USHORT scf_PktSeqNum; + ULONG scf_PktFlags; + ULONG scf_PktFlags2; + + ULONG scf_IndBytes; + ULONG scf_IndLine; +#endif + +#if DBG_WDW_CLOSE + + // Keep track of how long the window was closed on this connection. + ULONG scf_WdwCloseAve; + LARGE_INTEGER scf_WdwCloseTime; // Time when wdw was closed +#endif + + // device to which we are attached. + struct _DEVICE * scf_Device; + +} SPX_CONN_FILE, *PSPX_CONN_FILE; + + +// Basic states +// Least significant byte of flags is used. +// Mutually exclusive states are coded as numbers, others are bit flags. +// Only main states are currently in form of numbers. Also, send and receive. +// +// Once we go active, we need SEND/RECEIVE/DISC substates to be mutually +// exclusive with each other. As all three could be active at the same time. + +// Connection MAIN states. These are all mutually exclusive. +#define SPX_CONNFILE_MAINMASK 0x00000007 +#define SPX_CONNFILE_ACTIVE 0x00000001 +#define SPX_CONNFILE_CONNECTING 0x00000002 +#define SPX_CONNFILE_LISTENING 0x00000003 +#define SPX_CONNFILE_DISCONN 0x00000004 + +// Connecting states (VALID when CONNFILE_CONNECTING) +#define SPX_CONNECT_MASK 0x000000F0 +#define SPX_CONNECT_SENTREQ 0x00000010 +#define SPX_CONNECT_NEG 0x00000020 +#define SPX_CONNECT_W_SETUP 0x00000030 + +// Listening states (VALID when CONNFILE_LISTENING) +#define SPX_LISTEN_MASK 0x000000F0 +#define SPX_LISTEN_RECDREQ 0x00000010 +#define SPX_LISTEN_SENTACK 0x00000020 +#define SPX_LISTEN_NEGACK 0x00000030 +#define SPX_LISTEN_SETUP 0x00000040 + +// Connection SUB states +// Send machine states (VALID when CONNFILE_ACTIVE) +#define SPX_SEND_MASK 0x000000F0 +#define SPX_SEND_IDLE 0x00000000 +#define SPX_SEND_PACKETIZE 0x00000010 +#define SPX_SEND_RETRY 0x00000020 +#define SPX_SEND_RETRYWD 0x00000030 +#define SPX_SEND_RENEG 0x00000040 +#define SPX_SEND_RETRY2 0x00000050 +#define SPX_SEND_RETRY3 0x00000060 +#define SPX_SEND_WD 0x00000070 // We dont reneg pkt size on wdog + // Also we change to this state only + // 2nd time wdog fires w/out ack. +#define SPX_SEND_NAK_RECD 0x00000080 + +// Receive machine states (VALID when CONNFILE_ACTIVE) +#define SPX_RECV_MASK 0x00000F00 +#define SPX_RECV_IDLE 0x00000000 +#define SPX_RECV_POSTED 0x00000100 +#define SPX_RECV_PROCESS_PKTS 0x00000200 + +// Disconnect states (VALID when CONNFILE_DISCONN/CONNFILE_ACTIVE) +// These are valid when either ACTIVE/DISCONN is set. We use these when +// active for a orderly release, i.e. we receive pkt from remote, but we +// stay active (setting SPX_DISC_RECV_ORDREL) until our client posts a +// disconnect, which is when we move to disconnecting. +#define SPX_DISC_MASK 0x0000F000 +#define SPX_DISC_IDLE 0x00000000 +#define SPX_DISC_ABORT 0x00001000 +#define SPX_DISC_SENT_IDISC 0x00002000 +#define SPX_DISC_POST_ORDREL 0x00003000 +#define SPX_DISC_SENT_ORDREL 0x00004000 +#define SPX_DISC_ORDREL_ACKED 0x00005000 +#define SPX_DISC_POST_IDISC 0x00006000 + +// [SA] bug #14655 added flag to indicate that SpxConnInactivate already called for +// this disconnecting connection +// +#define SPX_DISC_INACTIVATED 0x00007000 + +// The following are not mutually exclusive. +#define SPX_CONNFILE_RECVQ 0x00010000 // Process completed receives/pkts +#define SPX_CONNFILE_RENEG_SIZE 0x00020000 // Size changed in renegotiate pkt +#define SPX_CONNFILE_ACKQ 0x00040000 // Waiting to piggyback ack queue +#define SPX_CONNFILE_PKTQ 0x00080000 // Waiting to packetize queue + +#define SPX_CONNFILE_ASSOC 0x00100000 // associated +#define SPX_CONNFILE_NEG 0x00200000 // CR had neg set (for delayed accept) +#define SPX_CONNFILE_SPX2 0x00400000 +#define SPX_CONNFILE_STREAM 0x00800000 +#define SPX_CONNFILE_R_TIMER 0x01000000 // Retry timer (only after ACTIVE) +#define SPX_CONNFILE_C_TIMER 0x01000000 // Connect timer +#define SPX_CONNFILE_W_TIMER 0x02000000 // Watchdog timer +#define SPX_CONNFILE_T_TIMER 0x04000000 // tdi connect/disc timer specified +#define SPX_CONNFILE_RENEG_PKT 0x08000000 // Renegotiate changed size, repacketize +#define SPX_CONNFILE_IND_IDISC 0x10000000 // Indicated abortive disc to afd +#define SPX_CONNFILE_IND_ODISC 0x20000000 // Indicated orderly release to afd + +#define SPX_CONNFILE_STOPPING 0x40000000 +#define SPX_CONNFILE_CLOSING 0x80000000 // closing + +#define SPX_CONNFILE2_PKT_NOIND 0x00000001 +#define SPX_CONNFILE2_RENEGRECD 0x00000002 // A renegotiate was received. + // scf_RenegAckAckNum set. +#define SPX_CONNFILE2_PKT 0x00000004 +#define SPX_CONNFILE2_FINDROUTE 0x00000010 // A find route in progress on conn. +#define SPX_CONNFILE2_NOACKWAIT 0x00000020 // Dont delay acks on connection, option +#define SPX_CONNFILE2_IMMED_ACK 0x00000040 // Send an immediate ack,no back traffic +#define SPX_CONNFILE2_IPXHDR 0x00000080 // Pass ipxhdr in receives + +// +// [SA] Saves the IDisc flag passed to AbortiveDisc; this is TRUE only if there was +// a remote disconnect on an SPX connection (in which case, we indicate TDI_DISCONNECT_RELEASE +// else we indicate TDI_DISCONNECT_ABORT) +// +#define SPX_CONNFILE2_IDISC 0x00000100 + +// +// Indicates an SPXI connfile waiting for a local disconnect in response +// to a TDI_DISCONNECT_RELEASE to AFD. +// +#define SPX_CONNFILE2_DISC_WAIT 0x00000200 + +// FindRoute request structure +typedef struct _SPX_FIND_ROUTE_REQUEST +{ + // !!!!This must be the first element in the structure + IPX_FIND_ROUTE_REQUEST fr_FindRouteReq; + PVOID fr_Ctx; + +} SPX_FIND_ROUTE_REQUEST, *PSPX_FIND_ROUTE_REQUEST; + +typedef struct _SPX_CONNFILE_LIST +{ + PSPX_CONN_FILE pcl_Head; + PSPX_CONN_FILE pcl_Tail; + +} SPX_CONNFILE_LIST, *PSPX_CONNFILE_LIST; + +// Exported routines + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT pConnCtx, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT NTSTATUS * pStatus); + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile); + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn); + +#if DBG +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile); +#endif + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus); + +BOOLEAN +SpxConnDequeuePktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pAckHdr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +// LOCAL functions +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr); + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile); + +USHORT +spxConnGetId( + VOID); + +VOID +spxConnInsertIntoActiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoInactiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoListenList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile); + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt); + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt); + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize); + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size); + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest); + +NTSTATUS +spxConnProcessRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnProcessIndData( + IN PSPX_CONN_FILE pSpxConnFile, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnOrderlyDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnInformedDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN Flag); // [SA] Bug #15249 + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +// +// MACROS +// +#define SHIFT100000 16 + +#define SPX_CONVERT100NSTOCENTISEC(Li) \ + RtlExtendedMagicDivide((Li), Magic100000, SHIFT100000) + +#define UNSIGNED_BETWEEN_WITH_WRAP(Low, High, Target) \ + ((Low <= High) ? ((Target >= Low) && (Target <= High)) : \ + ((Target >= Low) || (Target <= High))) + +// This is with the assumption that the window size will never be greater +// than the difference of 0x8000 and 0x1000. If High is < 1000 and Low +// is > 8000 then we can assume a wrap happened. Otherwise, we assume no +// wrap and do a straight compare. +#define MAX_WINDOW_SIZE 0x6000 +#define DEFAULT_WINDOW_SIZE 8 + +#define UNSIGNED_GREATER_WITH_WRAP(High, Low) \ + (((High < 0x1000) && (Low > 0x8000)) ? TRUE : (High > Low)) + +#define SPX_SET_ACKNUM(pSpxConnFile, RecdAckNum, RecdAllocNum) \ + { \ + DBGPRINT(SEND, DBG, \ + ("SPX_SET_ACKNUM: %lx.%lx = %lx.%lx (%s.%d)\n", \ + (RecdAckNum), (RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAllocNum), \ + __FILE__, __LINE__)); \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAckNum))) \ + { \ + (pSpxConnFile)->scf_RecdAckNum = (RecdAckNum); \ + } \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAllocNum)))\ + { \ + (pSpxConnFile)->scf_RecdAllocNum = (RecdAllocNum); \ + } \ + } + +#define BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum) \ + { \ + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT); \ + } + +#define END_PROCESS_PACKET(pSpxConnFile, fBuffered, fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, \ + (SPX_CONNFILE2_PKT |SPX_CONNFILE2_RENEGRECD)); \ + if (fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); \ + SPX_SET_RECVNUM(pSpxConnFile, fBuffered); \ + } \ + } + +#define INCREMENT_WINDOW(pSpxConnFile) \ + ((pSpxConnFile)->scf_SentAllocNum++) + +#define ADD_TO_WINDOW(pSpxConnFile, numPkts) \ + ((pSpxConnFile)->scf_SentAllocNum += (numPkts)) + +#if DBG_WDW_CLOSE +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + \ + if (fBuffered && \ + (UNSIGNED_GREATER_WITH_WRAP( \ + (pSpxConnFile)->scf_RecvSeqNum, \ + (pSpxConnFile)->scf_SentAllocNum))) \ + { \ + KeQuerySystemTime( \ + (PLARGE_INTEGER)&pSpxConnFile->scf_WdwCloseTime); \ + } \ + } +#else +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + } +#endif + + +#define SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest) \ + { \ + RemoveEntryList(REQUEST_LINKAGE((pRequest))); \ + pSpxConnFile->scf_CurRecvReq = NULL; \ + pSpxConnFile->scf_CurRecvOffset = 0; \ + pSpxConnFile->scf_CurRecvSize = 0; \ + if (!IsListEmpty(&(pSpxConnFile)->scf_RecvLinkage)) \ + { \ + PTDI_REQUEST_KERNEL_RECEIVE _p; \ + DBGPRINT(RECEIVE, DBG, \ + ("spxConnProcessRecv: CURRECV %lx\n", pRequest)); \ + \ + (pSpxConnFile)->scf_CurRecvReq = \ + LIST_ENTRY_TO_REQUEST( \ + (pSpxConnFile)->scf_RecvLinkage.Flink); \ + \ + _p = (PTDI_REQUEST_KERNEL_RECEIVE) \ + REQUEST_PARAMETERS((pSpxConnFile)->scf_CurRecvReq); \ + \ + (pSpxConnFile)->scf_CurRecvOffset = 0; \ + (pSpxConnFile)->scf_CurRecvSize = (_p)->ReceiveLength; \ + } \ + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || \ + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) \ + { \ + SPX_RECV_SETSTATE( \ + pSpxConnFile, \ + (pSpxConnFile->scf_CurRecvReq == NULL) ? \ + SPX_RECV_IDLE : SPX_RECV_POSTED); \ + } \ + } + +#define SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ActiveConnList; \ + (pSpxAddr)->sa_ActiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_INACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_InactiveConnList; \ + (pSpxAddr)->sa_InactiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ListenConnList; \ + (pSpxAddr)->sa_ListenConnList = pSpxConnFile; \ + } + + +// +// STATE MANIPULATION +// + +#if 0 +// +// Disabled for now +// +#define SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_StateBuffer[(pSpxConnFile)->scf_NextStatePtr++] = \ + (pSpxConnFile)->scf_Flags; \ + (pSpxConnFile)->scf_NextStatePtr %= CFMAX_STATES; +#else + +#define SPX_STORE_LAST_STATE(pSpxConnFile) + +#endif + +#define SPX_MAIN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNFILE_MAINMASK) + +// #define SPX_CONN_IDLE(pSpxConnFile) \ +// ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == 0)) + +#define SPX_CONN_IDLE(pSpxConnFile) \ + ((BOOLEAN)((SPX_MAIN_STATE(pSpxConnFile) == 0) || \ + ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && \ + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)))) + +#define SPX_CONN_ACTIVE(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE)) + +#define SPX_CONN_CONNECTING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_CONNECTING)) + +#define SPX_CONN_LISTENING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_LISTENING)) + +#define SPX_CONN_DISC(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN)) + +#if DBG + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#else + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#endif + +#define SPX_CONN_FLAG(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags & (Flag)) != 0)) + +#define SPX_CONN_FLAG2(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags2 & (Flag)) != 0)) + +#if DBG + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) +#else + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) + +#endif + +#define SPX_CONN_SETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 |= (Flag)) + +#define SPX_CONN_RESETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags &= ~(Flag)) + +#define SPX_CONN_RESETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 &= ~(Flag)) + +#define SPX2_CONN(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_SPX2)) + +#define SPX_CONN_STREAM(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_CONN_MSG(pSpxConnFile) \ + (!SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_LISTEN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_LISTEN_MASK) + +#define SPX_CONNECT_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNECT_MASK) + +#define SPX_SEND_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_SEND_MASK) + +#define SPX_RECV_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_RECV_MASK) + +#define SPX_DISC_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_DISC_MASK) + +#if DBG + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } + +#else + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } +#endif //DBG +#define SpxConnQueueSendPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendListTail->sr_Next = _pSendResd; \ + (pSpxConnFile)->scf_SendListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } + +#define SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendSeqListTail->sr_Next = _pSendResd;\ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendSeqPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendSeqListHead;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } + +#define SpxConnQueueRecvPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + (pSpxConnFile)->scf_RecvListTail->rr_Next = _pRecvResd; \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } \ + } + +#define SpxConnQueueRecvPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + _pRecvResd->rr_Next = (pSpxConnFile)->scf_RecvListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd; \ + } \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } + +#if DBG +#define SpxConnFileReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileRef (_ConnFile); \ + } + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileLockRef (_ConnFile); \ + } + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxConnFileDeref (_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _l; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_l)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, _l); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxConnFileReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock) + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock); + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + SpxConnFileDeref(_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_lockHandle)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, (_lockHandle)); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) + +#endif // DBG + + +// Set the packet size. If we are spx1 or spx2 and !neg, check if we are different +// nets, set to min then, else use the size indicated by IPX. If we are spx2, just +// set it to our local max. +// +// Also always even out packet size and round down. This solves an issue with +// data size needing to be even for some novell 802.2 clients. +// +// Fix after beta2 for tokring using receive size. Only if spx2 and neg. +#if defined(_PNP_POWER) +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + &(pSpxConnFile)->scf_LocalTarget.NicHandle, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#else +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + (pSpxConnFile)->scf_LocalTarget.NicId, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#endif _PNP_POWER + +#if DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#else // DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#endif // DBG + +#define SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile) \ + { \ + if (!SPX_CONN_FLAG( \ + (pSpxConnFile), \ + SPX_CONNFILE_RECVQ)) \ + { \ + SPX_CONN_SETFLAG((pSpxConnFile), SPX_CONNFILE_RECVQ); \ + SpxConnFileLockReference(pSpxConnFile, CFREF_RECV); \ + SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile); \ + } \ + } + +#define SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile) \ + { \ + if (SpxPktConnList.pcl_Tail) \ + { \ + SpxPktConnList.pcl_Tail->scf_PktNext = pSpxConnFile; \ + SpxPktConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxPktConnList.pcl_Tail = \ + SpxPktConnList.pcl_Head = pSpxConnFile; \ + } \ + } + +#define SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile) \ + { \ + if (SpxRecvConnList.pcl_Tail) \ + { \ + SpxRecvConnList.pcl_Tail->scf_ProcessRecvNext = pSpxConnFile; \ + SpxRecvConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxRecvConnList.pcl_Tail = \ + SpxRecvConnList.pcl_Head = pSpxConnFile; \ + } \ + } + + diff --git a/private/ntos/tdi/isnp/spx/h/spxdev.h b/private/ntos/tdi/isnp/spx/h/spxdev.h new file mode 100644 index 000000000..30c0adae5 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxdev.h @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +// Hash buckets for SPX_ADDR done using socket number +#define NUM_SPXADDR_HASH_BUCKETS 8 +#define NUM_SPXADDR_HASH_MASK 7 +#define NUM_SPXCONN_HASH_BUCKETS 8 +#define NUM_SPXCONN_HASH_MASK 7 + +// This structure defines the per-device structure for SPX +// (one of these is allocated globally). +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_ORPHAN 4 + +#define DREF_TOTAL 5 + +typedef struct _DEVICE { + + DEVICE_OBJECT dev_DevObj; // the I/O system's device object. + +#if DBG + ULONG dev_RefTypes[DREF_TOTAL]; +#endif + + CSHORT dev_Type; // type of this structure + USHORT dev_Size; // size of this structure + +#if DBG + UCHAR dev_Signature1[4]; // contains "SPX1" +#endif + + // activity count/this provider. + LONG dev_RefCount; + UCHAR dev_State; + + // number of adapters IPX is bound to. + USHORT dev_Adapters; + + // GLOBAL lock for reference count (used in ExInterlockedXxx calls). + CTELock dev_Interlock; + CTELock dev_Lock; + + // Hash table of lists of addresses opened on this device + struct _SPX_ADDR * dev_AddrHashTable[NUM_SPXADDR_HASH_BUCKETS]; + + // List of all active connections, later this be a tree. + struct _SPX_CONN_FILE * dev_GlobalActiveConnList[NUM_SPXCONN_HASH_BUCKETS]; + USHORT dev_NextConnId; + + // Other configuration parameters. + // Where the current socket allocation is. + USHORT dev_CurrentSocket; + + // Our node and network. + UCHAR dev_Network[4]; + UCHAR dev_Node[6]; + + // Pointer to the config information from registry + PCONFIG dev_ConfigInfo; + + // Control channel identifier + ULONG dev_CcId; + + // These are kept around for error logging, and stored right + // after this structure. + PWCHAR dev_DeviceName; +#if defined(_PNP_POWER) + USHORT dev_DeviceNameLen; +#else + ULONG dev_DeviceNameLen; +#endif _PNP_POWER + +#if DBG + UCHAR dev_Signature2[4]; // contains "SPX2" +#endif + + // Handle to ndis buffer pool for spx stack. + NDIS_HANDLE dev_NdisBufferPoolHandle; + + // registration handle with tdi clients. +#if defined(_PNP_POWER) + HANDLE dev_TdiRegistrationHandle; +#endif _PNP_POWER + + // This interlock is used to guard access to the statistics + // define below. + KSPIN_LOCK dev_StatInterlock; // for ULONG quantities + KSPIN_LOCK dev_StatSpinLock; // for LARGE_INTEGER quantities + + // Counters for most of the statistics that SPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + TDI_PROVIDER_STATISTICS dev_Stat; + + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + ERESOURCE dev_AddrResource; + + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + TDI_PROVIDER_INFO dev_ProviderInfo; // information about this provider. + +} DEVICE, * PDEVICE; + +// device state definitions +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +// SPX device name +#define SPX_DEVICE_NAME L"\\Device\\NwlnkSpx" + +#define SPX_TDI_RESOURCES 9 + + +// MACROS +#if DBG + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxDerefDevice (_Device); \ + } + +#else + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + SpxDerefDevice (_Device) + +#endif + +// EXPORTED ROUTINES + +VOID +SpxDestroyDevice( + IN PDEVICE Device); + +VOID +SpxDerefDevice( + IN PDEVICE Device); + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr); diff --git a/private/ntos/tdi/isnp/spx/h/spxerror.h b/private/ntos/tdi/isnp/spx/h/spxerror.h new file mode 100644 index 000000000..761342512 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxerror.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxerror.h + +Abstract: + + This module contains some error definitions for spx. + +Author: + + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + +Notes: Tab stop: 4 +--*/ + +// Define the modules names for SPX - use the high bits. +#define SPXDRVR 0x00010000 +#define SPXREG 0x00020000 +#define SPXDEV 0x00030000 +#define SPXBIND 0x00040000 +#define SPXRECV 0x00050000 +#define SPXSEND 0x00060000 +#define SPXTIMER 0x00070000 +#define SPXERROR 0x00080000 +#define SPXPKT 0x00090000 +#define SPXUTILS 0x000a0000 +#define SPXCPKT 0x000b0000 +#define SPXCONN 0x000c0000 +#define SPXADDR 0x000d0000 +#define SPXCUTIL 0x000e0000 +#define SPXINIT 0x000f0000 +#define SPXMEM 0x00100000 +#define SPXQUERY 0x00200000 + + +// DEBUGGING SUPPORT: +// Debugging messages are provided per-subsystem defined here, and within +// the subsystems, there are 4 levels of messages. +// +// The four levels of debug messages are: +// +// INFO: Informational messages, eg., entry exit in routines +// DBG: Used when debugging some msgs are turned from info to dbg +// WARN: Something went wrong, but its not an error, eg., packet was not ours +// ERR: Error situations, but we can still run if a retry happens +// FATAL: In this situation, the driver is not operational + +#define DBG_LEVEL_INFO 0x4000 +#define DBG_LEVEL_DBG 0x5000 +#define DBG_LEVEL_DBG1 0x5001 +#define DBG_LEVEL_DBG2 0x5002 +#define DBG_LEVEL_DBG3 0x5003 +#define DBG_LEVEL_WARN 0x6000 +#define DBG_LEVEL_ERR 0x7000 +#define DBG_LEVEL_FATAL 0x8000 + +// SUBSYSTEMS +#define DBG_COMP_DEVICE 0x00000001 +#define DBG_COMP_CREATE 0x00000002 +#define DBG_COMP_ADDRESS 0x00000004 +#define DBG_COMP_SEND 0x00000008 +#define DBG_COMP_NDIS 0x00000010 +#define DBG_COMP_RECEIVE 0x00000020 +#define DBG_COMP_CONFIG 0x00000040 +#define DBG_COMP_PACKET 0x00000080 +#define DBG_COMP_RESOURCES 0x00000100 +#define DBG_COMP_BIND 0x00000200 +#define DBG_COMP_UNLOAD 0x00000400 +#define DBG_COMP_DUMP 0x00000800 +#define DBG_COMP_REFCOUNTS 0x00001000 +#define DBG_COMP_SYSTEM 0x00002000 +#define DBG_COMP_CRITSEC 0x00004000 +#define DBG_COMP_UTILS 0x00008000 +#define DBG_COMP_TDI 0x00010000 +#define DBG_COMP_CONNECT 0x00020000 +#define DBG_COMP_DISC 0x00040000 +#define DBG_COMP_ACTION 0x00080000 +#define DBG_COMP_STATE 0x00100000 + +#define DBG_COMP_MOST (DBG_COMP_DEVICE | \ + DBG_COMP_CREATE | \ + DBG_COMP_ADDRESS | \ + DBG_COMP_SEND | \ + DBG_COMP_NDIS | \ + DBG_COMP_RECEIVE | \ + DBG_COMP_CONFIG | \ + DBG_COMP_PACKET | \ + DBG_COMP_RESOURCES | \ + DBG_COMP_BIND | \ + DBG_COMP_UNLOAD | \ + DBG_COMP_DUMP | \ + DBG_COMP_REFCOUNTS | \ + DBG_COMP_SYSTEM | \ + DBG_COMP_CRITSEC | \ + DBG_COMP_UTILS | \ + DBG_COMP_TDI | \ + DBG_COMP_CONNECT | \ + DBG_COMP_DISC | \ + DBG_COMP_ACTION | \ + DBG_COMP_STATE) + + +// More debugging support. These values define the dumping components. +// There are a max of 32 such components that can be defined. Each of +// these are associated with a dump routine. It one is specified and +// enabled, periodically it is called. It is upto that component to +// decide what it wants to do + +#define DBG_DUMP_DEF_INTERVAL 30 // In Seconds + +// This defines the number of times an error has to happen consecutively before +// it gets logged again. +#define ERROR_CONSEQ_FREQ 200 +#define ERROR_CONSEQ_TIME (60*30) // 30 minutes + +#ifdef DBG +typedef VOID (*DUMP_ROUTINE)(VOID); + +extern +BOOLEAN +SpxDumpComponents( + IN PVOID Context); + +#endif + +// +// PROTOTYPES +// + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue); + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); + + +// +// MACROS +// + +#if DBG +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#else + +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#endif + + +#if DBG + +#define DBGPRINT(Component, Level, Fmt) \ + { \ + if (((DBG_LEVEL_ ## Level) >= SpxDebugLevel) && \ + (SpxDebugSystems & (DBG_COMP_ ## Component))) \ + { \ + DbgPrint("SPX: "); \ + DbgPrint Fmt; \ + } \ + } + +#define DBGBRK(Level) \ + { \ + if ((DBG_LEVEL_ ## Level) >= SpxDebugLevel) \ + DbgBreakPoint(); \ + } + +#define TMPLOGERR() \ + { \ + DBGPRINT(MOST, ERR, \ + ("TempErrLog: %s, Line %ld\n", __FILE__, __LINE__)); \ + } + +#else +#define DBGPRINT(Component, Level, Fmt) +#define DBGBRK(Level) +#define TMPLOGERR() +#endif + +extern +VOID +SpxWriteErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); diff --git a/private/ntos/tdi/isnp/spx/h/spxmem.h b/private/ntos/tdi/isnp/spx/h/spxmem.h new file mode 100644 index 000000000..717c69a6b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxmem.h @@ -0,0 +1,142 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.h + +Abstract: + + This module contains memory management routines. + +Author: + + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +#define QWORDSIZEBLOCK(Size) (((Size)+sizeof(LARGE_INTEGER)-1) & ~(sizeof(LARGE_INTEGER)-1)) +#define SPX_MEMORY_SIGNATURE *(PULONG)"SPXM" +#define ZEROED_MEMORY_TAG 0xF0000000 +#define SPX_TAG *((PULONG)"SPX ") + +// +// Definitions for the block management package +// +typedef UCHAR BLKID; + +// Add a BLKID_xxx and an entry to atalkBlkSize for every block client +#define BLKID_TIMERLIST (BLKID)0 +#define BLKID_NDISSEND (BLKID)1 +#define BLKID_NDISRECV (BLKID)2 +#define NUM_BLKIDS (BLKID)3 + +typedef struct _BLK_CHUNK +{ + struct _BLK_CHUNK * bc_Next; // Pointer to next in the link + SHORT bc_NumFrees; // Number of free blocks in the chunk + UCHAR bc_Age; // Number of invocations since the chunk free + BLKID bc_BlkId; // Id of the block + struct _BLK_HDR * bc_FreeHead; // Head of the list of free blocks + +#ifndef SPX_OWN_PACKETS + PVOID bc_ChunkCtx; // Used to store pool header if not own + // packets +#else + PVOID bc_Padding; // Keep the header 16 bytes +#endif + + // This is followed by an array of N blks of size M such that the block header + // is exactly spxChunkSize[i] + +} BLK_CHUNK, *PBLK_CHUNK; + +typedef struct _BLK_HDR +{ + union + { + struct _BLK_HDR * bh_Next; // Valid when it is free + struct _BLK_CHUNK * bh_pChunk; // The parent chunk to which this blocks belong + // valid when it is allocated + }; + PVOID bh_Padding; // Make the header 8 bytes +} BLK_HDR, *PBLK_HDR; + +#define BC_OVERHEAD (8+8) // LARGE_INTEGER for SpxAllocMemory() header and + // POOL_HEADER for ExAllocatePool() header + +#define BLOCK_POOL_TIMER 1000 // Check interval (1 sec) +#define MAX_BLOCK_POOL_AGE 3 // # of timer invocations before free + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + + +#ifdef TRACK_MEMORY_USAGE + +#define SpxAllocateMemory(Size) SpxAllocMem((Size), FILENUM | __LINE__) + +extern +PVOID +SpxAllocMem( + IN ULONG Size, + IN ULONG FileLine +); + +extern +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine +); + +#else + +#define SpxAllocateMemory(Size) SpxAllocMem(Size) +#define SpxTrackMemoryUsage(pMem, Alloc, FileLine) + +extern +PVOID +SpxAllocMem( + IN ULONG Size +); + +#endif // TRACK_MEMORY_USAGE + +VOID +SpxFreeMemory( + IN PVOID pBuf); + +#define SpxAllocateZeroedMemory(Size) SpxAllocateMemory((Size) | ZEROED_MEMORY_TAG) + + +extern +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice); + +extern +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice); + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId); + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId); + diff --git a/private/ntos/tdi/isnp/spx/h/spxntdef.h b/private/ntos/tdi/isnp/spx/h/spxntdef.h new file mode 100644 index 000000000..60f9c9e54 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxntdef.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxntdef.h + +Abstract: + + Missing nt definitions in ntddk.h + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +NTSTATUS +NTAPI +NtCreateFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + +NTSTATUS +NTAPI +NtClose( + IN HANDLE Handle + ); + +NTSTATUS +NTAPI +NtDeviceIoControlFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG IoControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength + ); + +NTSTATUS +NTAPI +NtWaitForSingleObject( + IN HANDLE Handle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL + ); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxpkt.h b/private/ntos/tdi/isnp/spx/h/spxpkt.h new file mode 100644 index 000000000..b643ed95b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxpkt.h @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// Use our own NDIS packets +#define SPX_OWN_PACKETS 1 + +// Offsets into the IPX header +#define IPX_HDRSIZE 30 // Size of the IPX header +#define IPX_CHECKSUM 0 // Checksum +#define IPX_LENGTH 2 // Length +#define IPX_XPORTCTL 4 // Transport Control +#define IPX_PKTTYPE 5 // Packet Type +#define IPX_DESTADDR 6 // Dest. Address (Total) +#define IPX_DESTNET 6 // Dest. Network Address +#define IPX_DESTNODE 10 // Dest. Node Address +#define IPX_DESTSOCK 16 // Dest. Socket Number +#define IPX_SRCADDR 18 // Source Address (Total) +#define IPX_SRCNET 18 // Source Network Address +#define IPX_SRCNODE 22 // Source Node Address +#define IPX_SRCSOCK 28 // Source Socket Number + +#define IPX_NET_LEN 4 +#define IPX_NODE_LEN 6 + + +#include + +// Definition of the IPX/SPX header. +typedef struct _IPXSPX_HEADER +{ + USHORT hdr_CheckSum; + USHORT hdr_PktLen; + UCHAR hdr_XportCtrl; + UCHAR hdr_PktType; + UCHAR hdr_DestNet[4]; + UCHAR hdr_DestNode[6]; + USHORT hdr_DestSkt; + UCHAR hdr_SrcNet[4]; + UCHAR hdr_SrcNode[6]; + USHORT hdr_SrcSkt; + + // SPX Header Elements + UCHAR hdr_ConnCtrl; + UCHAR hdr_DataType; + USHORT hdr_SrcConnId; + USHORT hdr_DestConnId; + USHORT hdr_SeqNum; + USHORT hdr_AckNum; + USHORT hdr_AllocNum; + + // For non-CR SPXII packets only + USHORT hdr_NegSize; + +} IPXSPX_HDR, *PIPXSPX_HDR; + +#include + +// NDIS Packet size +#define NDIS_PACKET_SIZE 48 + +// Minimum header size (doesnt include neg size) +#define MIN_IPXSPX_HDRSIZE (sizeof(IPXSPX_HDR) - sizeof(USHORT)) +#define MIN_IPXSPX2_HDRSIZE sizeof(IPXSPX_HDR) +#define SPX_CR_PKTLEN 42 + +// SPX packet type +#define SPX_PKT_TYPE 0x5 + +// Connection control fields +#define SPX_CC_XHD 0x01 +#define SPX_CC_RES1 0x02 +#define SPX_CC_NEG 0x04 +#define SPX_CC_SPX2 0x08 +#define SPX_CC_EOM 0x10 +#define SPX_CC_ATN 0x20 +#define SPX_CC_ACK 0x40 +#define SPX_CC_SYS 0x80 + +#define SPX_CC_CR (SPX_CC_ACK | SPX_CC_SYS) + +// Data stream types +#define SPX2_DT_ORDREL 0xFD +#define SPX2_DT_IDISC 0xFE +#define SPX2_DT_IDISC_ACK 0xFF + +// Negotiation size +#define SPX_MAX_PACKET 576 +#define SPX_NEG_MIN SPX_MAX_PACKET +#define SPX_NEG_MAX 65535 + +// No packet references connection. But if the sends are being aborted, and +// the packet happens to be owned by ipx at the time, the pkt is dequeued from +// conn, the ABORT flag is set and conn is referenced for packet. +// +// Send packet states +// ABORT : Used for aborted packet. Calls AbortSendPkt(). +// IPXOWNS : Currently owned by ipx +// FREEDATA: Frees the data associated with second ndis buffer desc +// ACKREQ : Only for sequenced packets. Set by retry timer in packets it wants +// resent (1 for spx1, all pending for spx2) with ack bit set. +// DESTROY : Only for non-sequenced packets, dequeue packet from list and free. +// REQ : For both seq/non-seq. A request is associated with the packet +// SEQ : Packet is a sequenced packet. +// LASTPKT : Packet is last packet comprising the request, if acked req is done. +// EOM : Send EOM with the last packet for this request +// ACKEDPKT: Send completion must only deref req with pkt and complete if zero. +// + +#define SPX_SENDPKT_IDLE 0 +#define SPX_SENDPKT_ABORT 0x0002 +#define SPX_SENDPKT_IPXOWNS 0x0004 +#define SPX_SENDPKT_FREEDATA 0x0008 +#define SPX_SENDPKT_ACKREQ 0x0010 +#define SPX_SENDPKT_DESTROY 0x0020 +#define SPX_SENDPKT_REQ 0x0040 +#define SPX_SENDPKT_SEQ 0x0080 +#define SPX_SENDPKT_LASTPKT 0x0100 +#define SPX_SENDPKT_ACKEDPKT 0x0200 +#define SPX_SENDPKT_EOM 0x0400 +#define SPX_SENDPKT_REXMIT 0x0800 + +// Packet types +#define SPX_TYPE_CR 0x01 +#define SPX_TYPE_CRACK 0x02 +#define SPX_TYPE_SN 0x03 +#define SPX_TYPE_SNACK 0x04 +#define SPX_TYPE_SS 0x05 +#define SPX_TYPE_SSACK 0x06 +#define SPX_TYPE_RR 0x07 +#define SPX_TYPE_RRACK 0x08 +#define SPX_TYPE_IDISC 0x09 +#define SPX_TYPE_IDISCACK 0x0a +#define SPX_TYPE_ORDREL 0x0b +#define SPX_TYPE_ORDRELACK 0x0c +#define SPX_TYPE_DATA 0x0d +#define SPX_TYPE_DATAACK 0x0e +#define SPX_TYPE_DATANACK 0x0f +#define SPX_TYPE_PROBE 0x10 + +// Definition of the protocol reserved field of a send packet. +// BUGBUG: Make Len/HdrLen USHORTS, move to the end before the +// sr_SentTime so we dont use padding space. +typedef struct _SPX_SEND_RESD +{ + UCHAR sr_Id; // Set to SPX + UCHAR sr_Type; // What kind of packet + USHORT sr_State; // State of send packet + PVOID sr_Reserved1; // Needed by IPX + PVOID sr_Reserved2; // Needed by IPX +#if defined(_PNP_POWER) + PVOID sr_Reserved[SEND_RESERVED_COMMON_SIZE-2]; // needed by IPX for local target +#endif _PNP_POWER + ULONG sr_Len; // Length of packet + ULONG sr_HdrLen; // Included header length + + struct _SPX_SEND_RESD * sr_Next; // Points to next packet + // in send queue in conn. + PREQUEST sr_Request; // request associated + ULONG sr_Offset; // Offset in mdl for sends + +#ifndef SPX_OWN_PACKETS + PVOID sr_FreePtr; // Ptr to use in free chunk +#endif + + struct _SPX_CONN_FILE * sr_ConnFile; // that this send is on + USHORT sr_SeqNum; // Seq num for seq pkts + + // Quad word aligned. + LARGE_INTEGER sr_SentTime; // Time packet was sent + // Only valid for data pkt + // with ACKREQ set. + +} SPX_SEND_RESD, *PSPX_SEND_RESD; + + + +// Recv packet states +#define SPX_RECVPKT_IDLE 0 +#define SPX_RECVPKT_BUFFERING 0x0001 +#define SPX_RECVPKT_IDISC 0x0002 +#define SPX_RECVPKT_ORD_DISC 0x0004 +#define SPX_RECVPKT_INDICATED 0x0008 +#define SPX_RECVPKT_SENDACK 0x0010 +#define SPX_RECVPKT_EOM 0x0020 +#define SPX_RECVPKT_IMMEDACK 0x0040 + +#define SPX_RECVPKT_DISCMASK (SPX_RECVPKT_ORD_DISC | SPX_RECVPKT_IDISC) + +// Definition of the protocol reserved field of a receive packet. +typedef struct _SPX_RECV_RESD +{ + UCHAR rr_Id; // Set to SPX + USHORT rr_State; // State of receive packet + struct _SPX_RECV_RESD * rr_Next; // Points to next packet + ULONG rr_DataOffset; // To indicate/copy from + +#ifndef SPX_OWN_PACKETS + PVOID rr_FreePtr; // Ptr to use in free chunk +#endif + +#if DBG + USHORT rr_SeqNum; // Seq num of packet +#endif + + PREQUEST rr_Request; // request waiting on xfer + struct _SPX_CONN_FILE * rr_ConnFile; // that this recv is on + +} SPX_RECV_RESD, *PSPX_RECV_RESD; + + +// Destination built as an assign of 3 ulongs. +#define SpxBuildIpxHdr(pIpxSpxHdr, PktLen, pRemAddr, SrcSkt) \ + { \ + PBYTE pDestIpxAddr = (PBYTE)pIpxSpxHdr->hdr_DestNet; \ + (pIpxSpxHdr)->hdr_CheckSum = 0xFFFF; \ + PUTSHORT2SHORT((PUSHORT)(&(pIpxSpxHdr)->hdr_PktLen), (PktLen)); \ + (pIpxSpxHdr)->hdr_XportCtrl = 0; \ + (pIpxSpxHdr)->hdr_PktType = SPX_PKT_TYPE; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNet))= \ + *((UNALIGNED ULONG *)(SpxDevice->dev_Network)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNode)) = \ + *((UNALIGNED ULONG *)SpxDevice->dev_Node); \ + *((UNALIGNED USHORT *)((pIpxSpxHdr)->hdr_SrcNode+4)) = \ + *((UNALIGNED USHORT *)(SpxDevice->dev_Node+4)); \ + *((UNALIGNED USHORT *)&((pIpxSpxHdr)->hdr_SrcSkt)) = \ + SrcSkt; \ + } + +#define SpxCopyIpxAddr(pIpxSpxHdr, pDestIpxAddr) \ + { \ + PBYTE pRemAddr = (PBYTE)pIpxSpxHdr->hdr_SrcNet; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + } + +#ifndef SPX_OWN_PACKETS +#define SpxAllocSendPacket(_Device,_SendPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_SEND_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket), (_Handle)); \ + SEND_RESD(_SendPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_RECV_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_RecvPacket), (_Handle)); \ + RECV_RESD(_RecvPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxReInitSendPacket(_Packet) + { + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; + NdisReInitializePacket(_Packet); + SEND_RESD(_Packet)->sr_PoolHandle = (_Handle); + } + +#define SpxReInitRecvPacket(_Packet) + { + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; + NdisReInitializePacket(_Packet); + RECV_RESD(_Packet)->rr_PoolHandle = (_Handle); + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#else + +#define SpxAllocSendPacket(_Device, _SendPacket, _Status) \ + { \ + if (*(_SendPacket) = SpxBPAllocBlock(BLKID_NDISSEND)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + if (*(_RecvPacket) = SpxBPAllocBlock(BLKID_NDISRECV)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISSEND); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISRECV); \ + } + +#define SpxReInitSendPacket(_Packet) \ + { \ + } + +#define SpxReInitRecvPacket(_Packet) \ + { \ + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#endif + + +// +// Routine Prototypes +// + +VOID +SpxPktBuildCr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildCrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildSn( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSs( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSsAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSnAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildRr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State); + +VOID +SpxPktBuildRrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize); + +VOID +SpxPktBuildProbe( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildData( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length); + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ); + +VOID +SpxPktBuildAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend); + +VOID +SpxPktBuildDisc( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType); + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt); + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt); diff --git a/private/ntos/tdi/isnp/spx/h/spxquery.h b/private/ntos/tdi/isnp/spx/h/spxquery.h new file mode 100644 index 000000000..68e0a1ca8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxquery.h @@ -0,0 +1,54 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define SPX_TDI_PROVIDERINFO_VERSION 0x0001 +#define SPX_PINFOSENDSIZE 0xFFFFFFFF +#define SPX_PINFOMINMAXLOOKAHEAD 128 + +// +// Bug #14498: Indicate to AFD that we are capable of orderly disc so AFD can follow +// these semantics. +// In order to have SPXI connections work correctly, we OR this bit in at query time. +// (see SpxTdiQueryInformation) +// +#define SPX_PINFOSERVICEFLAGS ( TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_DELAYED_ACCEPTANCE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY) // | \ + // TDI_SERVICE_ORDERLY_RELEASE ) + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo); + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request); + diff --git a/private/ntos/tdi/isnp/spx/h/spxrecv.h b/private/ntos/tdi/isnp/spx/h/spxrecv.h new file mode 100644 index 000000000..0ce382990 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxrecv.h @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred); + +VOID +SpxReceiveComplete( + IN USHORT NicId); + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize); + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn); + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + diff --git a/private/ntos/tdi/isnp/spx/h/spxreg.h b/private/ntos/tdi/isnp/spx/h/spxreg.h new file mode 100644 index 000000000..4e0cb469b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxreg.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.h + +Abstract: + + Private include file for the ISN SPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#define HALFSEC_TO_MS_FACTOR 500 +#define IPX_REG_PATH L"NwlnkIpx\\Linkage" + +// These are used to index into the Parameters array in CONFIG. +#define CONFIG_CONNECTION_COUNT 0 +#define CONFIG_CONNECTION_TIMEOUT 1 +#define CONFIG_INIT_PACKETS 2 +#define CONFIG_MAX_PACKETS 3 +#define CONFIG_INITIAL_RETRANSMIT_TIMEOUT 4 +#define CONFIG_KEEPALIVE_COUNT 5 +#define CONFIG_KEEPALIVE_TIMEOUT 6 +#define CONFIG_WINDOW_SIZE 7 +#define CONFIG_SOCKET_RANGE_START 8 +#define CONFIG_SOCKET_RANGE_END 9 +#define CONFIG_SOCKET_UNIQUENESS 10 +#define CONFIG_MAX_PACKET_SIZE 11 +#define CONFIG_REXMIT_COUNT 12 + +// Hidden parameters +#define CONFIG_DISABLE_SPX2 13 +#define CONFIG_ROUTER_MTU 14 +#define CONFIG_BACKCOMP_SPX 15 + +#define CONFIG_PARAMETERS 16 + +// Main configuration structure. +typedef struct _CONFIG { + + ULONG cf_Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING cf_DeviceName; // device name exported + PWSTR cf_RegistryPathBuffer; // path to config info + +} CONFIG, * PCONFIG; + + +#define PARAM(x) (SpxDevice->dev_ConfigInfo->cf_Parameters[(x)]) + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr); + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config); + diff --git a/private/ntos/tdi/isnp/spx/h/spxsend.h b/private/ntos/tdi/isnp/spx/h/spxsend.h new file mode 100644 index 000000000..40ef810ea --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxsend.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +VOID +SpxSendComplete( + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus); + +VOID +SpxSendPktRelease( + IN PNDIS_PACKET pPkt, + IN UINT BufCount); diff --git a/private/ntos/tdi/isnp/spx/h/spxtimer.h b/private/ntos/tdi/isnp/spx/h/spxtimer.h new file mode 100644 index 000000000..225037bd2 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxtimer.h @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.h + +Abstract: + + This module contains routines to schedule timer events. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + 19 Jun 1992 Initial Version + +Notes: Tab stop: 4 +--*/ + +#define TIMER_DONT_REQUEUE 0 +#define TIMER_REQUEUE_CUR_VALUE 1 + +typedef ULONG (*TIMER_ROUTINE)(IN PVOID Context, IN BOOLEAN TimerShuttingDown); + +extern +NTSTATUS +SpxTimerInit( + VOID); + +extern +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG DeltaTime, // Schedule after this much time + IN PVOID pContext); // Context to pass to the routine + +extern +VOID +SpxTimerFlushAndStop( + VOID); + +extern +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue); + +#define TMR_SIGNATURE *(PULONG)"ATMR" +#if DBG +#define VALID_TMR(pTmr) (((pTmr) != NULL) && \ + ((pTmr)->tmr_Signature == TMR_SIGNATURE)) +#else +#define VALID_TMR(pTmr) ((pTmr) != NULL) +#endif +typedef struct _TimerList +{ +#if DBG + ULONG tmr_Signature; +#endif + struct _TimerList * tmr_Next; // Link to next + struct _TimerList ** tmr_Prev; // Link to prev + struct _TimerList * tmr_Overflow; // Link to overflow entry in hash table + ULONG tmr_AbsTime; // Absolute time, for re-enqueue + ULONG tmr_RelDelta; // Relative to the previous entry + ULONG tmr_Id; // Unique Id for this event + BOOLEAN tmr_Cancelled; // Was the timer cancelled? + TIMER_ROUTINE tmr_Worker; // Real Worker + PVOID tmr_Context; // Real context +} TIMERLIST, *PTIMERLIST; + + +#define SpxGetCurrentTime() (SpxTimerCurrentTime/SPX_TIMER_FACTOR) +#define SpxGetCurrentTick() SpxTimerCurrentTime + +// Keep this at a ONE second level. +#define SPX_TIMER_FACTOR 10 // i.e. 10 ticks per second +#define SPX_MS_TO_TICKS 100 // Divide ms by this to get ticks +#define SPX_TIMER_TICK -1000000L // 100ms in 100ns units +#define SPX_TIMER_WAIT 50 // Time to wait in FlushAndStop in ms +#define TIMER_HASH_TABLE 32 + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +spxTimerWorker( + IN PTIMERLIST pList); + +VOID +spxTimerEnqueue( + PTIMERLIST pListNew); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxutils.h b/private/ntos/tdi/isnp/spx/h/spxutils.h new file mode 100644 index 000000000..074a1649b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxutils.h @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// For PROTO_SPX, i'd return a device name from the dll of the form +// \Device\NwlnkSpx\SpxStream (for SOCK_STREAM) or +// \Device\NwlnkSpx\Spx (for SOCK_SEQPKT) +// +// and for PROTO_SPXII (the more common case we hope, even if +// internally we degrade to SPX1 cause of the remote client's +// limitations) +// \Device\NwlnkSpx\Stream (for SOCK_STREAM) or +// \Device\NwlnkSpx (for SOCK_SEQPKT) + +#define SOCKET1STREAM_SUFFIX L"\\SpxStream" +#define SOCKET1_SUFFIX L"\\Spx" +#define SOCKET2STREAM_SUFFIX L"\\Stream" +#define SOCKET1_TYPE_SEQPKT 0 +#define SOCKET2_TYPE_SEQPKT 1 +#define SOCKET1_TYPE_STREAM 2 +#define SOCKET2_TYPE_STREAM 3 + +#define IN_RANGE(_S, _RangeStart, _RangeEnd) \ + ((_S >= _RangeStart) && (_S <= _RangeEnd)) + + +// +// The following macros deal with on-the-wire integer and long values +// +// On the wire format is big-endian i.e. a long value of 0x01020304 is +// represented as 01 02 03 04. Similarly an int value of 0x0102 is +// represented as 01 02. +// +// The host format is not assumed since it will vary from processor to +// processor. +// + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = (USHORT) (*(PBYTE)(SrcPtr)) + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = (ULONG) (*(PBYTE)(SrcPtr)) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a dword from on-the-wire format to a dword in the host format +#define GETULONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 24) + \ + (*((PBYTE)(SrcPtr)+1) << 16) + \ + (*((PBYTE)(SrcPtr)+2) << 8) + \ + (*((PBYTE)(SrcPtr)+3) )) + +// Get a dword from on-the-wire format to a dword in the same format but +// also watch out for alignment +#define GETULONG2ULONG_NOCONV(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0); \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1); \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2); \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3); + +// Put a dword from the host format to a short to on-the-wire format +#define PUTBYTE2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = ((USHORT)(Src) % 256) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((USHORT)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE)(Src) + +// Put a dword from the host format to a byte to on-the-wire format +#define PUTULONG2BYTE(DstPtr, Src) \ + *(PBYTE)(DstPtr) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTULONG2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE) (Src) + +// Put a dword from the host format to a dword to on-the-wire format +#define PUTULONG2ULONG(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 24), \ + *((PBYTE)(DstPtr)+1) = (BYTE) ((ULONG)(Src) >> 16), \ + *((PBYTE)(DstPtr)+2) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+3) = (BYTE) (Src) + +// Put a BYTE[4] array into another BYTE4 array. +#define PUTBYTE42BYTE4(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0), \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1), \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2), \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3) + +// MIN/MAX macros +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + + + +// Exported prototypes + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr); + +LONG +SpxRandomNumber( + VOID); + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType); + +VOID +SpxSleep( + IN ULONG TimeInMs); + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket); + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr); + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress); + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength); + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1); diff --git a/private/ntos/tdi/isnp/spx/mp/makefile b/private/ntos/tdi/isnp/spx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/mp/sources b/private/ntos/tdi/isnp/spx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/spx/nwlnkspx.rc b/private/ntos/tdi/isnp/spx/nwlnkspx.rc new file mode 100644 index 000000000..02175f21d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/nwlnkspx.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 SPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkspx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkspx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/spx/precomp.h b/private/ntos/tdi/isnp/spx/precomp.h new file mode 100644 index 000000000..d227d02f9 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/precomp.h @@ -0,0 +1 @@ +#include "isnspx.h" diff --git a/private/ntos/tdi/isnp/spx/sources.inc b/private/ntos/tdi/isnp/spx/sources.inc new file mode 100644 index 000000000..419f580f1 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/sources.inc @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkspx + +TARGETNAME=nwlnkspx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\h;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\spxdrvr.c \ + ..\spxreg.c \ + ..\spxdev.c \ + ..\spxbind.c \ + ..\spxaddr.c \ + ..\spxconn.c \ + ..\spxcutil.c \ + ..\spxcpkt.c \ + ..\spxrecv.c \ + ..\spxsend.c \ + ..\spxquery.c \ + ..\spxutils.c \ + ..\spxmem.c \ + ..\spxtimer.c \ + ..\spxpkt.c \ + ..\globals.c \ + ..\spxerror.c \ + ..\nwlnkspx.rc + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/isnp/spx/spxaddr.c b/private/ntos/tdi/isnp/spx/spxaddr.c new file mode 100644 index 000000000..e9e85dc5c --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxaddr.c @@ -0,0 +1,1729 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, SpxAddrFileCreate) +#pragma alloc_text( PAGE, SpxAddrFileClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXADDR + +// Map all generic accesses to the same one. +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +#define REORDER(_Socket) ((((_Socket) & 0xff00) >> 8) | (((_Socket) & 0x00ff) << 8)) + + + + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the ST transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR pAddr; + PSPX_ADDR_FILE pAddrFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED * AddressName; + USHORT Socket, hostSocket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle, LockHandleAddr; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; + +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // The network name is in the EA, passed in the request. + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no EA\n", Request)); + + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // this may be a valid name; parse the name from the EA and use it if OK. + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + for (i=0;iTAAddressCount;i++) + { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + Socket = + ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + + GETSHORT2SHORT(&hostSocket, &Socket); + + DBGPRINT(CREATE, DBG, + ("SpxAddrOpen: Creating socket %lx.h%lx\n", + Socket, hostSocket )); + + found = TRUE; + } + break; + + } + else + { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + } + } + + if (!found) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no IPX Address\n", Request)); + + return STATUS_INVALID_ADDRESS_COMPONENT; + } + +#ifdef SOCKET_RANGE_OPEN_LIMITATION_REMOVED + // Is the socket in our range if its in the range 0x4000-0x7FFF + if (IN_RANGE(hostSocket, DYNSKT_RANGE_START, DYNSKT_RANGE_END)) + { + if (!IN_RANGE( + hostSocket, + PARAM(CONFIG_SOCKET_RANGE_START), + PARAM(CONFIG_SOCKET_RANGE_END))) + { + return(STATUS_INVALID_ADDRESS); + } + } +#endif + + // get an address file structure to represent this address. + status = SpxAddrFileCreate(Device, Request, &pAddrFile); + if (!NT_SUCCESS(status)) + return status; + + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + CTEGetLock (&Device->dev_Lock, &LockHandle); + + // We checkfor/create sockets within the critical section. + if (Socket == 0) + { + Socket = SpxAddrAssignSocket(Device); + + if (Socket == 0) + { + DBGPRINT(ADDRESS, ERR, + ("OpenAddress, no unique socket found\n")); + + CTEFreeLock (&Device->dev_Lock, LockHandle); + ExReleaseResource (&Device->dev_AddrResource); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + } + + pAddr = SpxAddrLookup(Device, Socket); + + if (pAddr == NULL) + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // This address doesn't exist. Create it. + // registering it. It also puts a ref of type ADDR_FILE on address. + pAddr = SpxAddrCreate( + Device, + Socket); + + if (pAddr != (PSPX_ADDR)NULL) + { +#ifdef ISN_NT + + // Initialize the shared access now. We use read access + // to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess); + + + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &pAddr->sa_SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + // Error, return status. + IoRemoveShareAccess (IrpSp->FileObject, &pAddr->u.sa_ShareAccess); + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + return status; + } + +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + // if the adapter isn't ready, we can't do any of this; get out +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) +#else + if (Device->dev_State == DEVICE_STATE_STOPPING) +#endif _PNP_POWER + { + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + status = STATUS_DEVICE_NOT_READY; + } + else + { + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; + + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + status = STATUS_SUCCESS; + } + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + + SpxAddrFileDestroy(pAddrFile); + } + } + else + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + DBGPRINT(ADDRESS, ERR, + ("Add to address %lx\n", pAddr)); + + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + pAddr->sa_SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { +#ifdef ISN_NT + + // Now check that we can obtain the desired share + // access. We use read access to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + SpxAddrLockReference (pAddr, AREF_ADDR_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&Device->dev_Lock, LockHandle); + + status = STATUS_SUCCESS; + } + } + + // Remove the reference from SpxLookupAddress. + SpxAddrDereference (pAddr, AREF_LOOKUP); + } + + return status; + +} // SpxAddrOpen + + + + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest + ) +{ + CTELockHandle lockHandle; + NTSTATUS status = STATUS_SUCCESS; + + PSPX_ADDR_FILE + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + PTDI_REQUEST_KERNEL_SET_EVENT + pParam = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(pRequest); + + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(status); + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + switch (pParam->EventType) + { + + case TDI_EVENT_ERROR: + + break; + + case TDI_EVENT_CONNECT: + + pSpxAddrFile->saf_ConnHandler = + (PTDI_IND_CONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_ConnHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE: + + pSpxAddrFile->saf_RecvHandler = + (PTDI_IND_RECEIVE)(pParam->EventHandler); + pSpxAddrFile->saf_RecvHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_DISCONNECT: + + pSpxAddrFile->saf_DiscHandler = + (PTDI_IND_DISCONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_DiscHandlerCtx = + pParam->EventContext; + + break; + + + case TDI_EVENT_SEND_POSSIBLE : + + pSpxAddrFile->saf_SendPossibleHandler = + (PTDI_IND_SEND_POSSIBLE)(pParam->EventHandler); + pSpxAddrFile->saf_SendPossibleHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + case TDI_EVENT_RECEIVE_EXPEDITED: + default: + + status = STATUS_INVALID_PARAMETER; + } + + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return(status); +} + + + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PSPX_ADDR pAddr; + int index; + CTELockHandle lockHandle; + + pAddr = (PSPX_ADDR)SpxAllocateZeroedMemory (sizeof(SPX_ADDR)); + if (pAddr == NULL) + { + DBGPRINT(ADDRESS, INFO, + ("Create address %lx failed\n", (ULONG)Socket)); + + return NULL; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address %lx (%lx)\n", pAddr, (ULONG)Socket)); + + pAddr->sa_Type = SPX_ADDRESS_SIGNATURE; + pAddr->sa_Size = sizeof (SPX_ADDR); + pAddr->sa_Flags = 0; + + pAddr->sa_Device = Device; + pAddr->sa_DeviceLock = &Device->dev_Lock; + CTEInitLock (&pAddr->sa_Lock); + + // This reference is for the address file that will associated with this addr. + pAddr->sa_RefCount = 1; + +#if DBG + pAddr->sa_RefTypes[AREF_ADDR_FILE] = 1; +#endif + + pAddr->sa_Socket = Socket; + + // Insert address into the device hash table. + index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + CTEGetLock (&Device->dev_Lock, &lockHandle); + pAddr->sa_Next = Device->dev_AddrHashTable[index]; + Device->dev_AddrHashTable[index] = pAddr; + CTEFreeLock (&Device->dev_Lock, lockHandle); + + SpxReferenceDevice (Device, DREF_ADDRESS); + + return pAddr; + +} // SpxAddrCreate + + + + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a SPX_ADDR_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PSPX_ADDR Address; + + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + + try + { + if ((pAddrFile->saf_Size == sizeof (SPX_ADDR_FILE)) && + (pAddrFile->saf_Type == SPX_ADDRESSFILE_SIGNATURE) ) + { + Address = pAddrFile->saf_Addr; + + if ((Address->sa_Size == sizeof (SPX_ADDR)) && + (Address->sa_Type == SPX_ADDRESS_SIGNATURE) ) + { + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if ((Address->sa_Flags & SPX_ADDR_CLOSING) == 0) + { + SpxAddrFileLockReference(pAddrFile, AFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx closing\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx bad signature\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pAddrFile)); + + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxAddrFileVerify: AF %lx exception\n", Address)); + + return GetExceptionCode(); + } + + return status; + +} // SpxAddrFileVerify + + + + +VOID +SpxAddrDestroy( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by SpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by SpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSPX_ADDR pAddr, *ppAddr; + CTELockHandle LockHandle; + + PSPX_ADDR Address = (PSPX_ADDR)Parameter; + PDEVICE Device = Address->sa_Device; + int index = (int)(Address->sa_Socket & NUM_SPXADDR_HASH_MASK); + + DBGPRINT(ADDRESS, INFO, + ("Destroy address %lx\n", Address)); + + SeDeassignSecurity (&Address->sa_SecurityDescriptor); + + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + CTEGetLock (&Device->dev_Lock, &LockHandle); + for (ppAddr = &Device->dev_AddrHashTable[index]; (pAddr = *ppAddr) != NULL;) + { + if (pAddr == Address) + { + *ppAddr = pAddr->sa_Next; + break; + } + + ppAddr = &pAddr->sa_Next; + } + CTEFreeLock (&Device->dev_Lock, LockHandle); + + SpxFreeMemory (Address); + SpxDereferenceDevice (Device, DREF_ADDRESS); + +} + + + + +#if DBG + +VOID +SpxAddrRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} + + + + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} +#endif + + + + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &Address->sa_RefCount, + (ULONG)-1, + Address->sa_DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) + { +#if ISN_NT + ExInitializeWorkItem( + &Address->u.sa_DestroyAddrQueueItem, + SpxAddrDestroy, + (PVOID)Address); + ExQueueWorkItem(&Address->u.sa_DestroyAddrQueueItem, DelayedWorkQueue); +#else + SpxAddrDestroy(Address); +#endif + + } + +} + + + + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + NTSTATUS status; + BYTE socketType; + CTELockHandle LockHandle; + PSPX_ADDR_FILE pAddrFile; + + // What is the address file type? + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) + { + return(status); + } + + pAddrFile = (PSPX_ADDR_FILE)SpxAllocateZeroedMemory (sizeof(SPX_ADDR_FILE)); + if (pAddrFile == NULL) + { + DBGPRINT(ADDRESS, ERR, + ("Create address file failed\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address file %lx\n", pAddrFile)); + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + pAddrFile->saf_Type = SPX_ADDRESSFILE_SIGNATURE; + pAddrFile->saf_Size = sizeof (SPX_ADDR_FILE); + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject = NULL; +#endif + + pAddrFile->saf_Device = Device; + pAddrFile->saf_Flags = SPX_ADDRFILE_OPENING; + if ((socketType == SOCKET1_TYPE_SEQPKT) || + (socketType == SOCKET1_TYPE_STREAM)) + { + if (socketType == SOCKET1_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_SPX2; + if (socketType == SOCKET2_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + pAddrFile->saf_RefCount = 1; + +#if DBG + pAddrFile->saf_RefTypes[AFREF_CREATE] = 1; +#endif + + pAddrFile->saf_CloseReq = (PREQUEST)NULL; + + // Initialize the request handlers. + pAddrFile->saf_ConnHandler = + pAddrFile->saf_ConnHandlerCtx = NULL; + pAddrFile->saf_DiscHandler = + pAddrFile->saf_DiscHandlerCtx = NULL; + pAddrFile->saf_RecvHandler = + pAddrFile->saf_RecvHandlerCtx = NULL; + pAddrFile->saf_ErrHandler = + pAddrFile->saf_ErrHandlerCtx = NULL; + + // Release lock + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // Put in the global list for our reference + spxAddrInsertIntoGlobalList(pAddrFile); + + *ppAddrFile = pAddrFile; + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by SpxAddrFileDereference. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + pAddrFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PSPX_ADDR Address; + PDEVICE Device; + PREQUEST CloseRequest; + PSPX_ADDR_FILE pRemAddr, *ppRemAddr; + + DBGPRINT(ADDRESS, INFO, + ("Destroy address file %lx\n", pAddrFile)); + + Address = pAddrFile->saf_Addr; + Device = pAddrFile->saf_Device; + + if (Address) + { + CTEGetLock (&Device->dev_Lock, &LockHandle1); + + // This addressfile was associated with an address. + CTEGetLock (&Address->sa_Lock, &LockHandle); + + // If the last reference on the address is being removed, set the + // closing flag to prevent further references. + + //if (Address->sa_RefCount == 1) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&Address->sa_RefCount, 0, &Address->sa_Lock) == 1) { + Address->sa_Flags |= SPX_ADDR_CLOSING; + } + + // Dequeue the address file from the address list. + for (ppRemAddr = &Address->sa_AddrFileList; (pRemAddr = *ppRemAddr) != NULL;) + { + if (pRemAddr == pAddrFile) + { + *ppRemAddr = pRemAddr->saf_Next; + break; + } + + ppRemAddr = &pRemAddr->saf_Next; + } + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject->FsContext = NULL; + pAddrFile->saf_FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->sa_Lock, LockHandle); + CTEFreeLock (&Device->dev_Lock, LockHandle1); + + // We will already have been removed from the ShareAccess + // of the owning address. + // + // Now dereference the owning address. + SpxAddrDereference(Address, AREF_ADDR_FILE); + } + + // Save this for later completion. + CloseRequest = pAddrFile->saf_CloseReq; + + // Remove from the global list + spxAddrRemoveFromGlobalList(pAddrFile); + + // return the addressFile to the pool of address files + SpxFreeMemory (pAddrFile); + + if (CloseRequest != (PREQUEST)NULL) + { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + SpxCompleteRequest (CloseRequest); + SpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} + + + + +#if DBG + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} // SpxRefAddressFile + + + + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} +#endif + + + + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddressFile to remove it from the system. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + (ULONG)-1, + pAddrFile->saf_AddrLock); + + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) + { + SpxAddrFileDestroy(pAddrFile); + } + +} + + + + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + SpxAddrLockReference(Address, AREF_LOOKUP); + return Address; + + } + } + + // The specified address was not found. + return NULL; + +} + + + + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + TRUE if so, else FALSE + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match + return TRUE; + } + } + + // The specified address was not found. + return FALSE; + +} // SpxAddrExists + + + + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT PSPX_CONN_FILE *ppSpxConnFile + ) +{ + PSPX_CONN_FILE pSpxConnFile; + NTSTATUS status = STATUS_INVALID_CONNECTION; + + for (pSpxConnFile = pSpxAddr->sa_ActiveConnList; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_Next) + { + if ((pSpxConnFile->scf_RemConnId == SrcConnId) && + (*((UNALIGNED ULONG *)SrcIpxAddr) == + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+4) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+8) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+8))) + { + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + *ppSpxConnFile = pSpxConnFile; + status = STATUS_SUCCESS; + break; + } + } + + return(status); +} + + + + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an pAddrFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + pAddrFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile, pSpxConnFileNext; + CTELockHandle LockHandle; + + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileStop: %lx\n", pAddrFile)); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if (pAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING) + { + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + } + + pAddrFile->saf_Flags |= SPX_ADDRFILE_CLOSING; + + pSpxConnFileNext = NULL; + if (pSpxConnFile = pAddrFile->saf_AssocConnList) + { + pSpxConnFileNext = pSpxConnFile; + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + } + + while (pSpxConnFile) + { + if (pSpxConnFileNext = pSpxConnFile->scf_AssocNext) + { + SpxConnFileReference(pSpxConnFileNext, CFREF_ADDR); + } + CTEFreeLock (&Address->sa_Lock, LockHandle); + + + DBGPRINT(CREATE, INFO, + ("SpxAddrFileClose: Assoc conn stop %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + SpxConnStop(pSpxConnFile); + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + pSpxConnFile = pSpxConnFileNext; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, INFO, + ("SpxAddrFileCleanup: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + if (!NT_SUCCESS (status)) + { + return(status); + } + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + SpxAddrFileStop(pSpxAddrFile, Address); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_SUCCESS; +} + + + + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileClose: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + + if (!NT_SUCCESS (status)) + { + return(status); + } + + pSpxAddrFile->saf_CloseReq = Request; + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + // Remove us from the access info for this address. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + +#ifdef ISN_NT + IoRemoveShareAccess (pSpxAddrFile->saf_FileObject, &Address->u.sa_ShareAccess); +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + + SpxAddrFileDereference (pSpxAddrFile, AFREF_CREATE); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_PENDING; + +} // SpxCloseAddressFile + + + + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + BOOLEAN wrapped = FALSE; + USHORT temp, Socket; + + // We have to auto-assign a socket. + temp = Device->dev_CurrentSocket; + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + while (TRUE) + { + Device->dev_CurrentSocket += (USHORT)PARAM(CONFIG_SOCKET_UNIQUENESS); + if (Device->dev_CurrentSocket > PARAM(CONFIG_SOCKET_RANGE_END)) + { + Device->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + wrapped = TRUE; + } + + if (!SpxAddrExists (Device, Socket)) + { + break; + } + + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + if (wrapped && (Device->dev_CurrentSocket >= temp)) + { + // If we have checked all possible values given SOCKET_UNIQUENESS... + // This may actually return ERROR even if there are + // available socket numbers although they may be + // implicitly in use due to SOCKET_UNIQUENESS being + // > 1. That is the way it is to work. + + Socket = 0; + break; + } + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + + return(Socket); +} + + + + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxAddrFile->saf_GlobalNext = SpxGlobalAddrList; + SpxGlobalAddrList = pSpxAddrFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_ADDR_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalAddrList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxAddrFile) + { + DBGPRINT(SEND, INFO, + ("SpxAddrRemoveFromGlobal: %lx\n", pSpxAddrFile)); + + // Remove from list + *ppC = pC->saf_GlobalNext; + break; + } + + ppC = &pC->saf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_ADDRESS; + + return(status); +} + + + + diff --git a/private/ntos/tdi/isnp/spx/spxbind.c b/private/ntos/tdi/isnp/spx/spxbind.c new file mode 100644 index 000000000..7610939f7 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxbind.c @@ -0,0 +1,600 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.c + +Abstract: + + This module contains the code to bind to the IPX transport, as well as the + indication routines for the IPX transport not including the send/recv ones. + +Author: + + Stefan Solomon (stefans) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXBIND + +VOID +SpxStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry); + +VOID +SpxLineDown ( + IN USHORT NicId); + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// globals and externs +// +extern CTELock spxTimerLock; +extern LARGE_INTEGER spxTimerTick; +extern KTIMER spxTimer; +extern KDPC spxTimerDpc; +extern BOOLEAN spxTimerStopped; +#endif _PNP_POWER + +NTSTATUS +SpxInitBindToIpx( + VOID + ) + +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objectAttr; + PIPX_INTERNAL_BIND_INPUT pBindInput; + PIPX_INTERNAL_BIND_OUTPUT pBindOutput; + + InitializeObjectAttributes( + &objectAttr, + &IpxDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = NtCreateFile( + &IpxHandle, + SYNCHRONIZE | GENERIC_READ, + &objectAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(status)) { + return status; + } + + if ((pBindInput = CTEAllocMem(sizeof(IPX_INTERNAL_BIND_INPUT))) == NULL) { + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Fill in our bind data +#if defined(_PNP_POWER) + pBindInput->Version = ISN_VERSION; +#else + pBindInput->Version = 1; +#endif _PNP_POWER + pBindInput->Identifier = IDENTIFIER_SPX; + pBindInput->BroadcastEnable = FALSE; + pBindInput->LookaheadRequired = IPX_HDRSIZE; + pBindInput->ProtocolOptions = 0; + pBindInput->ReceiveHandler = SpxReceive; + pBindInput->ReceiveCompleteHandler = SpxReceiveComplete; + pBindInput->StatusHandler = SpxStatus; + pBindInput->SendCompleteHandler = SpxSendComplete; + pBindInput->TransferDataCompleteHandler = SpxTransferDataComplete; + pBindInput->FindRouteCompleteHandler = SpxFindRouteComplete; + pBindInput->LineUpHandler = SpxLineUp; + pBindInput->LineDownHandler = SpxLineDown; + pBindInput->ScheduleRouteHandler = SpxScheduleRoute; +#if defined(_PNP_POWER) + pBindInput->PnPHandler = SpxPnPNotification; +#endif _PNP_POWER + + + // First get the length for the output buffer. + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + NULL, // Output Buffer + 0); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status != STATUS_BUFFER_TOO_SMALL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INVALID_PARAMETER); + } + + if ((pBindOutput = CTEAllocMem(ioStatusBlock.Information)) == NULL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + pBindOutput, // Output Buffer + ioStatusBlock.Information); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status == STATUS_SUCCESS) { + + // Get all the info from the bind output buffer and save in + // appropriate places. + IpxLineInfo = pBindOutput->LineInfo; + IpxMacHdrNeeded = pBindOutput->MacHeaderNeeded; + IpxInclHdrOffset = pBindOutput->IncludedHeaderOffset; + + IpxSendPacket = pBindOutput->SendHandler; + IpxFindRoute = pBindOutput->FindRouteHandler; + IpxQuery = pBindOutput->QueryHandler; + IpxTransferData = pBindOutput->TransferDataHandler; + +#if !defined(_PNP_POWER) + // Copy over the network node info. + RtlCopyMemory( + SpxDevice->dev_Network, + pBindOutput->Network, + IPX_NET_LEN); + + RtlCopyMemory( + SpxDevice->dev_Node, + pBindOutput->Node, + IPX_NODE_LEN); + + + DBGPRINT(TDI, INFO, + ("SpxInitBindToIpx: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // Find out how many adapters IPX has, if this fails + // just assume one. + // + + if ((*IpxQuery)( + IPX_QUERY_MAXIMUM_NIC_ID, + 0, + &SpxDevice->dev_Adapters, + sizeof(USHORT), + NULL) != STATUS_SUCCESS) { + + SpxDevice->dev_Adapters = 1; + + } +#endif !_PNP_POWER + } else { + + NtClose(IpxHandle); + status = STATUS_INVALID_PARAMETER; + } + CTEFreeMem(pBindInput); + CTEFreeMem(pBindOutput); + + return status; +} + + + + +VOID +SpxUnbindFromIpx( + VOID + ) + +{ + NtClose(IpxHandle); + return; +} + + + + +VOID +SpxStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxStatus: CALLED WITH %lx\n", + GeneralStatus)); + + return; +} + + + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +{ + CTELockHandle lockHandle; + PSPX_FIND_ROUTE_REQUEST pSpxFrReq = (PSPX_FIND_ROUTE_REQUEST)FindRouteRequest; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)pSpxFrReq->fr_Ctx; + + // This will be on a connection. Grab the lock, check the state and go from + // there. + if (pSpxConnFile == NULL) + { + // Should this ever happen? + KeBugCheck(0); + return; + } + + // Check the state. The called routines release the lock, remove the reference. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + // We are doing an active connect! + SpxConnConnectFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + else // For all others call active + { + SpxConnActiveFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + + // Free the find route request. + SpxFreeMemory(pSpxFrReq); + + return; +} + + + + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + +{ + // With PnP, our local address is changed when we get PnP + // notification. +#if !defined(_PNP_POWER) + + // + // If we get a line up for NicId 0, it means our local + // network number has changed, re-query from IPX. + // + + if (NicId == 0) { + + TDI_ADDRESS_IPX IpxAddress; + + if ((*IpxQuery)( + IPX_QUERY_IPX_ADDRESS, + 0, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + RtlCopyMemory( + SpxDevice->dev_Network, + &IpxAddress.NetworkAddress, + IPX_NET_LEN); + + DBGPRINT(TDI, INFO, + ("SpxLineUp: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // The node shouldn't change! + // + + if (!RtlEqualMemory( + SpxDevice->dev_Node, + IpxAddress.NodeAddress, + IPX_NODE_LEN)) { + + DBGPRINT(TDI, ERR, + ("SpxLineUp: Node address has changed\n")); + } + } + + } else { + + DBGPRINT(RECEIVE, ERR, + ("SpxLineUp: CALLED WITH %lx\n", + NicId)); + } + + return; +#endif !_PNP_POWER + +} + + + + +VOID +SpxLineDown ( + IN USHORT NicId + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxLineDown: CALLED WITH %lx\n", + NicId)); + + return; +} + + + + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxScheduleRoute: CALLED WITH %lx\n", + RouteEntry)); + + return; +} + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + NTSTATUS Status; + PDEVICE Device = SpxDevice; + UNICODE_STRING UnicodeDeviceName; + + DBGPRINT(DEVICE, DBG,("Received a pnp notification, opcode %d\n",OpCode)); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + CTELockHandle TimerLockHandle; + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->dev_State != DEVICE_STATE_OPEN ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + // + // Start the timer. It is possible that the timer + // was still running or we are still in the timer dpc + // from the previous ADD_DEVICE - DELETE_DEVICE execution + // cycle. But it is ok simply restart this, because + // KeSetTimer implicitly cancels the previous Dpc. + // + + CTEGetLock(&spxTimerLock, &TimerLockHandle); + spxTimerStopped = FALSE; + CTEFreeLock(&spxTimerLock, TimerLockHandle); + KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + + Device->dev_State = DEVICE_STATE_OPEN; + + + CTEAssert( !Device->dev_Adapters ); + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + IpxLineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumPacketSize; + // set the provider info + SpxDevice->dev_ProviderInfo.MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + }else { + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + } + + Device->dev_Adapters++; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->dev_DeviceName; + UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen; + UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to register Spx Device with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + CTEAssert( Device->dev_Adapters ); + Device->dev_Adapters--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->dev_State = DEVICE_STATE_LOADED; + Device->dev_Adapters = 0; + } + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + if ( PnPInfo->FirstORLastDevice ) { + SpxTimerFlushAndStop(); + // + // inform tdi clients about the device deletion + // + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to Deregister Spx Device with TDI\n")); + } + } + // + // TBD: call ExNotifyCallback + // + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEAssert( PnPInfo->NewReservedAddress ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/spx/spxconn.c b/private/ntos/tdi/isnp/spx/spxconn.c new file mode 100644 index 000000000..4528b64a4 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxconn.c @@ -0,0 +1,3862 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, SpxConnOpen) +#pragma alloc_text(PAGE, SpxConnCleanup) +#pragma alloc_text(PAGE, SpxConnClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXCONN + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT ConnCtx, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine is used to create a connection object and associate the + passed ConnectionContext with it. + +Arguments: + + pConnCtx - The TDI ConnectionContext to be associated with object + +Return Value: + + STATUS_SUCCESS if connection was successfully opened + Error otherwise. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PSPX_CONN_FILE pSpxConnFile; + +#ifdef ISN_NT + PIRP Irp = (PIRP)pRequest; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // Allocate memory for a connection object + if ((pSpxConnFile = SpxAllocateZeroedMemory(sizeof(SPX_CONN_FILE))) == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Initialize values + pSpxConnFile->scf_Flags = 0; + pSpxConnFile->scf_Type = SPX_CONNFILE_SIGNATURE; + pSpxConnFile->scf_Size = sizeof (SPX_CONN_FILE); + + CTEInitLock (&pSpxConnFile->scf_Lock); + + pSpxConnFile->scf_ConnCtx = ConnCtx; + pSpxConnFile->scf_Device = pDevice; + + // Initialize list for requests. + InitializeListHead(&pSpxConnFile->scf_ReqLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_ReqDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_DiscLinkage); + +#ifdef ISN_NT + // easy backlink to file object. + pSpxConnFile->scf_FileObject = IrpSp->FileObject; +#endif + + // For connections we go from 0->0 with flags indicating if a close + // happened. + pSpxConnFile->scf_RefCount = 0; + + // Insert into a global connection list. + spxConnInsertIntoGlobalList(pSpxConnFile); + +#if DBG + + // Initialize this to 0xFFFF so we dont hit assert on first packet. + pSpxConnFile->scf_PktSeqNum = 0xFFFF; + +#endif + + // Set values in the request. + REQUEST_OPEN_CONTEXT(pRequest) = (PVOID)pSpxConnFile; + REQUEST_OPEN_TYPE(pRequest) = (PVOID)TDI_CONNECTION_FILE; + + DBGPRINT(CREATE, INFO, + ("SpxConnOpen: Opened %lx\n", pSpxConnFile)); + + ASSERT(status == STATUS_SUCCESS); + return(status); +} + + + + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileCleanup: %lx.%lx when %lx\n", + pSpxConnFile, Request, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CleanupReq = Request; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // We have a reference, so it wont go to zero until stop returns. Therefore + // deref can expect flag to be set. + SpxConnStop(pSpxConnFile); + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + + // + // If this is a connection which is waiting for a local disconnect, + // deref it since we dont expect a disconnect after a cleanup. + // + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + KdPrint(("Deref for DISCWAIT on connfile: %lx\n", pSpxConnFile)); + + SpxConnFileDereference (pSpxConnFile, CFREF_DISCWAITSPX); + } else { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + + return STATUS_PENDING; +} + + + + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileClose: %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CloseReq = Request; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + return STATUS_PENDING; +} + + + + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!!Connection must have a reference when this is called!!! + +Arguments: + + +Return Value: + + +--*/ +{ + CTELockHandle lockHandle; + + DBGPRINT(CREATE, INFO, + ("SpxConnFileStop: %lx when %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags)); + + // Call disconnect and disassociate + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + } + else + { + // Disassociate if we are associated. + spxConnDisAssoc(pSpxConnFile, lockHandle); + } + + // Lock released at this point. + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + return; +} + + + + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine moves the connection from the device list to the inactive + connection list in the address of the address file specified. The address + file is pointed to by the connection and is referenced for the associate. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE pSpxAddrFile; + CTELockHandle lockHandle1, lockHandle2; + + BOOLEAN derefAddr = FALSE, derefConn = FALSE; + PFILE_OBJECT pFileObj = NULL; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + HANDLE AddrObjHandle = + ((PTDI_REQUEST_KERNEL_ASSOCIATE)(REQUEST_PARAMETERS(pRequest)))->AddressHandle; + + do + { + // Get the handle to the address object from the irp and map it to + // the corres. file object. + status = ObReferenceObjectByHandle( + AddrObjHandle, + 0, + 0, + KernelMode, + (PVOID *)&pFileObj, + NULL); + + if (!NT_SUCCESS(status)) + break; + + pSpxAddrFile = pFileObj->FsContext; + ASSERT(pFileObj->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + // Verify address file/connection file + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + break; + + derefAddr = TRUE; + + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + break; + + derefConn = TRUE; + + // Grab the addres file lock, then the connection lock for associate. + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + if (!SPX_CONN_FLAG(pSpxConnFile, (SPX_CONNFILE_CLOSING | + SPX_CONNFILE_STOPPING | + SPX_CONNFILE_ASSOC)) + && + !(pSpxAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING)) + { + derefAddr = FALSE; + SpxAddrFileTransferReference( + pSpxAddrFile, AFREF_VERIFY, AFREF_CONN_ASSOC); + + // Queue in the inactive list in the address + pSpxConnFile->scf_Next = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxAddrFile->saf_Addr->sa_InactiveConnList = pSpxConnFile; + + // Queue in the assoc list in the address file + pSpxConnFile->scf_AssocNext = pSpxAddrFile->saf_AssocConnList; + pSpxAddrFile->saf_AssocConnList = pSpxConnFile; + + // Remember the addrfile in the connection + pSpxConnFile->scf_AddrFile = pSpxAddrFile; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + status = STATUS_SUCCESS; + + DBGPRINT(CREATE, INFO, + ("SpxConnAssociate: %lx with address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandle1); + + // Dereference the file object corres. to the address object + ObDereferenceObject(pFileObj); + + } while (FALSE); + + if (derefAddr) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + if (derefConn) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return (status); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile) + || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + SpxConnStop(pSpxConnFile); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + CTELockHandle lockHandleAddr; + PSPX_ADDR_FILE pSpxAddrFile; + + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Check again as we had released the lock + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxConnFile->scf_AddrFile = NULL; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + + DBGPRINT(CREATE, INFO, + ("SpxConnDisAssociate: %lx from address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + if (NT_SUCCESS(status)) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + } + + return(status); +} + + + + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + BUGBUG: + We need to have another timer that will be started on the connection + if the tdi client indicated a timeout value. 0 -> we do not start such + a timer, -1 implies, we let our connection timeout values do their thing. + Any other value will forcibly shutdown the connect process, when the timer + fires. + +Return Value: + + +--*/ + +{ + PTDI_REQUEST_KERNEL_CONNECT pParam; + TDI_ADDRESS_IPX UNALIGNED * pTdiAddr; + PNDIS_PACKET pCrPkt; + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_ADDR pSpxAddr; + BOOLEAN locksHeld = TRUE; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Unpack the connect parameters + pParam = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(pRequest); + pTdiAddr= SpxParseTdiAddress( + pParam->RequestConnectionInformation->RemoteAddress); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: Remote SOCKET %lx on %lx.%lx\n", + pTdiAddr->Socket, + pSpxConnFile, + pRequest)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + do + { + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SPX2 requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SOCK_STREAM requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + } while (FALSE); + + if (!NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + if (pFindRouteReq) + { + SpxFreeMemory(pFindRouteReq); + } + + return(status); + } + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile) && + ((pSpxConnFile->scf_LocalConnId = spxConnGetId()) != 0)) + { + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_CONNECTING); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + + // Move connection from inactive list to non-inactive list. + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // This should never happen! + KeBugCheck(0); + } + + // Put connection in the non-inactive list. Connection id must be set. + SPX_INSERT_ADDR_ACTIVE( + pSpxAddr, + pSpxConnFile); + + // Insert in the global connection tree on device + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + // Store the remote address in the connection. + // !!NOTE!! We get both the network/socket in network form. + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) = + *((UNALIGNED ULONG *)(&pTdiAddr->NetworkAddress)); + + RtlCopyMemory( + pSpxConnFile->scf_RemAddr+4, + pTdiAddr->NodeAddress, + 6); + + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)) = + *((UNALIGNED USHORT *)(&pTdiAddr->Socket)); + + // Ok, we are all set, build connect packet, queue it into connection + // with the connect request. Ndis buffer already describes this memory + // Build IPX header. + + pCrPkt = NULL; // so it knows to allocate one. + + SpxPktBuildCr( + pSpxConnFile, + pSpxAddr, + &pCrPkt, + SPX_SENDPKT_IDLE, + SPX2_CONN(pSpxConnFile)); + + if (pCrPkt != NULL) + { + // Remember the request in the connection + // + // Dont queue for the failure case since we complete it in SpxInternalDispatch. + // + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + SpxConnQueueSendPktTail(pSpxConnFile, pCrPkt); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pIpxSpxHdr->hdr_DestNode, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNode); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pIpxSpxHdr->hdr_DestNode+4)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NETWORK %lx\n", + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet))); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NODE %02x-%02x-%02x-%02x-%02x-%02x\n", + pFindRouteReq->fr_FindRouteReq.Node[0], pFindRouteReq->fr_FindRouteReq.Node[1], + pFindRouteReq->fr_FindRouteReq.Node[2], pFindRouteReq->fr_FindRouteReq.Node[3], + pFindRouteReq->fr_FindRouteReq.Node[4], pFindRouteReq->fr_FindRouteReq.Node[5])); + + pFindRouteReq->fr_FindRouteReq.Identifier = IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // We wont force a rip for every connection. Only if its not + // in the IPX database. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // Reference for the find route. So that abort connect wont + // free up the connection until we return from here. + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + status = STATUS_PENDING; + } + else + { + // Abort connect attempt. + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + CTEAssert(pSpxConnFile->scf_ConnectReq == NULL); + + locksHeld = FALSE; + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (NT_SUCCESS(status)) + { + // Start off the find route request, We send the packet in completion. + // The verify reference is kept until the connect request completes. + // If connecting to network 0 we don't do this, proceed to find + // route completion which will send the request on very card. + + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + SpxFindRouteComplete( + &pFindRouteReq->fr_FindRouteReq, + TRUE); + + } else { + + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + } + else + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + SpxFreeMemory(pFindRouteReq); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + We assume the connection passed in is already associated with an address. + If it is not, we will die! Is that ok? + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle1, lockHandle2; + PSPX_ADDR pSpxAddr; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + + if (NT_SUCCESS(status)) + { + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + + // Move connection from inactive list to listening list. + if (NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // Put connection in the listening list. + SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile); + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + else + { + // This should never happen! + KeBugCheck(0); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle1); + } + + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_ADDR pSpxAddr; + NTSTATUS status; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: %lx\n", pSpxConnFile)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return (status); + } + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (NT_SUCCESS(status)) + { + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + status = STATUS_INVALID_CONNECTION; + if ((SPX_CONN_LISTENING(pSpxConnFile)) && + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_RECDREQ)) + { + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Call acceptcr now. + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: Accepted\n")); + + status = STATUS_PENDING; + } + else + { + // Free all locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + } + + // Remove reference. Note: Listen reference will exist if ok. And that will + // be transferred to the fact that the connection is active when accepted. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + If active, we do the following. + If informative disconnect, just remember the request in the connection. + We do not ref for request. Assume it will always be checked for when + changing from disconnect to idle. + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + SPX_SENDREQ_TYPE reqType; + int numDerefs = 0; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Deref unless the disc request gets queued in as a send request. + numDerefs++; + + DBGPRINT(CONNECT, DBG, + ("spxConnDisconnect: %lx On %lx when %lx.%lx %lx Params %lx\n", + pRequest, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile), + SPX_DISC_STATE(pSpxConnFile), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC), + pParam->RequestFlags)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnDisconnect: %lx\n", pSpxConnFile)); + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + switch (pParam->RequestFlags) + { + case TDI_DISCONNECT_WAIT: + + // If informative disconnect, just remember in the connection. + status = STATUS_INVALID_CONNECTION; + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + + break; + + case TDI_DISCONNECT_ABORT: + case TDI_DISCONNECT_RELEASE: + + // NOTE! We don't honor the async disconnect symantics of tdi + // but map them to an abortive disconnect. + // NOTE! If our send list is not empty but our client tries to + // do a orderly release, we just queue the ord rel as a send + // data request. In process ack, we check for the next packet + // to not be a ord rel before giving up on window closure. + // NOTE! For spx1 connection, map TDI_DISCONNECT_RELEASE to + // TDI_DISCONNECT_ABORT (Informed disconnect) + + if (!SPX2_CONN(pSpxConnFile)) + { + pParam->RequestFlags = TDI_DISCONNECT_ABORT; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // Since we are not a timer disconnect, then we need to keep + // retrying the disconnect packet. Change state to DISCONN if this + // is not an orderly release or we previously received an orderly + // release and are now confirming it. + // Retry timer will now keep sending out the disconnect packet. + + reqType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_ORDREL); + reqType = SPX_REQ_ORDREL; + } + else + { + // Abortive disconnect + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_IDISC); + numDerefs++; + + spxConnAbortSends( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Abort all receives if we are informed disconnect. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Since we released the lock, a remote IDISC could have come + // in in which case we really don't want to queue in the disc + // request. Instead, we set it as the disc request in the + // connection if one is not already there. + if (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDisconnect: DISC not POST! %lx.%lx\n", + pSpxConnFile, SPX_DISC_STATE(pSpxConnFile))); + + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + break; + } + } + + // !NOTE + // AbortSends might leave send requests around as packets might + // have been with ipx at the time. That is why SendComplete should + // never call AbortSends but must call AbortPkt else it may complete + // the following disconnect request prematurely. + + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = reqType; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Do not deref the connection, it is taken by the pending request + numDerefs--; + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandleConn); + + lockHeld = FALSE; + } + + status = STATUS_PENDING; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + status = STATUS_SUCCESS; + break; + + case SPX_CONNFILE_DISCONN: + + // When we queue in a disconnect as a send request, we expect + // to be able to set it into the scf_DiscReq when it is done. + // So we don't use scf_DiscReq here. This will be a problem if + // the client has a InformDiscReq pending, and a remote disconnect + // comes in, *and* the client then does a disc. We will be completing + // the request with STATUS_INVALID_CONNECTION. + status = STATUS_INVALID_CONNECTION; + if (pParam->RequestFlags != TDI_DISCONNECT_RELEASE) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + + // + // If this is a disconnect for a connection which was already + // disconnected (but AFD's disconnect handler was not called + // because the connfile could not be placed in the inactive list), + // set this flag so that the disconnect is not called from + // ConnInactivate now that the disconnect has occured here. + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // If this was an SPXI connection where we indicated TDI_DISCONNECT_RELEASE + // to AFD, the ref count was bumped up to indicate a wait for local disconnect + // from AFD. Now that we have this disconnect, deref the connection file. Now + // we are ready to truly inactivate this connection file. + // + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + lockHeld = FALSE; + + SpxConnFileDereference(pSpxConnFile, CFREF_DISCWAITSPX); + } + } + + break; + + default: + + // Should never happen! + status = STATUS_INVALID_CONNECTION; + } + + break; + + default: + + status = STATUS_INVALID_PARAMETER; + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + DBGPRINT(CONNECT, INFO, + ("SpxConnDisconnect: returning for %lx.%lx\n", pSpxConnFile, status)); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_SEND pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(SEND, DBG, + ("SpxConnSend: %lx.%lx.%lx.%lx\n", + pSpxConnFile, pRequest, pParam->SendLength, pParam->SendFlags)); + + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + DBGPRINT(SEND, INFO, + ("Send: %lx.%lx.%lx\n", + pParam->SendLength, pParam->SendFlags, pRequest)); + + status = STATUS_PENDING; + do + { + if (SPX_CONN_ACTIVE(pSpxConnFile) && + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED))) + { + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + DBGPRINT(SEND, INFO, + ("%lx\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + // + // Currently SPX implements DS_TYPE in such a way that the DS_TYPE field in a packet + // takes on the latest value from the connectionfile. Some customers complained that + // their apps assume that the DS_TYPE field should reflect the value at the time of + // the send (and Novell does this). + // + // So, instead of keeping a global value, we copy over the DS_TYPE value into the + // request if it pends. Look at spxpkt.c:SpxBuildData. + // + REQUEST_PARAMETERS(pRequest)->Others.Argument3 = (PVOID)pSpxConnFile->scf_DataType; + + } + else + { + // + // [SA] Bug #14655 + // Return the correct error message in case a send fails due to remote disconnect + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + status = STATUS_REMOTE_DISCONNECT ; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + break; + } + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize(pSpxConnFile, TRUE, lockHandleConn); + lockHeld = FALSE; + } + + } while (FALSE); + + + if (lockHeld) + { + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + CTELockHandle lockHandle; + BOOLEAN fLockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnReceive: %lx.%lx\n", pSpxConnFile, pRequest)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_ACTIVE(pSpxConnFile) && + !(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))) + { + status = STATUS_PENDING; + + // This routine adds its own reference. + SpxConnQueueRecv(pSpxConnFile, pRequest); + + // If recv pkt queue is non-empty then we have buffered data. Call + // process pkts/receives. + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) + { + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS Status; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + CTELockHandle lockHandle; + PIPX_SPXCONNSTATUS_DATA pGetStats; + PSPX_CONN_FILE pSpxConnFile = NULL; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(pRequest); + if (NdisBuffer == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer( + REQUEST_NDIS_BUFFER(pRequest), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + return STATUS_NOT_SUPPORTED; + } + + // Make sure we have enough room for just the header not + // including the data. + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, buffer too small\n")); + + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + // Make sure that the correct file object is being used. + switch (NwlinkAction->OptionType) + { + case NWLINK_OPTION_CONNECTION: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_CONNECTION_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not connection file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return(Status); + + break; + + case NWLINK_OPTION_ADDRESS: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not address file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(Status); + + break; + + default: + + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, option type %d\n", + NwlinkAction->OptionType)); + + return STATUS_INVALID_HANDLE; + } + + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + + Status = STATUS_SUCCESS; + + DBGPRINT(ACTION, INFO, + ("SpxConnAction: Option %x\n", NwlinkAction->Option)); + + switch (NwlinkAction->Option) + { + + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MSPX_SETDATASTREAM: + + if (pSpxConnFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + if (DataLength >= 1) + { + DBGPRINT(ACTION, INFO, + ("%lx: MIPX_SETSENDPTYPE %x\n", + pSpxConnFile, NwlinkAction->Data[0])); + + pSpxConnFile->scf_DataType = NwlinkAction->Data[0]; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MSPX_SENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_SENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break ; + + case MSPX_NOSENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_NOSENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_GETSTATS: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_GETSTATS\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + USHORT TempRetryCount; + + // + // Status fields are returned in network order. + // + + pGetStats = (PIPX_SPXCONNSTATUS_DATA)&NwlinkAction->Data[0]; + + switch (SPX_MAIN_STATE(pSpxConnFile)) { + case SPX_CONNFILE_LISTENING: pGetStats->ConnectionState = 1; break; + case SPX_CONNFILE_CONNECTING: pGetStats->ConnectionState = 2; break; + case SPX_CONNFILE_ACTIVE: pGetStats->ConnectionState = 3; break; + case SPX_CONNFILE_DISCONN: pGetStats->ConnectionState = 4; break; + default: pGetStats->ConnectionState = 0; + } + pGetStats->WatchDogActive = 1; // Always 1 + GETSHORT2SHORT( // scf_LocalConnId is in host order + &pGetStats->LocalConnectionId, + &pSpxConnFile->scf_LocalConnId); + pGetStats->RemoteConnectionId = pSpxConnFile->scf_RemConnId; + + GETSHORT2SHORT(&pGetStats->LocalSequenceNumber, &pSpxConnFile->scf_SendSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAckNumber, &pSpxConnFile->scf_RecvSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAllocNumber, &pSpxConnFile->scf_SentAllocNum); + GETSHORT2SHORT(&pGetStats->RemoteAckNumber, &pSpxConnFile->scf_RecdAckNum); + GETSHORT2SHORT(&pGetStats->RemoteAllocNumber, &pSpxConnFile->scf_RecdAllocNum); + + pGetStats->LocalSocket = pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket; + + RtlZeroMemory(pGetStats->ImmediateAddress, 6); + + // Remote network returned in net order. + *((ULONG UNALIGNED *)pGetStats->RemoteNetwork) = + *((ULONG UNALIGNED *)pSpxConnFile->scf_RemAddr); + + RtlCopyMemory( + pGetStats->RemoteNode, + &pSpxConnFile->scf_RemAddr[4], + 6); + + pGetStats->RemoteSocket = *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)); + + TempRetryCount = (USHORT)pSpxConnFile->scf_WRetryCount; + GETSHORT2SHORT(&pGetStats->RetransmissionCount, &TempRetryCount); + GETSHORT2SHORT(&pGetStats->EstimatedRoundTripDelay, &pSpxConnFile->scf_BaseT1); + pGetStats->RetransmittedPackets = 0; + pGetStats->SuppressedPacket = 0; + + DBGPRINT(ACTION, INFO, + ("SSeq %lx RSeq %lx RecdAck %lx RemAllocNum %lx\n", + pGetStats->LocalSequenceNumber, + pGetStats->LocalAckNumber, + pGetStats->RemoteAckNumber, + pGetStats->RemoteAllocNumber)); + + DBGPRINT(ACTION, INFO, + ("LocalSkt %lx RemSkt %lx LocConnId %lx RemConnId %lx\n", + pGetStats->LocalSocket, + pGetStats->RemoteSocket, + pGetStats->LocalConnectionId, + pGetStats->RemoteConnectionId)); + } + else + { + Status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + break; + + case MSPX_NOACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_NOACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_ACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_ACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + // The Option was not supported, so fail. + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (Status != STATUS_SUCCESS) { + DBGPRINT(ACTION, ERR, + ("Nwlink action %lx failed, status %lx\n", + NwlinkAction->Option, Status)); + } + +#endif + + if (pSpxConnFile) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + if (pSpxAddrFile) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + return Status; +} + + + + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + ULONG Timeout; + NTSTATUS status = STATUS_BAD_NETWORK_PATH; + + pSendResd = pSpxConnFile->scf_SendListHead; + pCrPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: %lx.%d\n", + pSpxConnFile, FoundRoute)); + +#if defined(_PNP_POWER) + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + // Here we are going to send on every NIC ID. We adjust the + // timeout down so that a full run through all the NIC IDs will + // take one normal timeout. We don't adjust the timer below + // 100 ms however. + + Timeout = (PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR) / SpxDevice->dev_Adapters; + if (Timeout < (HALFSEC_TO_MS_FACTOR/5)) { + Timeout = HALFSEC_TO_MS_FACTOR / 5; + } + + } else { + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; + } +#endif + + + // Timeout value is in half-seconds + if ((FoundRoute) && + ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + Timeout, + pSpxConnFile)) != 0)) + { + // Add a reference for the connect timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + +#if 0 + { + int i; + char *address = pFrReq->fr_FindRouteReq.LocalTarget.MacAddress; + + DbgPrint("FIND ROUTE LOCALTARGET.MAC:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + + address = pSpxConnFile->scf_RemAddr+4; + DbgPrint("SPX DESTINATION ADDRESS:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + } + + DbgPrint("NIC Id %lx\n", pFrReq->fr_FindRouteReq.LocalTarget.NicId); +#endif + + // If the mac address in local target is all zeros, fill it with our + // destination address. Also if this is a connect to network 0 fill + // it in with the destination address, and further down we will loop + // through all possible NIC IDs. + if (((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + || + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + // We are all set to go ahead with the connect. + // Timer is started on connection + status = STATUS_SUCCESS; + +#if defined(_PNP_POWER) + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT) * SpxDevice->dev_Adapters; + } else { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } +#endif _PNP_POWER + + SPX_CONN_SETFLAG(pSpxConnFile, + (SPX_CONNFILE_C_TIMER | SPX_CONNECT_SENTREQ)); + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + pSpxConnFile->scf_AckLocalTarget= pFrReq->fr_FindRouteReq.LocalTarget; + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#if defined(_PNP_POWER) + pSpxConnFile->scf_LocalTarget.NicHandle.NicId = 0; + pSpxConnFile->scf_AckLocalTarget.NicHandle.NicId = 0; +#else + pSpxConnFile->scf_LocalTarget.NicId = 1; + pSpxConnFile->scf_AckLocalTarget.NicId = 1; +#endif _PNP_POWER + } + + // We will be giving the packet to ipx. + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pCrPkt, pSendResd); + } + + if (!NT_SUCCESS(status)) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + DBGPRINT(CONNECT, ERR, + ("SpxConnConnectFindRouteComplete: FAILED on %lx.%d\n", + pSpxConnFile, FoundRoute)); + + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + } + + // Remove the reference for the call. + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + BOOLEAN fDisconnect = TRUE; + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + + DBGPRINT(CONNECT, DBG, + ("SpxConnActiveFindRouteComplete: %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // If we are disconnecting, just remove the reference and exit. + if (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE) + { + fDisconnect = FALSE; + + // We are here if either the wdog or the retry timer did a find + // route. We need to save the info from the find route if it was + // successful and just restart the timers. + if (FoundRoute) + { + // If the mac address in local target is all zeros, fill it with our + // destination address. + if ((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnActiveFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + } + + // Depending on state restart the wdog or retry timer. Add reference + // for it. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRY: + + // Set state to SPX_SEND_RETRYWD + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRYWD); + + // Start retry timer. + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Start watchdog timer. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_IDLE: + case SPX_SEND_PACKETIZE: + + // Do nothing, remove reference and leave. + break; + + default: + + KeBugCheck(0); + } + } + + if (fDisconnect) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnActiveFindRouteComplete: DISCONNECT %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // Abortive disc will reset the funky state if necessary. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + LockHandle, + FALSE); // [SA] Bug #15249 + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + We enter this routine during the connection attempt. We could be at any + stage of sending either the CR or the SN packet. If we have reached the end of + the retry count, we need to know the substate at that point. For a CR, we give + up trying to connect, and for a SN we try the next lower packet size or if we + have reached the minimum packet size, we give up the connect. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN fAbort = FALSE, locksHeld = FALSE, sendPkt = FALSE; + PREQUEST pRequest = NULL; + + // Get all locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnConnectTimer: Entered\n")); + + do + { + if ((!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) || + (!SPX_CONN_CONNECTING(pSpxConnFile) && + !SPX_CONN_LISTENING(pSpxConnFile))) + { + TimerShuttingDown = TRUE; + } + + if (TimerShuttingDown) + { + break; + } + + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // There should be only one packet in list, the cr. + CTEAssert(pSpxConnFile->scf_SendListHead == + pSpxConnFile->scf_SendListTail); + + pSendResd = pSpxConnFile->scf_SendListHead; + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, + NDIS_PACKET, + ProtocolReserved); + + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // No luck, we need to complete connect request with failure + ++SpxDevice->dev_Stat.NotFoundFailures; + fAbort = TRUE; + break; + } + + // We need to resend the packet + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + break; + } + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_NEG: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_W_SETUP: + default: + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: state is W_Setup %lx\n", + pSpxConnFile)); + + KeBugCheck(0); + } + } + else + { + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_SETUP: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. Have an abort + // kind of routine. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + default: + + KeBugCheck(0); + + } + } + + } while (FALSE); + + if (fAbort) + { + CTEAssert(!sendPkt); + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: Expired for %lx\n", pSpxConnFile)); + + spxConnAbortConnect( + pSpxConnFile, + STATUS_BAD_NETWORK_PATH, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (sendPkt) + { + CTEAssert(!fAbort); + +#if !defined(_PNP_POWER) + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) { + + // we are sending to all NICs because this is the initial + // connect frame and the remote network is 0. + + pSpxConnFile->scf_LocalTarget.NicId = (USHORT) + ((pSpxConnFile->scf_LocalTarget.NicId % SpxDevice->dev_Adapters) + 1); + + // we pass this a valid packet in pPkt, so it knows to + // just refresh the header and not update the protocol + // reserved variables. + + SpxPktBuildCr( + pSpxConnFile, + pSpxConnFile->scf_AddrFile->saf_Addr, + &pPkt, + 0, // state will not be updated + SPX2_CONN(pSpxConnFile)); + + } +#endif !_PNP_POWER + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + if (TimerShuttingDown || fAbort) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); + } + + return(TIMER_REQUEUE_CUR_VALUE); +} + + + + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + This is started on a connection right after the CR or the CR ack is received. + During the connection establishment phase, it does nothing other than decrement + the retry count and upon reaching 0, it aborts the connection. When it goes off + and finds the connection is active, it sends a probe. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + PNDIS_PACKET pProbe = NULL; + BOOLEAN lockHeld, fSpx2 = SPX2_CONN(pSpxConnFile), + fDisconnect = FALSE, fFindRoute = FALSE, fSendProbe = FALSE; + + DBGPRINT(CONNECT, INFO, + ("spxConnWatchdogTimer: Entered\n")); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + do + { + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // If the retry timer is active on this connection, and the watchdog + // timer happens to fire, just requeue ourselves for spx2. For spx1, + // we go ahead with sending a probe. Retry timer does the same things + // watchdog does for spx2. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // Squash the race condition where a disconnect request is never + // packetized, because the send state was not IDLE. + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: POST IDISC %lx\n", + pSpxConnFile)); + + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: PKT POST IDISC %lx\n", + pSpxConnFile)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle); + + lockHeld = FALSE; + break; + } + } + + if (!fSpx2) + { + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + fSendProbe = TRUE; + } + else + { + fDisconnect = TRUE; + } + + break; + } + + // SPX2 connection. Watchdog algorithm needs to do lots of goody + // stuff. If retry is active, just requeue ourselves. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + break; + + // There is a race between watchdog and retry if its started. Who + // ever changes the state first gets to go do its thing. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Enter WD state only if we fired for the second time witout + // an ack. This prevents PACKETIZE from blocking due to being + // in a non-idle state. + CTEAssert(pSpxConnFile->scf_WRetryCount != 0); + if ((pSpxConnFile->scf_WRetryCount)-- != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)) + { + // We enter the WD state. Build and send a probe. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_WD); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + + fSendProbe = TRUE; + break; + + case SPX_SEND_PACKETIZE: + + // Do nothing. + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRYWD: + case SPX_SEND_RENEG: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + // Do nothing. Send timer got in first. + DBGPRINT(CONNECT, DBG1, + ("SpxConnWDogTimer: When retry fired %lx\n", + pSpxConnFile)); + + break; + + case SPX_SEND_WD: + + // Decrement count. If not zero, send a probe. If half the + // count is reached, stop timer and call find route. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + if (pSpxConnFile->scf_WRetryCount != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)/2) + { + fSendProbe = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network) = + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6); + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnWDogTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + } + else + { + fDisconnect = TRUE; + } + + break; + + default: + + KeBugCheck(0); + } + + break; + + case SPX_CONNFILE_CONNECTING: + + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) || + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_NEG)) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_CONNFILE_LISTENING: + + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + default: + + // Should never happen! + KeBugCheck(0); + } + + } while (FALSE); + + if (fSendProbe) + { + CTEAssert(lockHeld); + CTEAssert(!fDisconnect); + + DBGPRINT(CONNECT, DBG1, + ("spxConnWatchdogTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + fSpx2); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + } + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + CTEAssert(!fSendProbe); + + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + + // If spx2, check if we need to do anything special. + // AbortiveDisc will reset funky state if needed. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fFindRoute) + { + CTEAssert(!fSendProbe); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + + if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return((TimerShuttingDown ? TIMER_DONT_REQUEUE : TIMER_REQUEUE_CUR_VALUE)); +} + + + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn; + PIPXSPX_HDR pSendHdr; + PNDIS_PACKET pPkt; + PNDIS_PACKET pProbe = NULL; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + USHORT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; + BOOLEAN lockHeld, fResendPkt = FALSE, fDisconnect = FALSE, + fFindRoute = FALSE, fBackoffTimer = FALSE; + PREQUEST pRequest = NULL; + + DBGPRINT(CONNECT, INFO, + ("spxConnRetryTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + do + { + // If timer is not up, no send pkts, just return. + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL)) + { +#if DBG + if ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL) + { + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // In all other cases, reenqueue with potentially modified reenqueue + // time. + reenqueueTime = pSpxConnFile->scf_BaseT1; + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: BaseT1 %lx on %lx\n", + pSpxConnFile->scf_BaseT1, pSpxConnFile)); + + // If an ack for a packet was processed while we were out, reset + // retry count and return. Or if we are packetizing, return. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) + { + break; + } + else if ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_RetrySeqNum != pSendResd->sr_SeqNum)) + { + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + break; + } + + // If packet is still with IPX, requeue for next time. + if (pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) + { + break; + } + + CTEAssert(pSendResd != NULL); + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // Do we backoff the timer? + fBackoffTimer = + (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_REXMIT) != 0); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + ++SpxDevice->dev_Stat.ResponseTimerExpirations; + + CTEAssert((ULONG)pSpxConnFile->scf_RRetryCount <= + PARAM(CONFIG_REXMIT_COUNT)); + + DBGPRINT(SEND, DBG1, + ("spxConnRetryTimer: Retry Count %lx on %lx\n", + pSpxConnFile->scf_RRetryCount, pSpxConnFile)); + + fResendPkt = TRUE; + if (pSpxConnFile->scf_RRetryCount-- != 0) + { + // We dont treat the IDISC packet as a data packet, so none + // of the fancy spx2 retry stuff if we are retrying the idisc. + if (SPX2_CONN(pSpxConnFile) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_IDISC)) + { + // We enter the RETRY state. Reference conn for this + // "funky" state. + CTEAssert(SPX2_CONN(pSpxConnFile)); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + fResendPkt = FALSE; + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + } + + break; + + case SPX_SEND_RETRY: + + // When we have reached retry_count/2 limit, start locate route. Do + // not queue ourselves. Handle restarting timer in find route + // completion. If timer starts successfully in find route comp, then + // it will change our state to RETRYWD. + + // Decrement count. If half the count is reached, stop timer and call + // find route. + if (pSpxConnFile->scf_RRetryCount-- != + (LONG)PARAM(CONFIG_REXMIT_COUNT)/2) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Alloc Mem %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4)) = + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnRetryTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + break; + + case SPX_SEND_RETRYWD: + + // Retry a watchdog packet WCount times (initialize to RETRY_COUNT). + // If process ack receives an ack (i.e. actual ack packet) while in + // this state, it will transition the state to RENEG. + // + // If the pending data gets acked while in this state, we go back + // to idle. + DBGPRINT(CONNECT, DBG1, + ("spxConnRetryTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Use watchdog count here. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + TRUE); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + break; + } + } + + // Just set state to retry data packet retry_count/2 times. + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY2); + break; + + case SPX_SEND_RENEG: + + // Renegotiate size. If we give up, goto RETRY3. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + break; + } + + // Send reneg packet, if we get the rr ack, then we resend data + // on queue. Note that each time we goto a new negotiate size, + // we rebuild the data packets. + if (pSpxConnFile->scf_RRetryCount-- == 0) + { + // Reset count. + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnRetryTimer: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + + // Are we at the lowest possible reneg pkt size? If not, try + // next lower. When we do this, we free all pending send + // packets and reset the packetize queue to the first packet. + // Process ack will just do packetize and will not do anything + // more other than resetting state to proper value. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG: %lx - CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // We tried lowest size and failed to receive ack. Just + // retry data packet, and disc if no ack. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(min), RETRY3: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + break; + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(!min): %lx - ATTEMPT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: %lx.%lx.%lx RENEG SEQNUM %lx ACKNUM %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RRetryCount, + pSpxConnFile->scf_MaxPktSize, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + pSpxConnFile->scf_SentAllocNum)); + + // Use first unused data packet sequence number. + SpxPktBuildRr( + pSpxConnFile, + &pPkt, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY)); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + fResendPkt = TRUE; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + } + + break; + + case SPX_SEND_RETRY2: + + // Retry the data packet for remaining amount of RRetryCount. If not + // acked goto cleanup. If ack received while in this state, goto idle. + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 2nd try Resend on %lx\n", + pSpxConnFile)); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_RETRY3: + + // Send data packet for RETRY_COUNT times initialized in RRetryCount + // before state changed to this state. If ok, process ack moves us + // back to PKT/IDLE. If not, we disconnect. + // We are going to resend this packet + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 3rd try Resend on %lx\n", + pSpxConnFile)); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Do nothing. Watchdog timer has fired, just requeue. + break; + + default: + + KeBugCheck(0); + } + + if (fBackoffTimer) + { + // Increase retransmit timeout by 50% upto maximum indicated by + // initial retransmission value. + + reenqueueTime += reenqueueTime/2; + if (reenqueueTime > MAX_RETRY_DELAY) + reenqueueTime = MAX_RETRY_DELAY; + + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = reenqueueTime; + pSpxConnFile->scf_DevT1 = 0; + + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Backed retry on %lx.%lx %lx\n", + pSpxConnFile, pSendResd->sr_SeqNum, reenqueueTime)); + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + + // Do not requeue this timer. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + + // Disconnect the connection. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + else if (fFindRoute) + { + CTEAssert(!fResendPkt); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: Find route on %lx\n", + pSpxConnFile)); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + else if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + reenqueueTime = TIMER_DONT_REQUEUE; + } + + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: Reenqueue time : %lx on %lx\n", + reenqueueTime, pSpxConnFile)); + + return(reenqueueTime); +} + + + + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandleConn; + + DBGPRINT(SEND, INFO, + ("spxConnAckTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (!TimerShuttingDown && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // We didnt have any back traffic, until we do a send from this + // end, send acks immediately. Dont try to piggyback. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + ++SpxDevice->dev_Stat.PiggybackAckTimeouts; + + DBGPRINT(SEND, DBG, + ("spxConnAckTimer: Send ack on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + SpxConnSendAck(pSpxConnFile, lockHandleConn); + } + else + { + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); +} + + + +// +// DISCONNECT ROUTINES +// + + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN IDiscFlag // [SA] Bug #15249 + ) +/*++ + +Routine Description: + + This is called when: + We time out or have insufficient resources - + STATUS_LINK_TIMEOUT/STATUS_INSUFFICIENT_RESOURCES + - We abort everything. Could be from watchdog or retry. Stop both. + + We receive a informed disconnect packet - + STATUS_REMOTE_DISCONNECT + - We abort everything. Ack must be sent by caller as an orphan pkt. + + We receive a informed disconnect ack pkt + STATUS_SUCCESS + - We abort everything + - Abort is done with status success (this completes our disc req in + the send queue) + + NOTE: CALLED UNDER THE CONNECTION LOCK. + +Arguments: +[SA] Bug #15249: Added IDiscFlag to indicate if this is an Informed Disconnect. If so, indicate + TDI_DISCONNECT_RELEASE to AFD so it allows a receive of buffered pkts. This flag is TRUE + only if this routine is called from SpxConnProcessIDisc for SPX connections. + +Return Value: + + +--*/ +{ + int numDerefs = 0; + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + BOOLEAN lockHeld = TRUE; + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: %lx - On %lx when %lx\n", + Status, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + switch (Status) { + case STATUS_LINK_TIMEOUT: ++SpxDevice->dev_Stat.LinkFailures; break; + case STATUS_INSUFFICIENT_RESOURCES: ++SpxDevice->dev_Stat.LocalResourceFailures; break; + case STATUS_REMOTE_DISCONNECT: ++SpxDevice->dev_Stat.RemoteDisconnects; break; + case STATUS_SUCCESS: + case STATUS_LOCAL_DISCONNECT: ++SpxDevice->dev_Stat.LocalDisconnects; break; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // For transition from active to disconn. + numDerefs++; + + case SPX_CONNFILE_DISCONN: + + // If we are in any state other than idle/packetize, + // remove the reference for the funky state, and reset the send state to be + // idle. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) + { +#if DBG + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnAbortiveDisc: When DISC STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(CONNECT, DBG1, + ("spxConnAbortiveDisc: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // This can be called when a idisc is received, or if a timer + // disconnect is happening, or if we sent a idisc/ordrel, but the retries + // timed out and we are aborting the connection. + // So if we are already aborting, never mind. + + // + // [SA] Bug #15249 + // SPX_DISC_INACTIVATED indicates a DISC_ABORT'ing connection that has been + // inactivated (connfile removed from active conn. list) + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + break; + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ABORT); + + // Stop all timers. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_RTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } +#if 0 + // + // [SA] We need to call AFD after aborting sends since this connection + // becomes a candidate for re-use as soon as the disconnect handler is + // called. + // We call the disconnect handler when the refcount falls to 0 and the + // connection transitions to the inactive list. + // + + // NOTE! We indicate disconnect to afd *before* aborting sends to avoid + // afd from calling us again with a disconnect. + // Get disconnect handler if we have one. And we have not indicated + // abortive disconnect on this connection to afd. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } +#endif + // + // [SA] Save the IDiscFlag in the Connection. + // + (IDiscFlag) ? + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC) : + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(CONNECT, INFO, + ("spxConnAbortiveDisc: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + + if (!IDiscFlag) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_ABORT); + } + else + { + // + // [SA] bug #15249 + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + spxConnAbortSends( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + lockHeld = FALSE; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + lockHeld = FALSE; + + { + CTELockHandle lockHandleAddr, lockHandleDev; + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Ensure we are still in connecting/listening, else call abortive + // again. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortConnect( + pSpxConnFile, + Status, + lockHandleDev, + lockHandleAddr, + LockHandleConn); + + break; + + case SPX_CONNFILE_ACTIVE: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CHG ACT Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortiveDisc( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn, + FALSE); // [SA] Bug #15249 + + break; + + default: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + break; + } + } + + default: + + // Already disconnected. + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxcpkt.c b/private/ntos/tdi/isnp/spx/spxcpkt.c new file mode 100644 index 000000000..deb185201 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcpkt.c @@ -0,0 +1,4131 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcpkt.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXCPKT + +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + BOOLEAN fNeg, fSpx2; + TA_IPX_ADDRESS srcIpxAddr; + PTDI_IND_CONNECT connHandler; + USHORT srcConnId, destConnId, destSkt, + pktLen, seqNum, ackNum, allocNum; + PVOID connHandlerCtx; + PREQUEST pListenReq; + PSPX_SEND_RESD pSendResd; + NTSTATUS status; + CTELockHandle lockHandle, lockHandleDev, lockHandleConn; + CONNECTION_CONTEXT connCtx; + PIRP acceptIrp; + PSPX_ADDR pSpxAddr; + PSPX_ADDR_FILE pSpxAddrFile, pSpxRefFile; + PSPX_CONN_FILE pSpxConnFile; + PNDIS_PACKET pCrAckPkt; + BOOLEAN connectAccepted = FALSE, delayAccept = FALSE, + addrLock = FALSE, tdiListen = FALSE; + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // Verify Connect Request + if (((pIpxSpxHdr->hdr_ConnCtrl & (SPX_CC_ACK | SPX_CC_SYS)) != + (SPX_CC_ACK | SPX_CC_SYS)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (seqNum != 0) || + (ackNum != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId != 0xFFFF)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifyCR Failed %lx.%lx\n", + srcConnId, destConnId)); + return; + } + + // Get the destination socket from the header + destSkt = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_DestSkt; + + SpxBuildTdiAddress( + &srcIpxAddr, + sizeof(srcIpxAddr), + (PBYTE)pIpxSpxHdr->hdr_SrcNet, + pIpxSpxHdr->hdr_SrcNode, + pIpxSpxHdr->hdr_SrcSkt); + + // Ok, get the address object this is destined for. + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + pSpxAddr = SpxAddrLookup(SpxDevice, destSkt); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + if (pSpxAddr == NULL) + { + DBGPRINT(RECEIVE, DBG, + ("SpxReceive: No addr for %lx\n", destSkt)); + + return; + } + + fSpx2 = ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2)); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Received connect req! %d.%d\n", + fSpx2, fNeg)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + + // We use a bit setting in the flag to prevent reentering + // per address file. + // + // We first search the list of non-inactive connections on the address + // this packet came in on to see if it is a duplicate. If it is, we just + // resend ack. Note we dont need to scan the global connection list. + status = SpxAddrConnByRemoteIdAddrLock( + pSpxAddr, srcConnId, pIpxSpxHdr->hdr_SrcNet, &pSpxConnFile); + + if (NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Received duplicate connect req! %lx\n", + pSpxConnFile)); + + if (SPX_CONN_ACTIVE(pSpxConnFile) || + (SPX_CONN_LISTENING(pSpxConnFile) && + ((SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) || + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Sending Duplicate CR - ACK! %lx\n", + pSpxConnFile)); + + // Build and send an ack + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)); + + if (pCrAckPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + // Send the CR Ack packet! + if (pCrAckPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + } + } + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // We should return in this if, else addrLock should be set to + // FALSE. + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; + } + + do + { + // New connection request: + // Assume we will be able to accept it and allocate a packet for the ack. + // Walk list of listening connections if any. + + pSpxRefFile = NULL; + if ((pSpxConnFile = pSpxAddr->sa_ListenConnList) != NULL) + { + PTDI_REQUEST_KERNEL_LISTEN pParam; + + DBGPRINT(RECEIVE, INFO, + ("SpxConnIndicate: Listen available!\n")); + + // dequeue connection + pSpxAddr->sa_ListenConnList = pSpxConnFile->scf_Next; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + pListenReq = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + pParam = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(pListenReq); + + // if autoaccept, acceptIrp = listenIrp, get connection id and + // process as we do for an indication. As the connection has a + // listen posted on it, it must have a reference for it. + // + // if !autoaccept, we need to complete the listen irp. + delayAccept = (BOOLEAN)((pParam->RequestFlags & TDI_QUERY_ACCEPT) != 0); + if (delayAccept) + { + // Remove the listen irp and prepare for completion. + // NOTE!! Here we do not remove the listen reference. This will + // be removed if disconnect happens, or if accept + // happens, it is transferred to being ref for connection + // being active. + RemoveEntryList(REQUEST_LINKAGE(pListenReq)); + REQUEST_STATUS(pListenReq) = STATUS_SUCCESS; + REQUEST_INFORMATION(pListenReq) = 0; + } + + // Are we ok with spx2? + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)) || + !fSpx2) + { + // We better use spx only. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + fSpx2 = fNeg = FALSE; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + connectAccepted = TRUE; + tdiListen = TRUE; + } + else + { + // No listens available. Check for connect handlers. + // Walk list of address files indicating to each until accepted. + for (pSpxAddrFile = pSpxAddr->sa_AddrFileList; + pSpxAddrFile != NULL; + pSpxAddrFile = pSpxAddrFile->saf_Next) + { + if ((pSpxAddrFile->saf_Flags & (SPX_ADDRFILE_CLOSING | + SPX_ADDRFILE_CONNIND)) || + ((connHandler = pSpxAddrFile->saf_ConnHandler) == NULL)) + { + continue; + } + + // Connect indication in progress, drop all subsequent. + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_CONNIND; + + connHandlerCtx = pSpxAddrFile->saf_ConnHandlerCtx; + SpxAddrFileLockReference(pSpxAddrFile, AFREF_INDICATION); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + // Make the indication. We are always returned an accept irp on + // indication. Else we fail to accept the connection. + status = (*connHandler)( + connHandlerCtx, + sizeof(srcIpxAddr), + (PVOID)&srcIpxAddr, + 0, // User data length + NULL, // User data + 0, // Option length + NULL, // Options + &connCtx, + &acceptIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConn: indicate status %lx.%lx\n", + status, acceptIrp)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_CONNIND; + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEAssert(acceptIrp != NULL); + + // Find the connection and accept the connection using that + // connection object. + SpxConnFileReferenceByCtxLock( + pSpxAddrFile, + connCtx, + &pSpxConnFile, + &status); + + if (!NT_SUCCESS(status)) + { + // The connection object is closing, or is not found + // in our list. The accept irp must have had the same + // connection object. AFD isnt behaving well. + KeBugCheck(0); + } + + // Only for debugging. + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_BYCTX, + CFREF_VERIFY); + + pListenReq = SpxAllocateRequest( + SpxDevice, + acceptIrp); + + IF_NOT_ALLOCATED(pListenReq) + { + acceptIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + break; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pListenReq)); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + connectAccepted = TRUE; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((pSpxAddrFile->saf_Flags & SPX_ADDRFILE_SPX2) && fSpx2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + else + { + fSpx2 = fNeg = FALSE; + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + + break; + } + else + { + // We are not going to accept the connection on this address. + // Try next one. + pSpxRefFile = pSpxAddrFile; + continue; + } + } + } + + } while (FALSE); + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // No need for flag from this point on. + // addrLock = FALSE; + } + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + if (connectAccepted) + { + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_LocalConnId = spxConnGetId(); + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RetrySeqNum = 0; + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + pSpxConnFile->scf_RecdAllocNum = allocNum; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleConnReq: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Set max packet size in connection + SPX_MAX_PKT_SIZE(pSpxConnFile, (fSpx2 && fNeg), fSpx2, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Accept connect req on %lx.%lx..%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + // Aborts must now deal with the lists. Need this as Accept has to + // deal with it. + // Put in non-inactive list. All processing now is equivalent to + // that when a listen is completed on a connection. + if ((!tdiListen) && (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile)))) + { + // Should never happen! + KeBugCheck(0); + } + + SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile); + + // Insert in the global connection tree on device. + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + SPX_CONN_SETFLAG(pSpxConnFile, + ((fNeg ? SPX_CONNFILE_NEG : 0) | + (fSpx2 ? SPX_CONNFILE_SPX2: 0))); + + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } +#if 0 + // + // Make sure that this connection got a local disconnect if it was an SPXI + // connection earlier, in response to a TDI_DISCONNECT_RELEASE. + // + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX] == 0); +#endif + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + SPX_LISTEN_SETSTATE(pSpxConnFile, (delayAccept ? SPX_LISTEN_RECDREQ : 0)); + + if (!delayAccept) + { + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandle, + lockHandleConn); + } + else + { + // Release the locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + // Complete the listen irp. Note reference is not removed. Done when + // accept is posted. + SpxCompleteRequest(pListenReq); + } + } else { + ++SpxDevice->dev_Stat.NoListenFailures; + } + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; +} + + + + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the client side of the connection. + Handles: + Session Negotiate + Sends Session Setup, when recd, handles SS Ack + + STATE MACHINE: + + RR + / \ + / \ ReceivedAck(SPX1Connection) + / \ + / \--------> ACTIVE + / ^ + Send / | + ACK / | + / | + / RecvNeg/NoNeg | + / SendSS | + SA--------->SS---------------+ + ^ | SSAckRecv + | | + +-----+ + RecvNeg + + RR - Received Connect Request + SA - Sent CR Ack + SS - Sent Session Setup + + We move from SA to SS when connection is not negotiatiable and we + immediately send the SS, or when we receive negotiate packet and send the neg + ack and the session setup. + + Note we could receive a negotiate packet when in SS, as our ack to the + negotiate could have been dropped. We deal with this. + +Arguments: + + +Return Value: + + +--*/ + +{ + PNDIS_PACKET pSnAckPkt, pSsPkt = NULL; + PSPX_SEND_RESD pSendResd, pSsSendResd; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN locksHeld = FALSE; + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromClient: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_RECDREQ: + + // Do nothing. + break; + + case SPX_LISTEN_SETUP: + + // Is this a setup ack? If so, yippee. Our ack to a negotiate packet + // could have been dropped, and so we could also get a negotiate packet + // in that case. If that happens, fall through. + // Verify Ss Ack + if (!SPX2_CONN(pSpxConnFile) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: VerifySSACK Failed Checking SN %lx.%lx\n", + srcConnId, destConnId)); + + // Fall through to see if this is a neg packet + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG))) + { + break; + } + } + else + { + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Recd SSACK %lx\n", + pSpxConnFile)); + + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + case SPX_LISTEN_SENTACK: + + // We expect a negotiate packet. + // We should have asked for SPX2/NEG to begin with. + // Verify Sn + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySN Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + CTEAssert(negSize > 0); + + // Build sn ack, abort if we fail + SpxPktBuildSnAck( + pSpxConnFile, + &pSnAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY); + + if (pSnAckPkt == NULL) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SNACK %lx\n", + pSpxConnFile)); + + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pSnAckPkt); + + // The session packet should already be on queue. + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pSsPkt)) + { + KeBugCheck(0); + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SS %lx\n", + pSpxConnFile)); + + pSsSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + + // We need to resend the packet + if ((pSsSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + pSsPkt = NULL; + } + else + { + // Set the size to the neg size indicated in connection. + // This could be lower than the size the packet was build + // with originally. But will never be higher. + pSsSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + spxConnSetNegSize( + pSsPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + } + + // If we are actually LISTEN_SETUP, then send the ss packet also. + // We need to start the connect timer to resend the ss pkt. + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + SPX_LISTEN_SETSTATE(pSpxConnFile, SPX_LISTEN_SETUP); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSnAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnAckPkt, pSendResd); + + // If we have to send the session setup packet, send that too. + if (pSsPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + return; +} + + + + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the server side of the connection. This will both + release the lock and dereference the connection as it sees fit. + + STATE MACHINE: + + SR--CTimerExpires-->IDLE + /| \ + / | \ ReceivedAck(SPX1Connection) + / | \ + / | \--------> ACTIVE + (Neg) / | ^ + Send / |RecvAck | + SN / |NoNeg | + / | | + / | | + / v | + SN--------->WS---------------+ + RecvSNAck RecvSS + + SR - Sent Connect request + SN - Sent Session Negotiate + WS - Waiting for session setup packet + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + BOOLEAN fNeg, fSpx2; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN cTimerCancelled = FALSE, fAbort = FALSE, locksHeld = FALSE; + PNDIS_PACKET pSsAckPkt, pSnPkt, pPkt = NULL; + + // We should get a CR Ack, or if our substate is sent session neg + // we should get a session neg ack, or if we are waiting for session + // setup, we should get one of those. + + fSpx2 = (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // Check if this qualifies as the ack. + // Verify CR Ack + if ((pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (seqNum != 0) || + (ackNum != 0) || + ((pktLen != MIN_IPXSPX_HDRSIZE) && + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen != MIN_IPXSPX2_HDRSIZE))) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleSessPktFromSrv: CRAck Invalid %lx %lx.%lx.%lx\n", + pSpxConnFile, + pktLen, negSize, pIpxSpxHdr->hdr_ConnCtrl)); + + break; + } + + // !!!BUGBUG!!! + // From current spx code base: + // Do we need to send an ack to this ack? In case of SPX only? + // What if this ack is dropped? We need to send an ack, if in future + // we get CONNECT REQ Acks, until we reach active? + // * If they want an ack schedule it. The normal case is for this not + // * to happen, but some Novell mainframe front ends insist on having + // * this. And technically, it is OK for them to do this. + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: Recd CRACK %lx\n", pSpxConnFile)); + + // Grab the remote alloc num/conn id (in net format) + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_RecdAllocNum = allocNum; + + // If we have been looking for network 0, which means the + // packets were sent on all NIC IDs, update our local + // target now that we have received a response. + +#if defined(_PNP_POWER) + if (pSpxConnFile->scf_LocalTarget.NicHandle.NicId == 0) { +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#endif _PNP_POWER + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + } + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + if (!fSpx2 || !fNeg) + { + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + fAbort = TRUE; + break; + } + + // Reference transferred to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // Set max packet size, assume not spx2 or !neg, so pass in FALSE + SPX_MAX_PKT_SIZE(pSpxConnFile, FALSE, FALSE, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPSrv: Accept connect req on %lx.%lx.%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + if (!fSpx2) + { + // Reset spx2 flags. + SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + + // Complete connect request, this free the lock. + // Cancels tdi timer too. Sets all necessary flags. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + if (!fNeg) + { + // Goto W_SETUP + // Reset all connect related flags, also spx2/neg flags. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_NEG); + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + break; + } + + // Reset max packet size. SPX2 and NEG. + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, pIpxSpxHdr->hdr_SrcNet); + + CTEAssert(negSize > 0); + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + pSpxConnFile->scf_MaxPktSize = + MIN(negSize, pSpxConnFile->scf_MaxPktSize); + + pSpxConnFile->scf_MaxPktSize = (USHORT) + MIN(pSpxConnFile->scf_MaxPktSize, PARAM(CONFIG_MAX_PACKET_SIZE)); + + // For SPX2 with negotiation, we set up sneg packet and move to + // SPX_CONNECT_NEG. + SpxPktBuildSn( + pSpxConnFile, + &pSnPkt, + SPX_SENDPKT_IPXOWNS); + + if (pSnPkt == NULL) + { + fAbort = TRUE; + break; + } + + // Queue in packet + SpxConnQueueSendPktTail(pSpxConnFile, pSnPkt); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SN %lx\n", + pSpxConnFile)); + + // Reset retry count for connect timer + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Change state. + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_NEG); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send the packet + pSendResd = (PSPX_SEND_RESD)(pSnPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnPkt, pSendResd); + break; + + case SPX_CONNECT_NEG: + + // We expect a session neg ack. + // We should have asked for SPX2/NEG to begin with. + // Verify SN Ack + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySNACK Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SNACK %lx %lx.%lx\n", + pSpxConnFile, negSize, pSpxConnFile->scf_MaxPktSize)); + + if (negSize > pSpxConnFile->scf_MaxPktSize) + negSize = pSpxConnFile->scf_MaxPktSize; + + // Get the size to use + if (negSize <= pSpxConnFile->scf_MaxPktSize) + { + pSpxConnFile->scf_MaxPktSize = negSize; + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + else + { + // Free the negotiate packet + SpxPktSendRelease(pPkt); + } + + CTEAssert(pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER); + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + // Start the watchdog timer, if fail, we abort. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + // Complete cr with error. + fAbort = TRUE; + break; + } + + // Reference goes to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + // We move to the W_SETUP state. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + } + + break; + + case SPX_CONNECT_W_SETUP: + + // Does this qualify as a session setup packet? + // Verify SS + if (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySS Failed %lx.%lx, %lx %lx.%lx\n", + srcConnId, destConnId, negSize, + pIpxSpxHdr->hdr_ConnCtrl, + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2))); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SS %lx\n", pSpxConnFile)); + + // Copy remote address over into connection (socket could change) + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + + // Build ss ack, abort if we fail + SpxPktBuildSsAck( + pSpxConnFile, + &pSsAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT); + + if (pSsAckPkt == NULL) + { + fAbort = TRUE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SSACK %lx\n", + pSpxConnFile)); + + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + + // We dont queue in the pkt as its already marked as abort. + // Queue in the packet. + // SpxConnQueueSendPktTail(pSpxConnFile, pSsAckPkt); + + // Complete connect, this releases lock. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSsAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsAckPkt, pSendResd); + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (fAbort) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (cTimerCancelled) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine abort a connection (both client and server side) in the middle + of a connection establishment. + + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + PREQUEST pRequest = NULL; + int numDerefs = 0; + + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + KeBugCheck(0); + } +#endif + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { // others should be counted elsewhere + ++SpxDevice->dev_Stat.LocalResourceFailures; + } + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel all timers + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + + // We could be called from disconnect for an accept in which case there + // will be no queued request. But we need to remove the reference if there + // is no request (an accept/listen irp) and listen state is on. + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + // Save req in conn for deref to complete. + pSpxConnFile->scf_ConnectReq = pRequest; + + numDerefs++; + } + else if (SPX_CONN_LISTENING(pSpxConnFile)) + { + numDerefs++; + } + + // Bug #20999 + // Race condition was an abort came in from timer, but the connect state + // was left unchanged. Due to an extra ref on the connection from the + // aborted cr, the state remained so, and then the cr ack came in, and + // a session neg was built and queued on the connection. Although it should + // not have been. And we hit the assert in deref where the connection is + // being reinitialized. Since this can be called for both listening and + // connecting connections, do the below. + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine completes a connection (both client and server side) + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + DBGPRINT(CONNECT, INFO, + ("spxConnCompleteConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + DBGBRK(FATAL); + } +#endif + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel tdi connect timer if we are connecting. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (pSpxConnFile->scf_CRetryCount == (LONG)(PARAM(CONFIG_CONNECTION_COUNT))) { + ++SpxDevice->dev_Stat.ConnectionsAfterNoRetry; + } else { + ++SpxDevice->dev_Stat.ConnectionsAfterRetry; + } + + // Reset all connect related flags + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + break; + + case SPX_CONNFILE_LISTENING: + + if (pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + break; + + default: + + KeBugCheck(0); + + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_ACTIVE); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_IDLE); + + ++SpxDevice->dev_Stat.OpenConnections; + + // Initialize timer values + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = PARAM(CONFIG_INITIAL_RETRANSMIT_TIMEOUT); + pSpxConnFile->scf_DevT1 = 0; + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest) = 0; + + // When we complete the request, we essentially transfer the reference + // to the fact that the connection is active. This will be taken away + // when a Disconnect happens on the connection and we transition from + // ACTIVE to DISCONN. + // numDerefs++; + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + // Complete request + SpxCompleteRequest(pRequest); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +{ + PNDIS_PACKET pSsPkt, pCrAckPkt; + PSPX_SEND_RESD pSendResd; + + BOOLEAN fNeg = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG); + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + DBGPRINT(CONNECT, DBG, + ("spxConnAcceptCr: %lx.%d.%d\n", + pSpxConnFile, fSpx2, fNeg)); + + // Build and queue in packet. + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + fNeg, + fSpx2); + + if ((pCrAckPkt != NULL) && + (pSpxConnFile->scf_LocalConnId != 0)) + { + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + else + { + goto AbortConnect; + } + + + // Start the timer + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + else + { + goto AbortConnect; + } + + + // We start the connect timer for retrying ss which we send out now + // if we are not negotiating. + if (fSpx2) + { + // Build the session setup packet also for spx2. + SpxPktBuildSs( + pSpxConnFile, + &pSsPkt, + (USHORT)(fNeg ? 0 : SPX_SENDPKT_IPXOWNS)); + + if (pSsPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pSsPkt); + } + else + { + goto AbortConnect; + } + + if (!fNeg) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + goto AbortConnect; + } + } + } + + CTEAssert((fNeg && fSpx2) || (!fSpx2 && !fNeg)); + + // For a SPX connection, we immediately become active. This happens + // in the completeConnect routine. !!Dont change it here!! + if (!fSpx2) + { + spxConnCompleteConnect( + pSpxConnFile, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + } + else + { + SPX_LISTEN_SETSTATE( + pSpxConnFile, (fNeg ? SPX_LISTEN_SENTACK : SPX_LISTEN_SETUP)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + } + + // Send the CR Ack packet! + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + + if (fSpx2 && !fNeg) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + return(TRUE); + + +AbortConnect: + + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + + return (FALSE); +} + + + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + The caller needs to set the state to packetize before calling this + routine. This can be called when SEND state is RENEG also. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + + fNormalState - If true, it will assume it can release lock to send, + else, it just builds pkts without releasing lock and + releases lock at end. Used after reneg changes size. + +Return Value: + + +--*/ +{ + PLIST_ENTRY p; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT windowSize; + ULONG dataLen; + USHORT sendFlags; + int numDerefs = 0; + BOOLEAN fFirstPass = TRUE, fSuccess = TRUE; + PREQUEST pRequest; + +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + fNormalState) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + // Build all of the packets. The firsttime flag is used so + // that if we get a 0 byte send, we will send it. The firsttime + // flag will be set and we will build the packet and send it. + // + // FOR SPX1, we cannot trust the remote window size. So we only send + // stuff if window size is greater than 0 *AND* we do not have any pending + // sends. Dont get in here if we are ABORT. Dont want to be handling any + // more requests. + while((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ABORT) && + ((pRequest = pSpxConnFile->scf_ReqPkt) != NULL) && + ((pSpxConnFile->scf_ReqPktSize > 0) || fFirstPass)) + { + fFirstPass = FALSE; + windowSize = pSpxConnFile->scf_RecdAllocNum - + pSpxConnFile->scf_SendSeqNum + 1; + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: WINDOW %lx for %lx\n", + windowSize, pSpxConnFile)); + + + DBGPRINT(SEND, DBG, + ("REMALLOC %lx SENDSEQ %lx\n", + pSpxConnFile->scf_RecdAllocNum, + pSpxConnFile->scf_SendSeqNum)); + + + CTEAssert(windowSize >= 0); + + // Disconnect/Orderly release is not subject to window closure. + if ((pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) && + ((windowSize == 0) || + (!SPX2_CONN(pSpxConnFile) && + (pSpxConnFile->scf_SendSeqListHead != NULL)))) + { + break; + } + + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) + { + CTEAssert(pRequest == pSpxConnFile->scf_ReqPkt); + + // Get data length + dataLen = (ULONG)MIN(pSpxConnFile->scf_ReqPktSize, + (pSpxConnFile->scf_MaxPktSize - + ((SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)))); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: %lx Sending %lx Size %lx Req %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + dataLen, + pSpxConnFile->scf_ReqPkt, + pSpxConnFile->scf_ReqPktSize)); + + // Build data packet. Handles 0-length for data. Puts in seq num in + // send resd section of packet also. + sendFlags = + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | + SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | + ((!SPX2_CONN(pSpxConnFile) || (windowSize == 1)) ? + SPX_SENDPKT_ACKREQ : 0)); + + if (dataLen == pSpxConnFile->scf_ReqPktSize) + { + // Last packet of send, ask for a ack. + sendFlags |= (SPX_SENDPKT_LASTPKT | SPX_SENDPKT_ACKREQ); + if ((pSpxConnFile->scf_ReqPktFlags & TDI_SEND_PARTIAL) == 0) + sendFlags |= SPX_SENDPKT_EOM; + } + + SpxPktBuildData( + pSpxConnFile, + &pPkt, + sendFlags, + (USHORT)dataLen); + } + else + { + dataLen = 0; + + DBGPRINT(SEND, DBG, + ("Building DISC packet on %lx ReqPktSize %lx\n", + pSpxConnFile, pSpxConnFile->scf_ReqPktSize)); + + // Build informed disc/orderly rel packet, associate with request + SpxPktBuildDisc( + pSpxConnFile, + pRequest, + &pPkt, + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | SPX_SENDPKT_LASTPKT), + (UCHAR)((pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) ? + SPX2_DT_ORDREL : SPX2_DT_IDISC)); + } + + if (pPkt != NULL) + { + // If we were waiting to send an ack, we don't have to as we are + // piggybacking it now. Cancel ack timer, get out. + if (fNormalState && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Piggyback happening for %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // We are sending data, allow piggybacks to happen. + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + if (SpxTimerCancelEvent(pSpxConnFile->scf_ATimerId, FALSE)) + { + numDerefs++; + } + } + + if (pSpxConnFile->scf_ReqPktType != SPX_REQ_DATA) + { + // For a disconnect set the state + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + numDerefs++; + } + else if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_ORDREL) + { + CTEAssert((SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_ACTIVE) || + (SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_DISCONN)); + + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + } + else + { + CTEAssert( + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)); + } + } + else + { + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + CTEAssert(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC); + + // Note we have send the idisc here. + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_IDISC); + } + } + } + else + { + fSuccess = FALSE; + break; + } + + + // Queue in packet, reference request for the packet + SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt); + REQUEST_INFORMATION(pRequest)++; + + pSpxConnFile->scf_ReqPktSize -= dataLen; + pSpxConnFile->scf_ReqPktOffset += dataLen; + + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx Size after pkt %lx.%lx\n", + pSpxConnFile->scf_ReqPkt, pSpxConnFile->scf_ReqPktSize, + dataLen)); + + // Even if window size if zero, setup next request is current one + // is done. We are here only after we have packetized this send req. + if (pSpxConnFile->scf_ReqPktSize == 0) + { + // This request has been fully packetized. Either go on to + // next request or we are done packetizing. + p = REQUEST_LINKAGE(pRequest); + if (p->Flink == &pSpxConnFile->scf_ReqLinkage) + { + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx done, no more\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktOffset = 0; + pRequest = NULL; + } + else + { + pRequest = LIST_ENTRY_TO_REQUEST(p->Flink); + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Req done, setting next %lx.%lx\n", + pRequest, pParam->SendLength)); + + DBGPRINT(SEND, INFO, + ("-%lx-\n", + pRequest)); + + // Set parameters in connection for another go. + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if ((pSpxConnFile->scf_ReqPktSize = pParam->SendLength) == 0) + { + // Another zero length send. + fFirstPass = TRUE; + } + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + fFirstPass = TRUE; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + } + } + + if (fNormalState) + { + // Send the packet if we are not at the reneg state + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + ++SpxDevice->dev_Stat.DataFramesSent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesSent, + dataLen); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if retry timer needs to be started. + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER))) + { + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(SEND, ERR, + ("SpxConnPacketize: Failed to start retry timer\n")); + + fSuccess = FALSE; + break; + } + } + } + + // Dont overwrite an error state. + if (((fNormalState) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE)) || + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) && + (pSpxConnFile->scf_SendSeqListHead == NULL))) + { + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, ERR, + ("COULD NOT PACKETIZE AFTER RENEG %lx\n", pSpxConnFile)); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(fSuccess); +} + + + + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_RECEIVE pParam; + NTSTATUS status = STATUS_PENDING; + + if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + pParam = (PTDI_REQUEST_KERNEL_RECEIVE)REQUEST_PARAMETERS(pRequest); + pSpxConnFile->scf_CurRecvReq = pRequest; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = pParam->ReceiveLength; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnQueueRecv: %lx.%lx\n", pRequest, pParam->ReceiveLength)); + + // Reference connection for this recv. + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + InsertTailList( + &pSpxConnFile->scf_RecvLinkage, + REQUEST_LINKAGE(pRequest)); + + // RECV irps have no creation references. + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + + // State to receive_posted if we are idle. + if (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_POSTED); + } + + return; +} + + + + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile + ) +{ + CTELockHandle lockHandleInter; + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + + InitializeListHead(&ReqList); + + DBGPRINT(RECEIVE, DBG, + ("spxConnCompletePended: PENDING RECV REQUESTS IN DONE LIST! %lx\n", + pSpxConnFile)); + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + p = pSpxConnFile->scf_RecvDoneLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + +#if DBG + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_RECEIVE) + { + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } + } +#endif + + SpxCompleteRequest(pRequest); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + // If we are not already in ack queue, queue ourselves in starting + // ack timer. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // First start ack timer. + if ((pSpxConnFile->scf_ATimerId = + SpxTimerScheduleEvent( + spxConnAckTimer, + 100, + pSpxConnFile)) != 0) + { + // Reference connection for timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + ++SpxDevice->dev_Stat.PiggybackAckQueued; + } + } + + return; +} + + + + + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendAck: ACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an ack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + FALSE, + 0); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendNack: NACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an nack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + TRUE, + NumToSend); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + CTELockHandle interLockHandle; + USHORT seqNum = 0, ackNum; + int numDerefs = 0; + BOOLEAN fLastPkt, lockHeld = TRUE, fAbort = FALSE, + fResetRetryTimer, fResendPkt = FALSE, fResetSendQueue = FALSE; + + if (pIpxSpxHdr != NULL) + { + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + // Ack numbers should already be set in connection! + if (SPX2_CONN(pSpxConnFile)) + { + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRYWD: + + // Did we receive an ack for pending data? If so, we goto + // idle and process the ack. + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RETRYWD %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + else + { + // Ok, we received an ack for our probe retry, goto + // renegotiate packet size. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + } + + break; + + case SPX_SEND_RENEG: + + // We better have a data packet in the list. + CTEAssert(pSpxConnFile->scf_SendSeqListHead); + +#if DBG + if ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx.%lx.%lx RENEGACK SEQNUM %lx ACKNUM %lx EXPSEQ %lx\n", + pSpxConnFile, + pIpxSpxHdr->hdr_ConnCtrl, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT), + seqNum, + ackNum, + (pSpxConnFile->scf_SendSeqListHead->sr_SeqNum + 1))); + } +#endif + + // Verify we received an RR ack. If so, we set state to + // SEND_RETRY3. First repacketize if we need to. + if ((SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT)) && + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2))) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG! NEW %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + // Dont allow anymore reneg packet acks to be looked at. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Also set the new send sequence number. + pSpxConnFile->scf_SendSeqNum = + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1); + + // Get the max packet size we will really use. Retry timer + // could have sent other sizes by now, so we can't depend + // on whats set. + // Remember max packet size in connection. + GETSHORT2SHORT( + &pSpxConnFile->scf_MaxPktSize, &pIpxSpxHdr->hdr_NegSize); + + // Basic sanity checking on the max packet size. + if (pSpxConnFile->scf_MaxPktSize < SPX_NEG_MIN) + pSpxConnFile->scf_MaxPktSize = SPX_NEG_MIN; + + // Get ready to reset the send queue. + fResetSendQueue = TRUE; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG DONE : RETRY3 %lx.%lx MP %lx!\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + else + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: DUPLICATE RENEG ACK %lx!\n", + pSpxConnFile)); + } + + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Data acked %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + +#if DBG + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: CONN RESTORED %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + } +#endif + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + break; + + case SPX_SEND_WD: + + // Ok, we received an ack for our watchdog. Done. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + break; + + default: + + break; + } + +#if DBG + if (seqNum != 0) + { + // We have a nack, which contains an implicit ack. + // Instead of nack processing, what we do is we resend a + // packet left unacked after ack processing. ONLY if we + // either enter the loop below (fResetRetryTimer is FALSE) + // or if seqNum is non-zero (SPX2 only NACK) + } +#endif + } + } + + // Once our numbers are updated, we check to see if any of our packets + // have been acked. + fResetRetryTimer = TRUE; + while (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) || + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) || + fResetSendQueue) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + // Reset retry timer + if (fResetRetryTimer) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_RTimerId, + TRUE); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + } + + fResetRetryTimer = FALSE; + } + + // Update the retry seq num. + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pRequest = pSendResd->sr_Request; + +#if DBG + if (fResetSendQueue) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RENEG %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(SEND, DBG, + ("%lx Acked\n", pSendResd->sr_SeqNum)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: %lx Seq %lx Acked Sr %lx Req %lx %lx.%lx\n", + pSpxConnFile, + pSendResd->sr_SeqNum, + pSendResd, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // If this packet is the last one comprising this request, remove request + // from queue. Calculate retry time. + fLastPkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_LASTPKT) != 0); + if ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) && + ((pSendResd->sr_State & SPX_SENDPKT_REXMIT) == 0) && + ((pSendResd->sr_SeqNum + 1) == pSpxConnFile->scf_RecdAckNum)) + { + LARGE_INTEGER li, ntTime; + int value; + + // This is the packet which is being acked. Adjust round trip + // timer. + li = pSendResd->sr_SentTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new time + SpxCalculateNewT1(pSpxConnFile, value); + } + } + + if (fLastPkt) + { + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Remove creation reference + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: LASTSEQ # %lx for Req %lx with %lx.%lx\n", + pSendResd->sr_SeqNum, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + } + + // Dequeue the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0); + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Dereference request for the dequeing of the packet + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Packet owned by IPX. What do we do now? Set acked pkt so request + // gets dereferenced in send completion. Note that the packet is already + // off the queue and is floating at this point. + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: IPXOWNS Pkt %lx with %lx.%lx\n", + pPkt, pRequest, REQUEST_STATUS(pRequest))); + + pSendResd->sr_State |= SPX_SENDPKT_ACKEDPKT; + } + + if (SPX2_CONN(pSpxConnFile) && + (REQUEST_MINOR_FUNCTION(pRequest) == TDI_DISCONNECT) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ORDREL_ACKED); + + // If we had received an ordrel in the meantime, we need + // to disconnect. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + fAbort = TRUE; + } + } + + // All packets comprising a request have been acked! + if (REQUEST_INFORMATION(pRequest) == 0) + { + CTELockHandle lockHandleInter; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + + } + } +#if DBG + else if (fLastPkt) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: ReqFloating %lx.%lx\n", + pSpxConnFile, pRequest)); + } +#endif + } + + // See if we reset the send queue and repacketize. + if (fResetSendQueue) + { + // Reset send queue and repacketize only if pkts left unacked. + if (pSpxConnFile->scf_SendSeqListHead) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resetting send queue %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + spxConnResetSendQueue(pSpxConnFile); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Repacketizing %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SpxConnPacketize(pSpxConnFile, FALSE, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + else + { + // We just go back to idle state now. + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: All packets acked reneg ack! %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + } + } + + // See if we resend a packet. + if ((seqNum != 0) && + !fAbort && + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)) + { + PIPXSPX_HDR pSendHdr; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + } + + // Push into packetize only if we received an ack. And if there arent any + // packets already waiting. Probably retransmit happening. + if (!fAbort && + SPX_CONN_ACTIVE(pSpxConnFile) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_ReqPkt != NULL) && + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_PKTQ)) && + ((pSpxConnFile->scf_SendSeqListHead) == NULL) && + (!SPX2_CONN(pSpxConnFile) || + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL)))) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: Recd ack pktizng\n", pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + SpxConnFileLockReference(pSpxConnFile, CFREF_PKTIZE); + + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + else if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(TRUE); +} + + + + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + USHORT seqNum, ackNum, allocNum, maxPktSize; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + // The remote sent us a renegotiate request. We need to send an ack back + // ONLY if we have not acked a data packet with that same sequence number. + // This is guaranteed by the fact that we will not accept the reneg request + // if we have already acked a data packet with the same seq num, as our + // receive seq number would be incremented already. + // + // Note that if we have pending send packets we may end up doing a reneg + // also. + + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&maxPktSize, &pIpxSpxHdr->hdr_PktLen); + + // If the received seq num is less than the expected receive sequence number + // we ignore this request. + if (!UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + (seqNum != pSpxConnFile->scf_RecvSeqNum)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx ERROR RENSEQ %lx RECVSEQ %lx %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + return; + } + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RENSEQ %lx RECVSEQ %lx MAXPKT %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, maxPktSize)); + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + // Set RenegAckAckNum before calling buildrrack. If a previous reneg + // request was received with a greater maxpktsize, send an ack with + // that maxpktsize. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD)) + { + pSpxConnFile->scf_RenegAckAckNum = pSpxConnFile->scf_RecvSeqNum; + pSpxConnFile->scf_RenegMaxPktSize= maxPktSize; + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + + // Adjust sentallocnum now that recvseqnum might have moved up. + pSpxConnFile->scf_SentAllocNum += + (seqNum - pSpxConnFile->scf_RenegAckAckNum); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM ADJUSTED %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + } + + // The recvseqnum for the reneg is always >= the renegackacknum. + pSpxConnFile->scf_RecvSeqNum = seqNum; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RESET RECVSEQ %lx SavedACKACK %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RenegAckAckNum)); + + // Build and send an ack. + SpxPktBuildRrAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + pSpxConnFile->scf_RenegMaxPktSize); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } +#if DBG + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendRenegReqAck: Could not allocate!\n")); + } +#endif + + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + int numDerefs = 0; + PNDIS_PACKET pPkt = NULL; + BOOLEAN lockHeld = TRUE, fAbort = FALSE; + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ORDREL_ACKED) + { + fAbort = TRUE; + } + + // Send an ack if one was asked for. And we are done with this pkt + // Update seq numbers and stuff. + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for this. Ordinary spx2 ack. + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // Get disconnect handler if we have one. And have not indicated + // abortive disconnect on this connection to afd. + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler =pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx=pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + // We abort any receives here if !fAbort else we abort conn. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + else + { + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle); + + lockHeld = FALSE; + } + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for the idisc. Need to modify data type + // and reset sys bit on ack. + // BUG #12344 - Fixing this led to the problem where we queue in + // the pkt below, but AbortSends could already have been called + // => this packet stays on queue without a ref, conn gets freed + // underneath, and in the sendcomplete we crash when this send + // completes. + // + // Fix is to setup this pkt as a aborted pkt to start with. + + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + PIPXSPX_HDR pSendHdr; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + pSendHdr->hdr_ConnCtrl &= ~SPX_CC_SYS; + pSendHdr->hdr_DataType = SPX2_DT_IDISC_ACK; + + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // We better not have any received pkts, we ignore disconnect + // pkts when that happens. + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + +#if DBG + if (pSpxConnFile->scf_SendSeqListHead != NULL) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA/DISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendListHead, + pSpxConnFile->scf_SendSeqListHead)); + } +#endif + + // Call abortive disconnect on connection. + + // + // [SA] bug #15249 + // This is an informed disconnect, hence pass DISCONNECT_RELEASE to AFD (TRUE in last param) + // + // + // We pass true only in the case of an SPX connection. SPX2 connections follow the + // exact semantics of Informed Disconnect. + // + if (!SPX2_CONN(pSpxConnFile)) { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + TRUE); + } else { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + CTEAssert(pSendResd != NULL); + + pRequest = pSendResd->sr_Request; + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = pSendResd->sr_Request; + pSpxConnFile->scf_ReqPktOffset = pSendResd->sr_Offset; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset SEND Req to %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest, pParam->SendLength)); + + // Set parameters in connection for another go. Size parameter is + // original size - offset at this point. + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength - + pSpxConnFile->scf_ReqPktOffset; + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset DISC Req to %lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest)); + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: Seq Num for %lx is now %lx\n", + pSpxConnFile, pSpxConnFile->scf_SendSeqNum)); + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + do + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + KeBugCheck(0); + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + } while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL); + + return; +} + + + + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Called to abort either a sequenced or a non-sequenced packet ONLY from + send completion. + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + if ((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) + { + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + // BUG #11626 - request is already removed from list. + // RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Free the packet + SpxPktSendRelease(pPkt); + SpxConnFileDereference(pSpxConnFile, CFREF_ABORTPKT); + + if (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + // First go through the non-seq pkt queue.Just set abort flag if owned by ipx + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) == 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt if its not already. + // We only do this check for the non-sequenced packets. + // BUG #12344 (see SpxRecvDiscPacket()) + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) == 0) + { + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + } + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + // If retry timer state is on, then we need to reset and deref. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + DBGPRINT(SEND, DBG1, + ("spxConnAbortSends: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // Remove creation references on all sends. + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + p = pSpxConnFile->scf_ReqLinkage.Flink; + while (p != &pSpxConnFile->scf_ReqLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on. Its complete or abort list for it. + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Aborting Send Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG1, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PBYTE pData; + ULONG dataLen; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + // Reset the current receive request values + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + // If we have any buffered data, abort it. + // Buffered data that is 0 bytes long (only eom) may not have a ndis + // buffer associated with it. + while ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) + { + if ((pSpxConnFile->scf_RecvListHead = pRecvResd->rr_Next) == NULL) + { + pSpxConnFile->scf_RecvListTail = NULL; + } + + pNdisPkt = (PNDIS_PACKET) + CONTAINING_RECORD(pRecvResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(RECEIVE, DBG1, + ("spxConnAbortRecvs: %lx in bufferlist on %lx\n", + pSpxConnFile, pNdisPkt)); + + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + SpxFreeMemory(pData); + NdisFreeBuffer(pNdisBuffer); + } + + // Packet consumed. Free it up. + numDerefs++; + + // Free the ndis packet + SpxPktRecvRelease(pNdisPkt); + } + + // If packets are on this queue, they are waiting for transfer data to + // complete. Can't do much about that, just go and remove creation refs + // on the receives. + if (!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + p = pSpxConnFile->scf_RecvLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(RECEIVE, DBG1, + ("SpxRecvAbort: Aborting Recv Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxRecvAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + numDerefs++; + + SpxCompleteRequest(pRequest); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +#if 0 + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT startSeqNum; + BOOLEAN fLockHeld = TRUE, fDone = FALSE; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + if (pSendResd) + { + startSeqNum = pSendResd->sr_SeqNum; + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: StartSeqNum %lx for resend on %lx\n", + startSeqNum, pSpxConnFile)); + + while (spxConnGetPktBySeqNum(pSpxConnFile, startSeqNum++, &pPkt)) + { + CTEAssert(pPkt != NULL); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if (!(pSendResd->sr_State & SPX_SENDPKT_IPXOWNS)) + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx resent on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + + // We are going to send this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_REXMIT); + } + else + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx owned by ipx on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + break; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + fLockHeld = FALSE; + + // If pkt has the ack bit set, we break. + fDone = ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) != 0); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + if (fDone) + { + break; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fLockHeld = TRUE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return; +} +#endif diff --git a/private/ntos/tdi/isnp/spx/spxcutil.c b/private/ntos/tdi/isnp/spx/spxcutil.c new file mode 100644 index 000000000..3f85b2281 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcutil.c @@ -0,0 +1,1738 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcutil.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXCUTIL + +// +// Minor utility routines +// + + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + int i; + + // We go thru table and see if this is the minimum size or if it + // can go down further. Return true if it is not the minimum size. + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[0])); + + if ((ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE) <= SpxMaxPktSize[0]) + return(FALSE); + + for (i = SpxMaxPktSizeIndex-1; i > 0; i--) + { + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[i])); + + if (SpxMaxPktSize[i] < (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE)) + break; + } + + *pNegSize = (USHORT)(SpxMaxPktSize[i] + MIN_IPXSPX2_HDRSIZE); + + DBGPRINT(CONNECT, ERR, + ("spxConnCheckNegSize: Trying Size %lx Min size possible %lx\n", + *pNegSize, SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)); + + return(TRUE); +} + + + + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + UINT bufCount; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + + CTEAssert(Size > 0); + NdisQueryPacket(pPkt, NULL, &bufCount, &pNdisBuffer, NULL); + CTEAssert (bufCount == 3); + + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisAdjustBufferLength(pNdisBuffer, Size); + + // Change it in send reserved + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Len = (Size + MIN_IPXSPX2_HDRSIZE); + + // Change in ipx header + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + PUTSHORT2SHORT((PUSHORT)&pIpxSpxHdr->hdr_PktLen, (Size + MIN_IPXSPX2_HDRSIZE)); + + // Change in the neg packet field of the header. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + (Size + MIN_IPXSPX2_HDRSIZE)); + + DBGPRINT(CONNECT, DBG, + ("spxConnSetNegSize: Setting size to %lx Hdr %lx\n", + Size, (Size + MIN_IPXSPX2_HDRSIZE))); + + return; +} + + + + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, pListHead, pListTail; + PSPX_SEND_RESD pSendResd; + BOOLEAN removed = TRUE; + + // If we are sequenced or not decides which list we choose. + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pListHead = pSpxConnFile->scf_SendSeqListHead; + pListTail = pSpxConnFile->scf_SendSeqListTail; + } + else + { + pListHead = pSpxConnFile->scf_SendListHead; + pListTail = pSpxConnFile->scf_SendListTail; + } + + // Most often, we will be at the head of the list. + if (pListHead == pSendResd) + { + if ((pListHead = pSendResd->sr_Next) == NULL) + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pSendResd)); + + pListTail = NULL; + } + } + else + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pSendResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->sr_Next == pSendResd) + { + if ((pSr->sr_Next = pSendResd->sr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->sr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pSpxConnFile->scf_SendSeqListHead = pListHead; + pSpxConnFile->scf_SendSeqListTail = pListTail; + } + else + { + pSpxConnFile->scf_SendListHead = pListHead; + pSpxConnFile->scf_SendListTail = pListTail; + } + } + + return(removed); +} + + + + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_RECV_RESD pSr, pListHead, pListTail; + PSPX_RECV_RESD pRecvResd; + BOOLEAN removed = TRUE; + + pRecvResd = (PSPX_RECV_RESD)(pPkt->ProtocolReserved); + pListHead = pSpxConnFile->scf_RecvListHead; + pListTail = pSpxConnFile->scf_RecvListTail; + + // Most often, we will be at the head of the list. + if (pListHead == pRecvResd) + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pRecvResd)); + + if ((pListHead = pRecvResd->rr_Next) == NULL) + { + pListTail = NULL; + } + } + else + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pRecvResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->rr_Next == pRecvResd) + { + if ((pSr->rr_Next = pRecvResd->rr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->rr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + pSpxConnFile->scf_RecvListHead = pListHead; + pSpxConnFile->scf_RecvListTail = pListTail; + } + + return(removed); +} + + + + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = (fSeqList ? + &pSpxConnFile->scf_SendSeqListHead : + &pSpxConnFile->scf_SendListHead); + + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_Type == PktType) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, INFO, + ("SpxConnFindByType: %lx.%lx.%d\n", pSr,*ppPkt, fSeqList)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = &pSpxConnFile->scf_SendSeqListHead; + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_SeqNum == SeqNum) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, DBG, + ("SpxConnFindBySeq: %lx.%lx.%d\n", pSr,*ppPkt, SeqNum)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +USHORT +spxConnGetId( + VOID + ) +/*++ + +Routine Description: + + This must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + BOOLEAN wrapped = FALSE; + USHORT startConnId, retConnId; + + startConnId = SpxDevice->dev_NextConnId; + + // Search the global active list. + do + { + if ((SpxDevice->dev_NextConnId >= startConnId) && wrapped) + { + retConnId = 0; + break; + } + + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + continue; + } + + // BUGBUG: Later this be a tree. + for (pSpxConnFile = SpxDevice->dev_GlobalActiveConnList[ + SpxDevice->dev_NextConnId & NUM_SPXCONN_HASH_MASK]; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_GlobalActiveNext) + { + if (pSpxConnFile->scf_LocalConnId == SpxDevice->dev_NextConnId) + { + break; + } + } + + // Increment for next time. + retConnId = SpxDevice->dev_NextConnId++; + + // Ensure we are still legal. We could return if connfile is null. + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + } + + if (pSpxConnFile != NULL) + { + continue; + } + + break; + + } while (TRUE); + + return(retConnId); +} + + + + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_Next; + break; + } + + ppRemConn = &pRemConn->scf_Next; + } + + if (pRemConn == NULL) + { + DBGBRK(FATAL); + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_AssocNext; + break; + } + + ppRemConn = &pRemConn->scf_AssocNext; + } + + if (pRemConn == NULL) + { + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + + // For now, its just a linear list. + pSpxConnFile->scf_GlobalActiveNext = + SpxDevice->dev_GlobalActiveConnList[index]; + + SpxDevice->dev_GlobalActiveConnList[index] = + pSpxConnFile; + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + PSPX_CONN_FILE pC, *ppC; + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + NTSTATUS status = STATUS_SUCCESS; + + // For now, its just a linear list. + for (ppC = &SpxDevice->dev_GlobalActiveConnList[index]; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalActiveNext; + break; + } + + ppC = &pC->scf_GlobalActiveNext; + } + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_GlobalNext = SpxGlobalConnList; + SpxGlobalConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_CONN_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalConnList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalNext; + break; + } + + ppC = &pC->scf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + + + +#if 0 + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_PktNext = SpxPktConnList; + SpxPktConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxPktConnList) != NULL) + { + SpxPktConnList = SpxPktConnList->scf_PktNext; + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + + + + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_ProcessRecvNext = SpxRecvConnList; + SpxRecvConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxRecvConnList) != NULL) + { + SpxRecvConnList = SpxRecvConnList->scf_ProcessRecvNext; + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromRecv: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + +#endif + + +// +// Reference/Dereference routines +// + + +#if DBG + +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFile + + + + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE CONNECTION LOCK HELD. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFileLock + +#endif + + + + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) + +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE DEVICE LOCK HELD!!! + + All active connections should be on the device active list. Later, + this data structure will be a tree, caching the last accessed + connection. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = + SpxDevice->dev_GlobalActiveConnList[ConnId & NUM_SPXCONN_HASH_MASK]; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_GlobalActiveNext) + { + if (pSpxChkConn->scf_LocalConnId == ConnId) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYID); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; + +} + + + + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!! + + Returns a referenced connection file with the associated context and + address file desired. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_Next) + { + if ((pSpxChkConn->scf_ConnCtx == Ctx) && + (pSpxChkConn->scf_AddrFile == pSpxAddrFile)) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYCTX); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; +} + + + + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + + try + { + if ((pConnFile->scf_Size == sizeof (SPX_CONN_FILE)) && + (pConnFile->scf_Type == SPX_CONNFILE_SIGNATURE)) + { + CTEGetLock (&pConnFile->scf_Lock, &LockHandle); + if (!SPX_CONN_FLAG(pConnFile, SPX_CONNFILE_CLOSING)) + { + SpxConnFileLockReference(pConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyConnFile: A %lx closing\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock (&pConnFile->scf_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxVerifyConnFile: AF %lx exception\n", pConnFile)); + + return GetExceptionCode(); + } + + return status; + +} // SpxVerifyConnFile + + + + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyConnectionFile to remove it from the system. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + BOOLEAN fDiscNotIndicated = FALSE; + BOOLEAN fIDiscFlag = FALSE; + BOOLEAN fSpx2; + + CTEAssert(pSpxConnFile->scf_RefCount > 0); + oldvalue = SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + (ULONG)-1, + &pSpxConnFile->scf_Lock); + + CTEAssert (oldvalue > 0); + if (oldvalue == 1) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + LIST_ENTRY discReqList, *p; + PREQUEST pDiscReq; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + PREQUEST pCloseReq = NULL, + pCleanupReq = NULL, + pConnectReq = NULL; + BOOLEAN fDisassoc = FALSE; + + InitializeListHead(&discReqList); + + // We may not be associated at this point. Note: When we are active we + // always have a reference. So its not like we execute this code very often. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (pSpxAddrFile) + { + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + //if (pSpxConnFile->scf_RefCount == 0) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&pSpxConnFile->scf_RefCount, 0, &pSpxConnFile->scf_Lock) == 0) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn is 0 %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // All pending requests on this connection are done. See if we + // need to complete the disconnect phase etc. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_DISCONN: + + // Disconnect is done. Move connection out of all the lists + // it is on, reset states etc. + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn being inactivated %lx\n", + pSpxConnFile)); + + // Time to complete disc requests if present. + // There could be multiple of them. + p = pSpxConnFile->scf_DiscLinkage.Flink; + while (p != &pSpxConnFile->scf_DiscLinkage) + { + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Disc on %lx.%lx\n", + pSpxConnFile, pDiscReq)); + + RemoveEntryList(REQUEST_LINKAGE(pDiscReq)); + + // Make sure Stacy is always wearing a particular + // kind of dress if she isn't already. + if (REQUEST_STATUS(pDiscReq) == STATUS_PENDING) + { + REQUEST_STATUS(pDiscReq) = STATUS_SUCCESS; + } + + InsertTailList( + &discReqList, + REQUEST_LINKAGE(pDiscReq)); + } + + // + // Note the state here, and check after the conn has been inactivated. + // + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) { + fDiscNotIndicated = TRUE; + } + + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC)) { + fIDiscFlag = TRUE; + } + + fSpx2 = (SPX2_CONN(pSpxConnFile)) ? TRUE : FALSE; + + // + // [SA] Bug #14655 + // Do not try to inactivate an already inactivated connection + // + + if (!(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + spxConnInactivate(pSpxConnFile); + } else { + // + // This is an SPXI connection which has got the local disconnect. + // Reset the flags now. + // + CTEAssert(!fDiscNotIndicated); + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_DISC_SETSTATE(pSpxConnFile, 0); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // [SA] If we were waiting for sends to be aborted and did not indicate this + // disconnect to AFD; and AFD did not call a disconnect on this connection, + // then call the disonnect handler now. + // + if (fDiscNotIndicated) { + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + ULONG discCode = 0; + + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) { + + // + // If this was an SPXI connection, the disconnect state is still + // DISCONN, so if this routine is re-entered, we need to prevent + // a re-indicate to AFD. + // Also, we need to wait for a local disconnect from AFD since + // we indicated a TDI_DISCONNECT_RELEASE. We bump up the ref count + // in this case. + // + if (!fSpx2) { + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) ); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + + if (fIDiscFlag) { + SpxConnFileLockReference(pSpxConnFile, CFREF_DISCWAITSPX); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + } + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + DBGPRINT(CONNECT, INFO, + ("spxDerefConnectionFile: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + if (fIDiscFlag) { + // + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + discCode = TDI_DISCONNECT_RELEASE; + } else { + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + discCode = TDI_DISCONNECT_ABORT; + } + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + discCode); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + } + } + + --SpxDevice->dev_Stat.OpenConnections; + + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + // Get connect/accept request if present. + pConnectReq = pSpxConnFile->scf_ConnectReq; + pSpxConnFile->scf_ConnectReq = NULL; + + spxConnInactivate(pSpxConnFile); + break; + + case SPX_CONNFILE_ACTIVE: + + KeBugCheck(0); + + default: + + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == 0); + break; + } + + // If stopping, disassociate from the address file. Complete + // cleanup request. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn stopping %lx\n", + pSpxConnFile)); + + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + SPX_CONN_RESETFLAG(pSpxConnFile,SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must + // be in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + + DBGPRINT(CREATE, INFO, + ("SpxConnDerefDisAssociate: %lx from addr file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + fDisassoc = TRUE; + } + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + } + + if (fDisassoc) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + + if (pConnectReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Connect on %lx req %lx\n", + pSpxConnFile, pConnectReq)); + + // Status will already be set in here. We should be here only if + // connect is being aborted. + SpxCompleteRequest(pConnectReq); + } + + while (!IsListEmpty(&discReqList)) + { + p = RemoveHeadList(&discReqList); + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(CONNECT, DBG, + ("SpxConnFileDeref: DISC REQ %lx.%lx Completing\n", + pSpxConnFile, pDiscReq)); + + SpxCompleteRequest(pDiscReq); + } + + if (pCleanupReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Cleanup complete %lx req %lx\n", + pSpxConnFile, pCleanupReq)); + + REQUEST_INFORMATION(pCleanupReq) = 0; + REQUEST_STATUS(pCleanupReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCleanupReq); + } + + if (pCloseReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Freed %lx close req %lx\n", + pSpxConnFile, pCloseReq)); + + CTEAssert(pSpxConnFile->scf_RefCount == 0); + + // Remove from the global list + if (!NT_SUCCESS(spxConnRemoveFromGlobalList(pSpxConnFile))) + { + KeBugCheck(0); + } + + // Free it up. + SpxFreeMemory (pSpxConnFile); + + REQUEST_INFORMATION(pCloseReq) = 0; + REQUEST_STATUS(pCloseReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCloseReq); + } + } + + return; + +} // SpxDerefConnectionFile + + + + +VOID +spxConnReInit( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + // Reinit all variables. + pSpxConnFile->scf_Flags2 = 0; + + pSpxConnFile->scf_GlobalActiveNext = NULL; + pSpxConnFile->scf_PktNext = NULL; + pSpxConnFile->scf_CRetryCount = 0; + pSpxConnFile->scf_WRetryCount = 0; + pSpxConnFile->scf_RRetryCount = 0; + pSpxConnFile->scf_RRetrySeqNum = 0; + + pSpxConnFile->scf_CTimerId = + pSpxConnFile->scf_RTimerId = + pSpxConnFile->scf_WTimerId = + pSpxConnFile->scf_TTimerId = + pSpxConnFile->scf_ATimerId = 0; + + pSpxConnFile->scf_LocalConnId = + pSpxConnFile->scf_SendSeqNum = + pSpxConnFile->scf_SentAllocNum = + pSpxConnFile->scf_RecvSeqNum = + pSpxConnFile->scf_RetrySeqNum = + pSpxConnFile->scf_RecdAckNum = + pSpxConnFile->scf_RemConnId = + pSpxConnFile->scf_RecdAllocNum = 0; + +#if DBG + // Initialize so we dont hit breakpoint on seq 0 + pSpxConnFile->scf_PktSeqNum = 0xFFFF; +#endif + + pSpxConnFile->scf_DataType = 0; + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListTail == NULL); + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + pSpxConnFile->scf_ReqPkt = NULL; + + return; +} + + + + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!! Called with dev/addr/connection lock held !!! + +Arguments: + + This gets us back to associate SAVING the state of the STOPPING and + CLOSING flags so that dereference can go ahead and finish those. + +Return Value: + + +--*/ +{ + BOOLEAN fStopping, fClosing, fAborting; + + fStopping = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + fClosing = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + + // + // [SA] Bug #14655 + // Save the disconnect states so that a proper error can be given in the case of + // a send after a remote disconnection. + // + + // + // Bug #17729 + // Dont retain these flags if a local disconnect has already occured. + // + + fAborting = (!SPX2_CONN(pSpxConnFile) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)); + +#if DBG + pSpxConnFile->scf_GhostFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_GhostFlags2 = pSpxConnFile->scf_Flags2; + pSpxConnFile->scf_GhostRefCount = pSpxConnFile->scf_RefCount; +#endif + + // Clear all flags, go back to the assoc state. Restore stop/close + pSpxConnFile->scf_Flags = SPX_CONNFILE_ASSOC; + SPX_CONN_SETFLAG(pSpxConnFile, + ((fStopping ? SPX_CONNFILE_STOPPING : 0) | + (fClosing ? SPX_CONNFILE_CLOSING : 0))); + + // + // [SA] bug #14655 + // In order to avoid a re-entry, mark connection as SPX_DISC_INACTIVATED + // + if (fAborting) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_INACTIVATED); + } + + // Remove connection from global list on device + if (!NT_SUCCESS(spxConnRemoveFromGlobalActiveList( + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Remove connection from active list on address + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxConnFile->scf_AddrFile->saf_Addr->sa_ActiveConnList, + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Put connection in inactive list on address + SPX_INSERT_ADDR_INACTIVE( + pSpxConnFile->scf_AddrFile->saf_Addr, + pSpxConnFile); + + spxConnReInit(pSpxConnFile); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxdev.c b/private/ntos/tdi/isnp/spx/spxdev.c new file mode 100644 index 000000000..431498686 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdev.c @@ -0,0 +1,242 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDEV + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitCreateDevice) +#pragma alloc_text(PAGE, SpxDestroyDevice) +#endif + + + + +VOID +SpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->dev_RefCount); + + CTEAssert (result >= 0); + + if (result == 0) + { + // Close binding to IPX + SpxUnbindFromIpx(); + + // Set unload event. + KeSetEvent(&SpxUnloadEvent, IO_NETWORK_INCREMENT, FALSE); + } + +} // SpxDerefDevice + + + + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE * DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG DeviceNameOffset; + + + DBGPRINT(DEVICE, INFO, + ("SpxInitCreateDevice - Create device %ws\n", DeviceName->Buffer)); + + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + DBGPRINT(DEVICE, ERR, ("IoCreateDevice failed\n")); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + Device = (PDEVICE)deviceObject; + + DBGPRINT(DEVICE, INFO, ("IoCreateDevice succeeded %lx\n", Device)); + + // Initialize our part of the device context. + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + DeviceNameOffset = sizeof(DEVICE); + + // Copy over the device name. + Device->dev_DeviceNameLen = DeviceName->Length + sizeof(WCHAR); + Device->dev_DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + + RtlCopyMemory( + Device->dev_DeviceName, + DeviceName->Buffer, + DeviceName->Length); + + Device->dev_DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // Initialize the reference count. + Device->dev_RefCount = 1; + +#if DBG + Device->dev_RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->dev_Signature1, "IDC1", 4); + RtlCopyMemory(Device->dev_Signature2, "IDC2", 4); +#endif + + // Set next conn id to be used. + Device->dev_NextConnId = (USHORT)SpxRandomNumber(); + if (Device->dev_NextConnId == 0xFFFF) + { + Device->dev_NextConnId = 1; + } + + DBGPRINT(DEVICE, ERR, + ("SpxInitCreateDevice: Start Conn Id %lx\n", Device->dev_NextConnId)); + + // Initialize the resource that guards address ACLs. + ExInitializeResource (&Device->dev_AddrResource); + + // initialize the various fields in the device context + CTEInitLock (&Device->dev_Interlock); + CTEInitLock (&Device->dev_Lock); + KeInitializeSpinLock (&Device->dev_StatInterlock); + KeInitializeSpinLock (&Device->dev_StatSpinLock); + + Device->dev_State = DEVICE_STATE_CLOSED; + Device->dev_Type = SPX_DEVICE_SIGNATURE; + Device->dev_Size = sizeof (DEVICE); + + Device->dev_Stat.Version = 0x100; + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} // SpxCreateDevice + + + + +VOID +SpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&Device->dev_AddrResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} // SpxDestroyDevice diff --git a/private/ntos/tdi/isnp/spx/spxdrvr.c b/private/ntos/tdi/isnp/spx/spxdrvr.c new file mode 100644 index 000000000..0e9935d1a --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdrvr.c @@ -0,0 +1,1008 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdrvr.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the SPX/SPXII module of the ISN transport. + +Author: + + Adam Barr (adamba) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDRVR + +// Forward declaration of various routines used in this module. + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath); + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject); + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SpxUnload) +#pragma alloc_text(PAGE, SpxDispatchOpenClose) +#pragma alloc_text(PAGE, SpxDispatch) +#pragma alloc_text(PAGE, SpxDeviceControl) +#pragma alloc_text(PAGE, SpxUnload) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the SPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + UNICODE_STRING deviceName; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN BoundToIpx = FALSE; + + // DBGBRK(FATAL); + + // Initialize the Common Transport Environment. + if (CTEInitialize() == 0) { + return (STATUS_UNSUCCESSFUL); + } + + // We have this #define'd. Ugh, but CONTAINING_RECORD has problem owise. + CTEAssert(NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); + + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + RtlInitUnicodeString(&deviceName, SPX_DEVICE_NAME); + status = SpxInitCreateDevice( + DriverObject, + &deviceName, + &SpxDevice); + + if (!NT_SUCCESS(status)) + { + return(status); + } + + do + { + CTEInitLock (&SpxGlobalInterlock); + CTEInitLock (&SpxGlobalQInterlock); + + // Initialize the unload event + KeInitializeEvent( + &SpxUnloadEvent, + NotificationEvent, + FALSE); + + // !!!The device is created at this point!!! + // Get information from the registry. + status = SpxInitGetConfiguration( + RegistryPath, + &SpxDevice->dev_ConfigInfo); + + if (!NT_SUCCESS(status)) + { + break; + } + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif _PNP_POWER + + // Bind to the IPX transport. + if (!NT_SUCCESS(status = SpxInitBindToIpx())) + { + // BUGBUG: Have ipx name here as second string + LOG_ERROR( + EVENT_TRANSPORT_BINDING_FAILED, + status, + NULL, + NULL, + 0); + + break; + } + + BoundToIpx = TRUE; + +#if !defined(_PNP_POWER) + // Initialize the timer system + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif !_PNP_POWER + + // Initialize the block manager + if (!NT_SUCCESS(status = SpxInitMemorySystem(SpxDevice))) + { + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + break; + } + + // Initialize the driver object with this driver's entry points. + DriverObject->MajorFunction [IRP_MJ_CREATE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] + = SpxDispatch; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] + = SpxDispatchInternal; + DriverObject->DriverUnload = SpxUnload; + + // Initialize the provider info + SpxQueryInitProviderInfo(&SpxDevice->dev_ProviderInfo); + SpxDevice->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + +#if !defined(_PNP_POWER) + // We are open now. + SpxDevice->dev_State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == SpxDevice->dev_State ) { + SpxDevice->dev_State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + } while (FALSE); + + if (!NT_SUCCESS(status) ) + { + // Delete the device and any associated resources created. + if( BoundToIpx ) { + SpxDerefDevice(SpxDevice); + } + SpxDestroyDevice(SpxDevice); + } + + return (status); +} + + + + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. The I/O system will not + call us until nobody above has ST open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + UNREFERENCED_PARAMETER (DriverObject); + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + + // Remove creation reference count on the IPX device object. + SpxDerefDevice(SpxDevice); + + // Wait on the unload event. + KeWaitForSingleObject( + &SpxUnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL); + + // Release the block memory stuff + SpxDeInitMemorySystem(SpxDevice); + SpxDestroyDevice(SpxDevice); + return; +} + + + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // Make sure status information is consistent every time. + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + + Status = SpxDeviceControl (DeviceObject, Irp); + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + // + // Complete the Irp here instead of below. + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } // major function switch + + /* Commented out and re-located to the default case above. + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + */ + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatch + +VOID +SpxAssignControlChannelId( + IN PDEVICE Device, + IN PIRP Request + ) +/*++ + +Routine Description: + + This routine is required to ensure that the Device lock (to protect the ControlChannelId in the Device) + is not taken in a pageable routine (SpxDispatchOpenClose). + + NOTE: SPX returns the ControlChannelId in the Request, but never uses it later when it comes down in a + close/cleanup. The CCID is a ULONG; in future, if we start using this field (as in IPX which uses these Ids + to determine lineup Irps to complete), then we may run out of numbers (since we monotonically increase the CCID); + though there is a low chance of that since we will probably run out of memory before that! Anyhow, if that + happens, one solution (used in IPX) is to make the CCID into a Large Integer and pack the values into the + REQUEST_OPEN_TYPE(Irp) too. + + +Arguments: + + Device - Pointer to the device object for this driver. + + Request - Pointer to the request packet representing the I/O request. + +Return Value: + + None. + +--*/ +{ + CTELockHandle LockHandle; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->dev_CcId); + ++Device->dev_CcId; + if (Device->dev_CcId == 0) { + Device->dev_CcId = 1; + } + + CTEFreeLock (&Device->dev_Lock, LockHandle); +} + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + BOOLEAN found; + PREQUEST Request; + UINT i; + PFILE_FULL_EA_INFORMATION openType; + CONNECTION_CONTEXT connCtx; + + +#if !defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = SpxAddrOpen (Device, Request); + break; + } + + // Connection? + found = TRUE; + + for (i=0;iEaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + if (openType->EaValueLength < sizeof(CONNECTION_CONTEXT)) + { + + DBGPRINT(CREATE, ERR, + ("Create: Context size %d\n", openType->EaValueLength)); + + Status = STATUS_EA_LIST_INCONSISTENT; + break; + } + + connCtx = + *((CONNECTION_CONTEXT UNALIGNED *) + &openType->EaName[openType->EaNameLength+1]); + + Status = SpxConnOpen( + Device, + connCtx, + Request); + + break; + } + + } else { + + // + // Takes a lock in a Pageable routine - call another (non-paged) function to do that. + // + SpxAssignControlChannelId(Device, Request); + + REQUEST_OPEN_TYPE(Request) = (PVOID)SPX_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileClose(Device, Request); + break; + + case TDI_CONNECTION_FILE: + Status = SpxConnClose(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileCleanup(Device, Request); + break; + + case TDI_CONNECTION_FILE: + + Status = SpxConnCleanup(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } // major function switch + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchOpenClose + + + + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // Convert the user call to the proper internal device call. + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + if (Status == STATUS_SUCCESS) { + + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + Status = SpxDispatchInternal (DeviceObject, Irp); + + // + // Return the proper error code here. If SpxDispatchInternal returns an error, + // then we used to map it to pending below; this is wrong since the client above + // us could wait for ever since the IO subsystem does not set the event if an + // error is returned and the Irp is not marked pending. + // + + // Status = STATUS_PENDING; + } else { + + DBGPRINT(TDI, DBG, + ("Unknown Tdi code in Irp: %lx\n", Irp)); + + // + // Complete the Irp.... + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + return Status; + +} // SpxDeviceControl + + + + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PREQUEST Request; + KIRQL oldIrql; + NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; + PDEVICE Device = (PDEVICE)DeviceObject; + + + if (Device->dev_State != DEVICE_STATE_OPEN) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Cancel irp + IoAcquireCancelSpinLock(&oldIrql); + if (!Irp->Cancel) + { + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)SpxTdiCancel); + } + IoReleaseCancelSpinLock(oldIrql); + + if (Irp->Cancel) + return STATUS_CANCELLED; + + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + switch (REQUEST_MINOR_FUNCTION(Request)) + { + case TDI_ACCEPT: + + Status = SpxConnAccept( + Device, + Request); + + break; + + case TDI_SET_EVENT_HANDLER: + + Status = SpxAddrSetEventHandler( + Device, + Request); + + break; + + case TDI_RECEIVE: + + Status = SpxConnRecv( + Device, + Request); + break; + + + case TDI_SEND: + + Status = SpxConnSend( + Device, + Request); + break; + + case TDI_ACTION: + + Status = SpxConnAction( + Device, + Request); + break; + + case TDI_ASSOCIATE_ADDRESS: + + Status = SpxConnAssociate( + Device, + Request); + + break; + + case TDI_DISASSOCIATE_ADDRESS: + + Status = SpxConnDisAssociate( + Device, + Request); + + break; + + case TDI_CONNECT: + + Status = SpxConnConnect( + Device, + Request); + + break; + + case TDI_DISCONNECT: + + Status = SpxConnDisconnect( + Device, + Request); + break; + + case TDI_LISTEN: + + Status = SpxConnListen( + Device, + Request); + break; + + case TDI_QUERY_INFORMATION: + + Status = SpxTdiQueryInformation( + Device, + Request); + + break; + + case TDI_SET_INFORMATION: + + Status = SpxTdiSetInformation( + Device, + Request); + + break; + + // Something we don't know about was submitted. + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (Status != STATUS_PENDING) + { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IoAcquireCancelSpinLock(&oldIrql); + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(oldIrql); + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchInternal + + + + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cancellation of IO requests + +Arguments: + + +Return Value: +--*/ +{ + PREQUEST Request; + PSPX_ADDR_FILE pSpxAddrFile; + PSPX_ADDR pSpxAddr; + PDEVICE Device = (PDEVICE)DeviceObject; + CTELockHandle connectIrql; + CTELockHandle TempIrql; + PSPX_CONN_FILE pSpxConnFile; + + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + return; + } + + DBGPRINT(TDI, ERR, + ("SpxTdiCancel: Cancel irp called %lx.%lx\n", + Irp, REQUEST_OPEN_CONTEXT(Request))); + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) + { + case TDI_CONNECTION_FILE: + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + CTEGetLock(&pSpxConnFile->scf_Lock, &connectIrql); + + // + // Swap the irql + // + TempIrql = connectIrql; + connectIrql = Irp->CancelIrql; + Irp->CancelIrql = TempIrql; + + IoReleaseCancelSpinLock (Irp->CancelIrql); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + // + // This releases the lock + // + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + connectIrql, + FALSE); // [SA] bug #15249 + } + } + +// SpxConnStop((PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request)); + break; + + case TDI_TRANSPORT_ADDRESS_FILE: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + pSpxAddr = pSpxAddrFile->saf_Addr; + SpxAddrFileStop(pSpxAddrFile, pSpxAddr); + break; + + default: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + break; + + } + +} diff --git a/private/ntos/tdi/isnp/spx/spxerror.c b/private/ntos/tdi/isnp/spx/spxerror.c new file mode 100644 index 000000000..7d2cc7444 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxerror.c @@ -0,0 +1,316 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxerror.c + +Abstract: + + This module contains code which provides error logging support. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXERROR + +LONG SpxLastRawDataLen = 0; +NTSTATUS SpxLastUniqueErrorCode = STATUS_SUCCESS; +NTSTATUS SpxLastNtStatusCode = STATUS_SUCCESS; +ULONG SpxLastErrorCount = 0; +LONG SpxLastErrorTime = 0; +BYTE SpxLastRawData[PORT_MAXIMUM_MESSAGE_LENGTH - \ + sizeof(IO_ERROR_LOG_PACKET)] = {0}; + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + + int insertionStringLength = 0; + + // Filter out events such that the same event recurring close together does not + // cause errorlog clogging. The scheme is - if the event is same as the last event + // and the elapsed time is > THRESHOLD and ERROR_CONSEQ_FREQ simulataneous errors + // have happened, then log it else skip + if ((UniqueErrorCode == SpxLastUniqueErrorCode) && + (NtStatusCode == SpxLastNtStatusCode)) + { + SpxLastErrorCount++; + if ((SpxLastRawDataLen == RawDataLen) && + (RtlEqualMemory(SpxLastRawData, RawDataBuf, RawDataLen)) && + ((SpxLastErrorCount % ERROR_CONSEQ_FREQ) != 0) && + ((SpxGetCurrentTime() - SpxLastErrorTime) < ERROR_CONSEQ_TIME)) + { + return(FALSE); + } + } + + SpxLastUniqueErrorCode = UniqueErrorCode; + SpxLastNtStatusCode = NtStatusCode; + SpxLastErrorCount = 0; + SpxLastErrorTime = SpxGetCurrentTime(); + if (RawDataLen != 0) + { + SpxLastRawDataLen = RawDataLen; + RtlCopyMemory( + SpxLastRawData, + RawDataBuf, + RawDataLen); + } + + return(TRUE); +} + + + + +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + if (!SpxFilterErrorLogEntry( + EVENT_TRANSPORT_RESOURCE_POOL, + STATUS_INSUFFICIENT_RESOURCES, + (PVOID)&BytesNeeded, + sizeof(BytesNeeded))) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->dev_DeviceNameLen + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + // Convert the error value into a buffer. + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) + { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + RtlCopyMemory( + StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + } +} + + + + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + RawDataBuf - The number of ULONGs of dump data. + + RawDataLen - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[4] = L"Spx"; + + if (!SpxFilterErrorLogEntry( + ErrorCode, + FinalStatus, + RawDataBuf, + RawDataLen)) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + EntrySize += (UCHAR)Device->dev_DeviceNameLen; + } + else + { + EntrySize += sizeof(DriverName); + } + + if (SecondString) + { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)RawDataLen; + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (RawDataLen != 0) + { + RtlCopyMemory(errorLogEntry->DumpData, RawDataBuf, RawDataLen); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + } + else + { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + + if (SecondString) + { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxmem.c b/private/ntos/tdi/isnp/spx/spxmem.c new file mode 100644 index 000000000..9cd400e5b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxmem.c @@ -0,0 +1,897 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.c + +Abstract: + + This module contains code which implements the memory allocation wrappers. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + Jameel Hyder (jameelh) Initial Version + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, SpxInitMemorySystem) +#pragma alloc_text( PAGE, SpxDeInitMemorySystem) +#endif + +// Define module number for event logging entries +#define FILENUM SPXMEM + +// Globals for this module +// Some block sizes (like NDISSEND/NDISRECV are filled in after binding with IPX) +USHORT spxBlkSize[NUM_BLKIDS] = // Size of each block + { + sizeof(BLK_HDR)+sizeof(TIMERLIST), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +USHORT spxChunkSize[NUM_BLKIDS] = // Size of each Chunk + { + 512-BC_OVERHEAD, // BLKID_TIMERLIST + 512-BC_OVERHEAD, // BLKID_NDISSEND + 512-BC_OVERHEAD // BLKID_NDISRECV + }; + + +// Filled in after binding with IPX +// Reference for below. +// ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ +// (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST +USHORT spxNumBlks[NUM_BLKIDS] = // Number of blocks per chunk + { + ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ + (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +CTELock spxBPLock[NUM_BLKIDS] = { 0 }; +PBLK_CHUNK spxBPHead[NUM_BLKIDS] = { 0 }; + + + + +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + !!! MUST BE CALLED AFTER BINDING TO IPX!!! + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i; + NDIS_STATUS ndisStatus; + + // Try to allocate the ndis buffer pool. + NdisAllocateBufferPool( + &ndisStatus, + &pSpxDevice->dev_NdisBufferPoolHandle, + 20); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + return(STATUS_INSUFFICIENT_RESOURCES); + + for (i = 0; i < NUM_BLKIDS; i++) + CTEInitLock (&spxBPLock[i]); + + // Set the sizes in the block id info arrays. + for (i = 0; i < NUM_BLKIDS; i++) + { + // BUGBUG: Do it. + switch (i) + { + case BLKID_NDISSEND: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_SEND_RESD) + + NDIS_PACKET_SIZE + + IpxMacHdrNeeded + + MIN_IPXSPX2_HDRSIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + case BLKID_NDISRECV: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_RECV_RESD) + + NDIS_PACKET_SIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + default: + + break; + } + + } + + SpxTimerScheduleEvent((TIMER_ROUTINE)spxBPAgePool, + BLOCK_POOL_TIMER, + NULL); +} + + + + +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i, j, NumBlksPerChunk; + PBLK_CHUNK pChunk, pFree; + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + for (pChunk = spxBPHead[i]; + pChunk != NULL; ) + { + DBGPRINT(RESOURCES, ERR, + ("SpxInitMemorySystem: Freeing %lx\n", pChunk)); + + CTEAssert (pChunk->bc_NumFrees == NumBlksPerChunk); + + if ((pChunk->bc_BlkId == BLKID_NDISSEND) || + (pChunk->bc_BlkId == BLKID_NDISRECV)) + { + PBLK_HDR pBlkHdr; + + // We need to free the Ndis stuff for these guys + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + +#ifdef SPX_OWN_PACKETS + // Only need to free the ndis buffer. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + + // + // Free the second MDL also + // + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } +#else + // Need to free both the packet and the buffer. + ppNdisPkt = (PNDIS_PACKET *)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + + NdisUnchainBufferAtFront(*ppNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } + NdisFreePacket(*ppNdisPkt); +#endif + } + } + pFree = pChunk; + pChunk = pChunk->bc_Next; + +#ifndef SPX_OWN_PACKETS + // Free the ndis packet pool in chunk + NdisFreePacketPool((NDIS_HANDLE)pFree->bc_ChunkCtx); +#endif + SpxFreeMemory(pFree); + } + } + + // Free up the ndis buffer pool + NdisFreeBufferPool( + pSpxDevice->dev_NdisBufferPoolHandle); + + return; +} + + + + +PVOID +SpxAllocMem( +#ifdef TRACK_MEMORY_USAGE + IN ULONG Size, + IN ULONG FileLine +#else + IN ULONG Size +#endif // TRACK_MEMORY_USAGE + ) +/*++ + +Routine Description: + + Allocate a block of non-paged memory. This is just a wrapper over ExAllocPool. + Allocation failures are error-logged. We always allocate a ULONG more than + the specified size to accomodate the size. This is used by SpxFreeMemory + to update the statistics. + +Arguments: + + +Return Value: + + +--*/ +{ + PBYTE pBuf; + BOOLEAN zeroed; + + // round up the size so that we can put a signature at the end + // that is on a LARGE_INTEGER boundary + zeroed = ((Size & ZEROED_MEMORY_TAG) == ZEROED_MEMORY_TAG); + + Size = QWORDSIZEBLOCK(Size & ~ZEROED_MEMORY_TAG); + + // Do the actual memory allocation. Allocate eight extra bytes so + // that we can store the size of the allocation for the free routine + // and still keep the buffer quadword aligned. + + if ((pBuf = ExAllocatePoolWithTag(NonPagedPool, Size + sizeof(LARGE_INTEGER) +#if DBG + + sizeof(ULONG) +#endif + ,SPX_TAG)) == NULL) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxAllocMemory: failed - size %lx\n", Size)); + + TMPLOGERR(); + return NULL; + } + + // Save the size of this block in the four extra bytes we allocated. + *((PULONG)pBuf) = (Size + sizeof(LARGE_INTEGER)); + + // Return a pointer to the memory after the size longword. + pBuf += sizeof(LARGE_INTEGER); + +#if DBG + *((PULONG)(pBuf+Size)) = SPX_MEMORY_SIGNATURE; + DBGPRINT(RESOURCES, INFO, + ("SpxAllocMemory: %lx Allocated %lx bytes @%lx\n", + *(PULONG)((PBYTE)(&Size) - sizeof(Size)), Size, pBuf)); +#endif + + SpxTrackMemoryUsage((PVOID)(pBuf - sizeof(LARGE_INTEGER)), TRUE, FileLine); + + if (zeroed) + RtlZeroMemory(pBuf, Size); + + return (pBuf); +} + + + + +VOID +SpxFreeMemory( + IN PVOID pBuf + ) +/*++ + +Routine Description: + + Free the block of memory allocated via SpxAllocMemory. This is + a wrapper around ExFreePool. + +Arguments: + + +Return Value: + + +--*/ +{ + PULONG pRealBuffer; + + // Get a pointer to the block allocated by ExAllocatePool -- + // we allocate a LARGE_INTEGER at the front. + pRealBuffer = ((PULONG)pBuf - 2); + + SpxTrackMemoryUsage(pRealBuffer, FALSE, 0); + +#if DBG + // Check the signature at the end + if (*(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) + != SPX_MEMORY_SIGNATURE) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxFreeMemory: Memory overrun on block %lx\n", pRealBuffer)); + + DBGBRK(FATAL); + } + + *(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) = 0; +#endif + +#if DBG + *pRealBuffer = 0; +#endif + + // Free the pool and return. + ExFreePool(pRealBuffer); +} + + + + +#ifdef TRACK_MEMORY_USAGE + +#define MAX_PTR_COUNT 4*1024 +#define MAX_MEM_USERS 512 +CTELock spxMemTrackLock = {0}; +CTELockHandle lockHandle = {0}; +struct +{ + PVOID mem_Ptr; + ULONG mem_FileLine; +} spxMemPtrs[MAX_PTR_COUNT] = {0}; + +struct +{ + ULONG mem_FL; + ULONG mem_Count; +} spxMemUsage[MAX_MEM_USERS] = {0}; + +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine + ) +/*++ + +Routine Description: + + Keep track of memory usage by storing and clearing away pointers as and + when they are allocated or freed. This helps in keeping track of memory + leaks. + +Arguments: + + +Return Value: + + +--*/ +{ + static int i = 0; + int j, k; + + CTEGetLock (&spxMemTrackLock, &lockHandle); + + if (Alloc) + { + for (j = 0; j < MAX_PTR_COUNT; i++, j++) + { + i = i & (MAX_PTR_COUNT-1); + if (spxMemPtrs[i].mem_Ptr == NULL) + { + spxMemPtrs[i].mem_Ptr = pMem; + spxMemPtrs[i++].mem_FileLine = FileLine; + break; + } + } + + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == FileLine) + { + spxMemUsage[k].mem_Count ++; + break; + } + } + if (k == MAX_MEM_USERS) + { + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == 0) + { + spxMemUsage[k].mem_FL = FileLine; + spxMemUsage[k].mem_Count = 1; + break; + } + } + } + if (k == MAX_MEM_USERS) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: Out of space on spxMemUsage !!!\n")); + + DBGBRK(FATAL); + } + } + else + { + for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--) + { + k = k & (MAX_PTR_COUNT-1); + if (spxMemPtrs[k].mem_Ptr == pMem) + { + spxMemPtrs[k].mem_Ptr = 0; + spxMemPtrs[k].mem_FileLine = 0; + break; + } + } + } + + CTEFreeLock (&spxMemTrackLock, lockHandle); + + if (j == MAX_PTR_COUNT) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: %s\n", Alloc ? "Table Full" : "Can't find")); + + DBGBRK(FATAL); + } +} + +#endif // TRACK_MEMORY_USAGE + + + + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Alloc a block of memory from the block pool package. This is written to speed up + operations where a lot of small fixed size allocations/frees happen. Going to + ExAllocPool() in these cases is expensive. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_HDR pBlk = NULL; + PBLK_CHUNK pChunk, *ppChunkHead; + USHORT BlkSize; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PNDIS_BUFFER pNdisIpxSpxBuffer; + + + CTEAssert (BlockId < NUM_BLKIDS); + + if (BlockId < NUM_BLKIDS) + { + BlkSize = spxBlkSize[BlockId]; + ppChunkHead = &spxBPHead[BlockId]; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = *ppChunkHead; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pChunk->bc_NumFrees > 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Found space in Chunk %lx\n", pChunk)); +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPHits); +#endif + break; + } + } + + if (pChunk == NULL) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Allocating a new chunk for Id %d\n", BlockId)); + +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPMisses); +#endif + pChunk = SpxAllocateMemory(spxChunkSize[BlockId]); + if (pChunk != NULL) + { + LONG i, j; + PBLK_HDR pBlkHdr; + USHORT NumBlksPerChunk; + + NumBlksPerChunk = spxNumBlks[BlockId]; + pChunk->bc_NumFrees = NumBlksPerChunk; + pChunk->bc_BlkId = BlockId; + pChunk->bc_FreeHead = (PBLK_HDR)((PBYTE)pChunk + sizeof(BLK_CHUNK)); + + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Initializing chunk %lx\n", pChunk)); + + // Initialize the blocks in the chunk + for (i = 0, pBlkHdr = pChunk->bc_FreeHead; + i < NumBlksPerChunk; + i++, pBlkHdr = pBlkHdr->bh_Next) + { + NDIS_STATUS ndisStatus; + + pBlkHdr->bh_Next = (PBLK_HDR)((PBYTE)pBlkHdr + BlkSize); + if (BlockId == BLKID_NDISSEND) + { + PBYTE pHdrMem; + +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + // Allocate a ndis buffer descriptor describing hdr memory + // and queue it in. + pHdrMem = (PBYTE)pNdisPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD); + + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem, + IpxMacHdrNeeded); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + + + NdisAllocateBuffer( + &ndisStatus, + &pNdisIpxSpxBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem + IpxMacHdrNeeded, + MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisIpxSpxBuffer); + + + + pSendResd = (PSPX_SEND_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + + // Initialize elements of the protocol reserved structure. + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = SPX_SENDPKT_IDLE; + } + else if (BlockId == BLKID_NDISRECV) + { +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + pRecvResd = (PSPX_RECV_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + // Initialize elements of the protocol reserved structure. + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = SPX_RECVPKT_IDLE; + } + } + + if (i != NumBlksPerChunk) + { + // This has to be a failure from Ndis for send blocks!!! + // Undo a bunch of stuff + CTEAssert (BlockId == BLKID_NDISSEND); + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < i; j++, pBlkHdr = pBlkHdr->bh_Next) + { + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisBuffer); + + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisIpxSpxBuffer); + + if (pNdisIpxSpxBuffer) + { + NdisFreeBuffer(pNdisIpxSpxBuffer); + } + } + + SpxFreeMemory(pChunk); + pChunk = NULL; + } + else + { + // Successfully initialized the chunk, link it in + pChunk->bc_Next = *ppChunkHead; + *ppChunkHead = pChunk; + } + } + } + + if (pChunk != NULL) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + DBGPRINT(RESOURCES, INFO, + ("SpxBPAllocBlock: Allocating a block out of chunk %lx(%d) for Id %d\n", + pChunk, pChunk->bc_NumFrees, BlockId)); + + pChunk->bc_NumFrees --; + pChunk->bc_Age = 0; // Reset age + pBlk = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlk->bh_Next; + pBlk->bh_pChunk = pChunk; + + // Skip the block header! + pBlk++; + } + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + } + + return pBlk; +} + + + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Return a block to its owning chunk. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk; + PBLK_HDR pBlkHdr = (PBLK_HDR)((PCHAR)pBlock - sizeof(BLK_HDR)); + CTELockHandle lockHandle; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = spxBPHead[BlockId]; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pBlkHdr->bh_pChunk == pChunk) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPFreeBlock: Returning Block %lx to chunk %lx for Id %d\n", + pBlkHdr, pChunk, BlockId)); + + CTEAssert (pChunk->bc_NumFrees < spxNumBlks[BlockId]); + pChunk->bc_NumFrees ++; + pBlkHdr->bh_Next = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlkHdr; + break; + } + } + CTEAssert ((pChunk != NULL) && (pChunk->bc_FreeHead == pBlkHdr)); + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + return; +} + + + + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + Age out the block pool of unused blocks + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk, *ppChunk, pFree = NULL; + LONG i, j, NumBlksPerChunk; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + + if (TimerShuttingDown) + { + return TIMER_DONT_REQUEUE; + } + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + CTEGetLock(&spxBPLock[i], &lockHandle); + + for (ppChunk = &spxBPHead[i]; + (pChunk = *ppChunk) != NULL; ) + { + if ((pChunk->bc_NumFrees == NumBlksPerChunk) && + (++(pChunk->bc_Age) >= MAX_BLOCK_POOL_AGE)) + { + DBGPRINT(SYSTEM, INFO, + ("spxBPAgePool: freeing Chunk %lx, Id %d\n", + pChunk, pChunk->bc_BlkId)); + + *ppChunk = pChunk->bc_Next; +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPAge); +#endif + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + PBLK_HDR pBlkHdr; + + // We need to free Ndis stuff for these guys + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + } + } + + SpxFreeMemory(pChunk); + } + else + { + ppChunk = &pChunk->bc_Next; + } + } + CTEFreeLock(&spxBPLock[i], lockHandle); + } + + return TIMER_REQUEUE_CUR_VALUE; +} diff --git a/private/ntos/tdi/isnp/spx/spxpkt.c b/private/ntos/tdi/isnp/spx/spxpkt.c new file mode 100644 index 000000000..5f472d8cd --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxpkt.c @@ -0,0 +1,1594 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.c + +Abstract: + + This module contains code that builds various spx packets. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXPKT + +VOID +SpxPktBuildCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + NOTE: If *ppPkt is NULL, we allocate a packet. If not, we just + recreate the data and don't update the packet's state. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + if (*ppPkt == NULL) { + + SpxAllocSendPacket(SpxDevice, &pCrPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + } else { + + pCrPkt = *ppPkt; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + NdisQueryPacket(pCrPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? (SPX_CC_SPX2 | SPX_CC_NEG) : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = 0xFFFF; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + // Initialize + + if (*ppPkt == NULL) { + + pSendResd = (PSPX_SEND_RESD)(pCrPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX_HDRSIZE; + + *ppPkt = pCrPkt; + } + + return; +} + + + + +VOID +SpxPktBuildCrAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrAckPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pCrAckPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrAckPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pCrAckPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | + (fSpx2 ? SPX_CC_SPX2 : 0) | + (fNeg ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnBuildCrAck: Spx2 packet size %d.%lx\n", + pSpxConnFile->scf_MaxPktSize)); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pCrAckPkt; + return; +} + + + +VOID +SpxPktBuildSn( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSn: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SN; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSnAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SNACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSs( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSs: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SS; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + } while (FALSE); + + return; +} + + + +VOID +SpxPktBuildSsAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SSACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRr( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + // For a renegotiate request, we use the sequence number of + // the first waiting data packet. Passed in. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + SeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_SeqNum = SeqNum; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRrAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + // For the RrAck, ack number will be the appropriate number + // for the last data packet received. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RenegAckAckNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + MaxPktSize); + + DBGPRINT(SEND, DBG3, + ("SpxPktBuildRrAck: SEQ %lx ACKNUM %lx ALLOCNUM %lx MAXPKT %lx\n", + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_RenegAckAckNum, + pSpxConnFile->scf_SentAllocNum, + MaxPktSize)); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDiscPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDiscPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDiscPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pDiscPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_ACK | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0) | + ((DataType == SPX2_DT_IDISC) ? 0 : SPX_CC_EOM)); + + pIpxSpxHdr->hdr_DataType = DataType; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDiscPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = + ((DataType == SPX2_DT_IDISC) ? SPX_TYPE_IDISC : SPX_TYPE_ORDREL); + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pRequest; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Offset = 0; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pDiscPkt; + } + + return; +} + + + + +VOID +SpxPktBuildProbe( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pProbe; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pProbe, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pProbe + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pProbe, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? SPX_CC_SPX2 : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_PROBE; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = NULL; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE + : MIN_IPXSPX_HDRSIZE); + + *ppPkt = pProbe; + } + + return; +} + + + + +VOID +SpxPktBuildData( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length + ) +/*++ + +Routine Description: + + Handles zero length sends. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDataPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDataPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Make a ndis buffer descriptor for the data if present. + if (Length > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_ReqPkt), + pSpxConnFile->scf_ReqPktOffset, + Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the send packet + SpxPktSendRelease(pDataPkt); + return; + } + + // Chain this in the packet + NdisChainBufferAtBack(pDataPkt, pNdisBuffer); + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDataPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + Length += hdrLen; + + NdisQueryPacket(pDataPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + Length, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (((State & SPX_SENDPKT_ACKREQ) ? SPX_CC_ACK : 0) | + ((State & SPX_SENDPKT_EOM) ? SPX_CC_EOM : 0) | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = (UCHAR)REQUEST_PARAMETERS(pSpxConnFile->scf_ReqPkt)->Others.Argument3; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDataPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = SPX_TYPE_DATA; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pSpxConnFile->scf_ReqPkt; + pSendResd->sr_Offset = pSpxConnFile->scf_ReqPktOffset; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = Length; + pSendResd->sr_HdrLen = hdrLen; + + if (State & SPX_SENDPKT_ACKREQ) + { + KeQuerySystemTime((PLARGE_INTEGER)&pSendResd->sr_SentTime); + } + + CTEAssert(pSendResd->sr_Len <= pSpxConnFile->scf_MaxPktSize); + *ppPkt = pDataPkt; + + // Ok, allocation succeeded. Increment send seq. + pSpxConnFile->scf_SendSeqNum++; + } + + return; +} + + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ) +/*++ + +Routine Description: + + Creates a TargetBufferChain from the SourceBufferChain. The copy begins at + the 'Offset' location in the source chain. It copies 'Length' bytes. It also + handles Length = 0. If we run out of source chain before copying length amount + of bytes or run out of memory to create any more buffers for the target chain, + we clean up the partial chain created so far. + +Arguments: + + Status - Status of the request. + TargetChain - Pointer to the allocated buffer descriptor. + PoolHandle - Handle that is used to specify the pool. + SourceChain - Pointer to the descriptor of the source memory. + Offset - The Offset in the sources memory from which the copy is to + begin + Length - Number of Bytes to copy. + +Return Value: + + None. + +--*/ +{ + UINT BytesBeforeCurBuffer = 0; + PNDIS_BUFFER CurBuffer = SourceChain; + UINT BytesLeft; + UINT AvailableBytes; + PNDIS_BUFFER NewNdisBuffer, StartTargetChain; + + CTEAssert( SourceChain ); + + // First of all find the source buffer that contains data that starts at + // Offset. + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( BytesBeforeCurBuffer + AvailableBytes <= Offset ) { + BytesBeforeCurBuffer += AvailableBytes; + CurBuffer = CurBuffer->Next; + if ( CurBuffer ) { + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } else { + break; + } + } + + if ( ! CurBuffer ) { + *Status = STATUS_UNSUCCESSFUL; + return; + } + + // + // Copy the first buffer. This takes care of Length = 0. + // + BytesLeft = Length; + + // + // ( Offset - BytesBeforeCurBuffer ) gives us the offset within this buffer. + // + + AvailableBytes -= ( Offset - BytesBeforeCurBuffer ); + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &NewNdisBuffer, + PoolHandle, + CurBuffer, + Offset - BytesBeforeCurBuffer, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + return; + } + + StartTargetChain = NewNdisBuffer; + BytesLeft -= AvailableBytes; + + // + // Did the first buffer have enough data. If so, we r done. + // + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + // + // Now follow the Mdl chain and copy more buffers. + // + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( CurBuffer ) { + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + PoolHandle, + CurBuffer, + 0, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + return; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + BytesLeft -= AvailableBytes; + + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } + + // + // Ran out of source chain. This should not happen. + // + + CTEAssert( FALSE ); + + // For Retail build we clean up anyways. + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + *Status = STATUS_UNSUCCESSFUL; + return; +} + + +VOID +SpxPktBuildAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, ERR, + ("SpxPktBuildAck: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + // Send where data came from + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | (fSpx2 ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + if (fBuildNack) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + NumToResend); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + // Put current send seq number in packet for spx1 + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = (fBuildNack ? SPX_TYPE_DATANACK : SPX_TYPE_DATAACK); + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pPkt; + return; +} + + + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt + ) +{ + ((PSPX_RECV_RESD)(pPkt->ProtocolReserved))->rr_State = SPX_RECVPKT_IDLE; + SpxFreeRecvPacket(SpxDevice, pPkt); + return; +} + + + + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt + ) +{ + PNDIS_BUFFER pBuf, pIpxSpxBuf, pFreeBuf; + UINT bufCount; + + CTEAssert((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_IPXOWNS) == 0); + + NdisQueryPacket(pPkt, NULL, &bufCount, &pBuf, NULL); + + // BufCount == 1 for only the header. That's ok, we just reset the length + // and free the packet to the buffer pools. Else we need to free user buffers + // before that. + + NdisUnchainBufferAtFront( + pPkt, + &pBuf); + + NdisUnchainBufferAtFront( + pPkt, + &pIpxSpxBuf); + + // + // Set the header length to the max. that can be needed. + // + NdisAdjustBufferLength(pIpxSpxBuf, MIN_IPXSPX2_HDRSIZE); + + while (bufCount-- > 2) + { + PBYTE pData; + ULONG dataLen; + + NdisUnchainBufferAtBack( + pPkt, + &pFreeBuf); + + // See if we free data associated with the buffer + if ((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_FREEDATA) != 0) + { + NdisQueryBuffer(pFreeBuf, &pData, &dataLen); + CTEAssert(pData != NULL); + SpxFreeMemory(pData); + } + + CTEAssert(pFreeBuf != NULL); + NdisFreeBuffer(pFreeBuf); + } + + NdisReinitializePacket(pPkt); + + // Initialize elements of the protocol reserved structure. + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Id = IDENTIFIER_SPX; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State = SPX_SENDPKT_IDLE; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved1= NULL; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved2= NULL; + + NdisChainBufferAtFront( + pPkt, + pBuf); + + NdisChainBufferAtBack( + pPkt, + pIpxSpxBuf); + + SpxFreeSendPacket(SpxDevice, pPkt); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxquery.c b/private/ntos/tdi/isnp/spx/spxquery.c new file mode 100644 index 000000000..047ecabe8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxquery.c @@ -0,0 +1,259 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Author: + + Adam Barr (adamba) Initial Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxQueryInitProviderInfo) +#endif + +// Define module number for event logging entries +#define FILENUM SPXQUERY + +// Useful macro to obtain the total length of an MDL chain. +#define SpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo + ) +{ + // Initialize to defaults first + RtlZeroMemory((PVOID)ProviderInfo, sizeof(TDI_PROVIDER_INFO)); + + ProviderInfo->Version = SPX_TDI_PROVIDERINFO_VERSION; + KeQuerySystemTime (&ProviderInfo->StartTime); + ProviderInfo->MinimumLookaheadData = SPX_PINFOMINMAXLOOKAHEAD; + ProviderInfo->MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + ProviderInfo->MaxSendSize = SPX_PINFOSENDSIZE; + ProviderInfo->ServiceFlags = SPX_PINFOSERVICEFLAGS; + return; +} + + + + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE AddressFile; + PSPX_CONN_FILE ConnectionFile; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS SpxAddress; + } AddressInfo; + + + + // what type of status do we want? + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) + { + case TDI_QUERY_CONNECTION_INFO: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADDRESS_INFO: + + // The caller wants the exact address value. + + ConnectionFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + status = SpxConnFileVerify(ConnectionFile); + + if (status == STATUS_SUCCESS) { + AddressFile = ConnectionFile->scf_AddrFile; + SpxConnFileDereference(ConnectionFile, CFREF_VERIFY); + } else { + AddressFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + } + + status = SpxAddrFileVerify(AddressFile); + + if (status == STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, INFO, + ("SpxTdiQuery: Net.Socket %lx.%lx\n", + *(PULONG)Device->dev_Network, + AddressFile->saf_Addr->sa_Socket)); + + AddressInfo.ActivityCount = 0; + (VOID)SpxBuildTdiAddress( + &AddressInfo.SpxAddress, + sizeof(TA_IPX_ADDRESS), + Device->dev_Network, + Device->dev_Node, + AddressFile->saf_Addr->sa_Socket); + + status = TdiCopyBufferToMdl( + &AddressInfo, + 0, + sizeof(AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + SpxAddrFileDereference(AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: { + BYTE socketType; + TDI_PROVIDER_INFO providerInfo = Device->dev_ProviderInfo; + + // + // The device name extension comes down in the Irp + // + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) { + DBGPRINT(RECEIVE, ERR, ("TDI_QUERY_PROVIDER_INFO: SpxUtilGetSocketType failed: %lx\n", status)); + return(status); + } + + // + // The Catapult folks had a problem where AFD was discarding buffered sends on the NT box when it got a + // local disconnect on SPX1. This was because the Orderly release flag was always set in the provider + // info. AFD queries this once per device type. We detect the device above and OR in the orderly release + // flag if this query came down on an SPX2 endpoint. + // This is to make sure that AFD follows the correct disconnect semantics for SPX1 and SPX2 (SPX1 does + // only abortive; SPX2 does both abortive and orderly). + // + // BUGBUG: this will still not solve the problem completely since a connection that starts off as an SPX2 + // one can still be negotiated to SPX1 if the remote supports only SPX1. + // + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) { + + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX2 socket\n")); + providerInfo.ServiceFlags |= TDI_SERVICE_ORDERLY_RELEASE; + } else { + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX1 socket\n")); + } + + status = TdiCopyBufferToMdl ( + &providerInfo, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + } + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->dev_Stat, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} // SpxTdiQueryInformation + + + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} // SpxTdiSetInformation + diff --git a/private/ntos/tdi/isnp/spx/spxrecv.c b/private/ntos/tdi/isnp/spx/spxrecv.c new file mode 100644 index 000000000..2408f25e8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxrecv.c @@ -0,0 +1,2837 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXRECV + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +{ + PIPXSPX_HDR pHdr; + + // We have a separate routine to process SYS packets. DATA packets are + // processed within this routine. + if (LookaheadBufferSize < MIN_IPXSPX_HDRSIZE) + { + DBGPRINT(RECEIVE, ERR, + ("SpxReceive: Invalid length %lx\n", LookaheadBufferSize)); + + return; + } + + ++SpxDevice->dev_Stat.PacketsReceived; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + if ((pHdr->hdr_ConnCtrl & SPX_CC_SYS) == 0) + { + // Check for data packets + if ((pHdr->hdr_DataType != SPX2_DT_ORDREL) && + (pHdr->hdr_DataType != SPX2_DT_IDISC) && + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK)) + { + // HANDLE DATA PACKET + SpxRecvDataPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + else + { + // The whole packet better be in the lookahead, else we ignore. + if (LookaheadBufferSize == PacketSize) + { + SpxRecvDiscPacket( + LookaheadBuffer, + RemoteAddress, + LookaheadBufferSize); + } + } + } + else + { + SpxRecvSysPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + + return; +} + + + + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + CTELockHandle lockHandle; + NTSTATUS status; + BOOLEAN fAck, fEom, fBuffered, fImmedAck, fLockHeld; + PNDIS_BUFFER pNdisBuffer; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: For %lx with status %lx\n", pNdisPkt, NdisStatus)); + + pRecvResd = RECV_RESD(pNdisPkt); + pSpxConnFile = pRecvResd->rr_ConnFile; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + + fEom = ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + fImmedAck = ((pRecvResd->rr_State & SPX_RECVPKT_IMMEDACK) != 0); + fBuffered = ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + fAck = ((pRecvResd->rr_State & SPX_RECVPKT_SENDACK) != 0); + + // Check if receive is done. If we remove the reference for this + // packet and it goes to zero, that means the receive was aborted. + // Move to the completion queue. + // If receive is filled up, then remove the creation reference + // i.e. just complete the receive at this point. + // There can be only one packet per receive, we dont support + // out of order reception. + + if (!fBuffered) + { + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert((pNdisBuffer != NULL) || (BytesTransferred == 0)); + + // BUG #11772 + // On MP-machines scf_CurRecvReq could be set to NULL. Get the req + // from the recv packet. + // pRequest = pSpxConnFile->scf_CurRecvReq; + // CTEAssert(pRequest == pRecvResd->rr_Request); + pRequest = pRecvResd->rr_Request; + + // Remove reference for this packet. + --(REQUEST_INFORMATION(pRequest)); + + if (NdisStatus == NDIS_STATUS_SUCCESS) + { + pSpxConnFile->scf_CurRecvOffset += BytesTransferred; + pSpxConnFile->scf_CurRecvSize -= BytesTransferred; + +#if DBG + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (BytesTransferred != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= BytesTransferred; + } + } +#endif + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom || + ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL))) + { + CTELockHandle lockHandleInter; + + // We are done with this receive. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + + status = STATUS_SUCCESS; + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + status = STATUS_RECEIVE_PARTIAL; + } + + if ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL)) + { + status = REQUEST_STATUS(pRequest); + } + + REQUEST_STATUS(pRequest) = status; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + } + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + else + { + // Buffered receive, queue it in if successful. + // BUG #18363 + // IF WE DISCONNECTED in the meantime, we need to just dump this + // packet. + if (SPX_CONN_ACTIVE(pSpxConnFile) && + (NdisStatus == NDIS_STATUS_SUCCESS)) + { + // Queue packet in connection. Reference connection for this. + SpxConnQueueRecvPktTail(pSpxConnFile, pNdisPkt); + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: Buffering: %lx Pkt %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, BytesTransferred, pRecvResd->rr_State)); + + // There could either be queued receives. (This could happen in + // a partial receive case. Or if a receive got queued in while we + // were processing this packet (Possible on MP)), or a packet was + // buffered while we were completing some receives + + CTEAssert(pSpxConnFile->scf_RecvListHead); + + if ((pSpxConnFile->scf_CurRecvReq != NULL) || + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)) + { + CTELockHandle interLockHandle; + + // Push this connection into a ProcessRecv queue which will be + // dealt with in receive completion. + + DBGPRINT(RECEIVE, DBG, + ("spxRecvTransferData: Queueing for recvp %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // Get the global q lock, push into recv list. + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + } + else + { + PBYTE pData; + ULONG dataLen; + + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + // Free the data, ndis buffer. + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + SpxFreeMemory(pData); + } + + // Dont send ack, set status to be failure so we free packet/buffer. + fAck = FALSE; + NdisStatus = NDIS_STATUS_FAILURE; + } + } + + END_PROCESS_PACKET( + pSpxConnFile, fBuffered, (NdisStatus == NDIS_STATUS_SUCCESS)); + + if (fAck) + { + // Rem ack addr should have been copied in receive. + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (!fBuffered || (NdisStatus != STATUS_SUCCESS)) + { + // Free the ndis packet/buffer + SpxPktRecvRelease(pNdisPkt); + } + + return; +} + + + + +VOID +SpxReceiveComplete( + IN USHORT NicId + ) + +{ + CTELockHandle lockHandleInter, lockHandle; + PREQUEST pRequest; + BOOLEAN fConnLockHeld, fInterlockHeld; + PSPX_CONN_FILE pSpxConnFile; + int numDerefs = 0; + + // See if any connections need recv processing. This will also take + // care of any acks opening up window so our sends go to the max. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + fInterlockHeld = TRUE; + + while ((pSpxConnFile = SpxRecvConnList.pcl_Head) != NULL) + { + // Reset for each connection + numDerefs = 0; + + if ((SpxRecvConnList.pcl_Head = pSpxConnFile->scf_ProcessRecvNext) == NULL) + SpxRecvConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_ProcessRecvNext = NULL; + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromRecv: %lx\n", pSpxConnFile)); + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + do + { + // Complete pending requests. + while (!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) + { + pRequest = + LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqDoneLinkage.Flink); + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert (REQUEST_MINOR_FUNCTION(pRequest) != TDI_RECEIVE); + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + + // Call process pkts if we have any packets or if any receives to + // complete. Note this will call even when there are no receives + // queued and the first packet has already been indicated. + if ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage) || + (pSpxConnFile->scf_RecvListHead != NULL))) + { + // We have the flag reference on the connection. + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + +#if DBG + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: RecvDone left %lx\n", + pSpxConnFile)); + } +#endif + + // Hmm. This check is rather expensive, and essentially we are doing + // it twice. Should look to see if this can be modified safely. + } while ((!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + ((!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + (SPX_RECVPKT_BUFFERING | SPX_RECVPKT_INDICATED)) == + SPX_RECVPKT_BUFFERING))))); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RECVQ); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_RECV, + CFREF_VERIFY); + + numDerefs++; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + + // First see if we need to packetize. + while ((pSpxConnFile = SpxPktConnList.pcl_Head) != NULL) + { + if ((SpxPktConnList.pcl_Head = pSpxConnFile->scf_PktNext) == NULL) + SpxPktConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_PktNext = NULL; + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fConnLockHeld = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("SpxReceiveComplete: Packetizing %lx\n", pSpxConnFile)); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + if (SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle)) + { + // Done. + fConnLockHeld = FALSE; + } + } + + if (fConnLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_PKTIZE); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + if (fInterlockHeld) + { + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + return; +} + + + + +// +// PACKET HANDLING ROUTINES +// + + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming system packet. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld = FALSE; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvSysPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + if ((pktLen == SPX_CR_PKTLEN) && + (destConnId == 0xFFFF) && + (pHdr->hdr_ConnCtrl & SPX_CC_CR)) + { + spxConnHandleConnReq( + pHdr, + pRemoteAddr); + + return; + } + + // + // [SA] Bug #14917 + // Some SPX SYS packets (no extended ack field) may come in with the SPX2 bit set. + // Make sure we don't discard these packets. + // + + // if ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && (pktLen < MIN_IPXSPX2_HDRSIZE)) + // { + // return; + // } + + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnSysPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Id %lx NOT FOUND\n", destConnId)); + return; + } + + do + { + + DBGPRINT(RECEIVE, INFO, + ("SpxConnSysPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // This could be one of many packets. Connection ack/Session negotiate/ + // Session setup, Data Ack, Probe/Ack, Renegotiate/Ack. We shunt + // off all the packets to different routines but process the data + // ack packets here. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAddr); + + lockHeld = TRUE; + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromSrv( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_LISTENING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromClient( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // NOTE: Our ack to a session setup might get dropped. + // But the SS Ack is similar to a normal SPX2 ack. + // We dont have to do anything special. + + // Received ack/nack/reneg/reneg ack/disc associated packet. + // Disc packets except ordrel ack have non-zero datastream type. + if ((pHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) + { + // We received a renegotiate packet. Ignore all ack values + // in a reneg req. + SpxConnProcessRenegReq(pSpxConnFile, pHdr, pRemoteAddr, lockHandle); + lockHeld = FALSE; + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + if (((pHdr->hdr_ConnCtrl & SPX_CC_ACK) == 0) && + (pHdr->hdr_DataType == 0)) + { + SpxConnProcessAck(pSpxConnFile, pHdr, lockHandle); + lockHeld = FALSE; + } + else + { + // Just process the numbers we got. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + lockHeld = FALSE; + } + + // If the remote wants us to send an ack, do it. + if (pHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // First copy the remote address in connection. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (!lockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + } + + SpxConnSendAck(pSpxConnFile, lockHandle); + lockHeld = FALSE; + break; + } + + break; + + default: + + // Ignore this packet. + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Ignoring packet, state is not active\n")); + break; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (LookaheadSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvDiscPacket: Packet Size %lx\n", + pktLen)); + + return; + } + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDiscPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDiscPacket: Id %lx NOT FOUND", destConnId)); + + return; + } + + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: Id %lx Conn %lx DiscType %lx\n", + destConnId, pSpxConnFile, pHdr->hdr_DataType)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + + // Unless we are in the active/disconnecting, but send state = idle + // and recv state = idle/recv posted, we ignore all disconnect packets. + if (((SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_ACTIVE) && + (SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_DISCONN)) || + ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_IDLE) && + (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_POSTED)) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx, %lx, %lx.%lx, %d.%d\n", + pSpxConnFile, + SPX_MAIN_STATE(pSpxConnFile), + SPX_SEND_STATE(pSpxConnFile), SPX_RECV_STATE(pSpxConnFile), + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)), + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)))); + + break; + } + + // If we have received a disconnect, process received ack to complete any + // pending sends before we allow the disconnect. This ack number will be + // the last word on this session. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + switch (pHdr->hdr_DataType) + { + case SPX2_DT_ORDREL: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: Recd ORDREl!\n")); + + // BUGBUG: Need to deal with all sthe states. + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // On receive, we do check the seq num for the orderly release, just + // like for a data packet. + // If this was not already indicated, indicate it now. That is all + // we do for an orderly release. When our client does a orderly rel + // and we receive the ack for that, call abortive with success. + + // Verify ord rel packet, this checks if seq nums match also. + if ((pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) || + (pHdr->hdr_DataType != SPX2_DT_ORDREL) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: OR Failed/Ignored %lx, %lx.%lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + lockHeld = FALSE; + } + + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessOrdRel(pSpxConnFile, lockHandle); + lockHeld = FALSE; + } + else + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + // Set flag in last recd buffer + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_ORD_DISC; + } + + break; + + case SPX2_DT_IDISC: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC %lx!\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: SEND %d. RECV %d.%lx!\n", + IsListEmpty(&pSpxConnFile->scf_ReqLinkage), + IsListEmpty(&pSpxConnFile->scf_RecvLinkage), + pSpxConnFile->scf_RecvDoneLinkage)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + !(pHdr->hdr_ConnCtrl & SPX_CC_ACK) || + (pHdr->hdr_DataType != SPX2_DT_IDISC) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:IDISC Ignored %lx.%lx.%lx.%lx\n", + pSpxConnFile, seqNum, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO RECV DATA IDISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessIDisc(pSpxConnFile, lockHandle); + + lockHeld = FALSE; + } + else + { + // Set flag in last recd buffer + + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_IDISC; + } + + break; + + case SPX2_DT_IDISC_ACK: + + // Done with informed disconnect. Call abort connection with + // status success. That completes the pending disconnect request + // with status_success. + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC ack!\n", pSpxConnFile)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:Ver idisc ack Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + break; + } + + // We should be in the right state to accept this. + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_IDISC)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + break; + + default: + + KeBugCheck(0); + } + + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvBufferPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT LookaheadOffset, + IN PIPXSPX_HDR pIpxSpxHdr, + IN UINT PacketSize, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PSPX_RECV_RESD pRecvResd; + ULONG bytesCopied; + BOOLEAN fEom; + NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; + PBYTE pData = NULL; + PNDIS_BUFFER pNdisBuffer = NULL; + + if (PacketSize > 0) + { + // Allocate memory for this data. + if (pData = (PBYTE)SpxAllocateMemory(PacketSize)) + { + // Describe memory with a ndis buffer descriptor. + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + PacketSize); + } + else + { + ndisStatus = NDIS_STATUS_RESOURCES; + } + } + + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Queue the buffer into the packet if there is one. + if (pNdisBuffer) + { + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_DataOffset= 0; + +#if DBG + // Store seq number + GETSHORT2SHORT(&pRecvResd->rr_SeqNum , &pIpxSpxHdr->hdr_SeqNum); +#endif + + pRecvResd->rr_State = + (SPX_RECVPKT_BUFFERING | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fEom ? SPX_RECVPKT_EOM : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + } + + pRecvResd->rr_Request = NULL; + pRecvResd->rr_ConnFile = pSpxConnFile; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvBufferPkt: %lx Len %lx DataPts %lx F %lx\n", + pSpxConnFile, PacketSize, pData, pRecvResd->rr_State)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Call ndis transfer data. Copy ENTIRE packet. copySize has + // been modified so use original values. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (PacketSize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadOffset, + PacketSize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + + // BUG: FDDI returns pending which screws us up here. Stupid bug + ndisStatus = NDIS_STATUS_SUCCESS; + } + } + + // ASSERT: Lock will be freed in the success case. + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvBufferPkt: FAILED!\n")); + + END_PROCESS_PACKET(pSpxConnFile, FALSE, FALSE); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + if (pData != NULL) + { + SpxFreeMemory(pData); + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + + return; +} + + + + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadSize, + IN UINT LookaheadOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + ULONG receiveFlags; + PSPX_CONN_FILE pSpxConnFile; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PIRP pRecvIrp; + ULONG bytesTaken, iOffset, copySize, bytesCopied; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + NDIS_STATUS ndisStatus; + PREQUEST pRequest = NULL; + BOOLEAN fEom, + fImmedAck = FALSE, fLockHeld = FALSE, fPktDone = FALSE; + + pIpxSpxHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pIpxSpxHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + // We keep and use the remote id in the net format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: packet received dest %lx src %lx seq %lx\n", + pIpxSpxHdr->hdr_DestSkt, pIpxSpxHdr->hdr_SrcSkt, seqNum)); + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen < MIN_IPXSPX2_HDRSIZE)) + { + return; + } + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: Id %lx NOT FOUND", destConnId)); + return; + } + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + +#if 0 + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); +#endif + + fLockHeld = TRUE; + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + // Verify data packet, this checks if seq nums match also. + if ((pIpxSpxHdr->hdr_SrcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + !((pktLen >= MIN_IPXSPX_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen >= MIN_IPXSPX2_HDRSIZE)))) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + ++SpxDevice->dev_Stat.DataFramesRejected; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesRejected, + pktLen - (SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + + // + // Bug #16975: Set the remote ack addr for use in SpxConnSendAck() + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + + break; + } + + // If we have received an orderly release, we accept no more data + // packets. + if (SPX_CONN_FLAG( + pSpxConnFile, + (SPX_CONNFILE_IND_IDISC | + SPX_CONNFILE_IND_ODISC)) + + || + + ((pSpxConnFile->scf_RecvListTail != NULL) && + ((pSpxConnFile->scf_RecvListTail->rr_State & + SPX_RECVPKT_DISCMASK) != 0))) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDataPacket: After ord rel %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // We are processing a packet OR a receive is about to complete. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)) + { + BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum); + } + else + { + // Already processing a packet. Or a receive is waiting to + // complete. Get out. + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + iOffset = MIN_IPXSPX2_HDRSIZE; + if (!SPX2_CONN(pSpxConnFile)) + { + iOffset = 0; + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)) + { + iOffset = MIN_IPXSPX_HDRSIZE; + } + } + + copySize = pktLen - iOffset; + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + // Do we attempt to piggyback? If not, fImmedAck is true. + // For SPX1 we dont piggyback. + // Bug #18253 + fImmedAck = (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM) == 0)); + + // If we do not have EOM to indicate AND we are a zero-sized packet + // then just consume this packet. + if (!fEom && (copySize == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: ZERO LENGTH PACKET NO EOM %lx.%lx\n", + pSpxConnFile, seqNum)); + + fPktDone = TRUE; + break; + } + + receiveFlags = TDI_RECEIVE_NORMAL; + receiveFlags |= ((fEom ? TDI_RECEIVE_ENTIRE_MESSAGE : 0) | + (((MacOptions & + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0)); + + ++SpxDevice->dev_Stat.DataFramesReceived; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesReceived, + copySize); + + // Ok, we accept this packet. Depending on our state. + switch (SPX_RECV_STATE(pSpxConnFile)) + { + case SPX_RECV_PROCESS_PKTS: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: recv completions on %lx\n", + pSpxConnFile)); + + goto BufferPacket; + + case SPX_RECV_IDLE: + + // If recv q is non-empty we are buffering data. + // Also, if no receive handler goto buffer data. Also, if receives + // are being completed, buffer this packet. + if ((pSpxConnFile->scf_RecvListHead != NULL) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + !(pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: RecvListHead non-null %lx\n", + pSpxConnFile)); + + goto BufferPacket; + } + + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + + // Don't indicate this packet again. + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); + +#if DBG + CTEAssert(pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + + // + // Comment this out for Buf # 10394. we'r hitting this assert + // even when there was no data loss. + // + // CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != seqNum); + + pSpxConnFile->scf_PktSeqNum = seqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; + + pSpxConnFile->scf_IndBytes = copySize; + pSpxConnFile->scf_IndLine = __LINE__; + + +#endif + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + receiveFlags, + LookaheadSize - iOffset, + copySize, + &bytesTaken, + LookaheadBuffer + iOffset, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: IND Flags %lx.%lx ConnID %lx,\ + %lx Ctx %lx SEQ %lx Size %lx . %lx .%lx IND Status %lx\n", + pIpxSpxHdr->hdr_ConnCtrl, + receiveFlags, + destConnId, + pSpxConnFile, + pSpxConnFile->scf_ConnCtx, + seqNum, + LookaheadSize - iOffset, + copySize, + bytesTaken, + status)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: %x %x %x %x %x %x %x %x %x %x %x %x\n", + *(LookaheadBuffer+iOffset), + *(LookaheadBuffer+iOffset+1), + *(LookaheadBuffer+iOffset+2), + *(LookaheadBuffer+iOffset+3), + *(LookaheadBuffer+iOffset+4), + *(LookaheadBuffer+iOffset+5), + *(LookaheadBuffer+iOffset+6), + *(LookaheadBuffer+iOffset+7), + *(LookaheadBuffer+iOffset+8), + *(LookaheadBuffer+iOffset+9), + *(LookaheadBuffer+iOffset+10), + *(LookaheadBuffer+iOffset+11))); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. + CTEAssert((bytesTaken != 0) || fEom); + fPktDone = TRUE; + +#if DBG + // Set this to 0, since we just indicated, there could + // not have been other data. + pSpxConnFile->scf_IndBytes = 0; +#endif + + break; + } + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + break; + } + + // If there was indicated but not received data waiting + // (which in this path there will never be, the request + // could be completed given the data filled it up, and + // the lock released. + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + } + else if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + // Data was not accepted. Need to buffer data and + // reduce window. + goto BufferPacket; + } + + // Fall through to recv_posted. + } + else + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: !!!Ignoring %lx Seq %lx\n", + pSpxConnFile, + seqNum)); + + break; + } + + case SPX_RECV_POSTED: + + if (pSpxConnFile->scf_RecvListHead != NULL) + { + // This can happen also. Buffer packet if it does. + goto BufferPacket; + } + + // If a receive irp is posted, then process the receive irp. If + // we fell thru we MAY already will have an irp. + if (pRequest == NULL) + { + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_CurRecvReq != NULL); + pRequest = pSpxConnFile->scf_CurRecvReq; + } + + // Process receive. Here we do not need to worry about + // indicated yet not received data. We just deal with + // servicing the current packet. + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + if ((LookaheadSize == PacketSize) && + (pSpxConnFile->scf_CurRecvSize >= copySize)) + { + bytesCopied = 0; + status = STATUS_SUCCESS; + if (copySize > 0) + { + status = TdiCopyBufferToMdl( + LookaheadBuffer, + iOffset, + copySize, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + } + + DBGPRINT(RECEIVE, DBG, + ("BytesCopied %lx CopySize %lx, Recv Size %lx.%lx\n", + bytesCopied, copySize, + pSpxConnFile->scf_CurRecvSize, + pSpxConnFile->scf_CurRecvOffset)); + } + + // Update current request values and see if this request + // is to be completed. Either zero or fEom. + pSpxConnFile->scf_CurRecvOffset += bytesCopied; + pSpxConnFile->scf_CurRecvSize -= bytesCopied; + +#if DBG + // Decrement indicated data count + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + if (bytesCopied != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= bytesCopied; + } + } +#endif + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + CTELockHandle lockHandleInter; + + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest)= + pSpxConnFile->scf_CurRecvOffset; + + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnData: Completing recv %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + fPktDone = TRUE; + } + else + { + // Need to allocate a ndis receive packet for transfer + // data. + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: %lx.%lx Tranfer data needed!\n", + copySize, pSpxConnFile->scf_CurRecvSize)); + + if (copySize > pSpxConnFile->scf_CurRecvSize) + { + // Partial receive. Buffer and then deal with it. + goto BufferPacket; + } + + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Describe the receive irp's data with a ndis buffer + // descriptor. + if (copySize > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + copySize); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the recv packet + SpxPktRecvRelease(pNdisPkt); + break; + } + + // Queue the buffer into the packet + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + // Don't care about whether this is indicated or not here + // as it is not a buffering packet. + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = + ((fEom ? SPX_RECVPKT_EOM : 0) | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fImmedAck ? SPX_RECVPKT_IMMEDACK : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? + SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + } + + pRecvResd->rr_Request = pRequest; + pRecvResd->rr_ConnFile = pSpxConnFile; + + // reference receive request + REQUEST_INFORMATION(pRequest)++; + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + fLockHeld = FALSE; + + // Call ndis transfer data. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (copySize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + copySize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + } + + break; + + default: + + KeBugCheck(0); + break; + } + + break; + +BufferPacket: + + SpxRecvBufferPkt( + pSpxConnFile, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + pIpxSpxHdr, + copySize, + RemoteAddress, + lockHandle); + + fLockHeld = FALSE; + } + + } while (FALSE); + + // Here we process a received ack. + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // Send an ack if one was asked for. And we are done with this packet. + if (fPktDone) + { + END_PROCESS_PACKET(pSpxConnFile, FALSE, TRUE); + } + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) && fPktDone) + { + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // First copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + PBYTE pData; + ULONG dataLen, copyLen; + BOOLEAN fLockHeld = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numPkts = 0, numDerefs = 0; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Flush %lx\n", + pSpxConnFile, BytesToFlush)); + + while (((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + ((BytesToFlush > 0) || + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0))) + { + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + if ((BytesToFlush == 0) && (dataLen != 0)) + { + // Don't flush this packet. + break; + } + + // Allow for zero data, eom only packets. + copyLen = MIN((dataLen - pRecvResd->rr_DataOffset), BytesToFlush); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Pkt %lx DataLen %lx Copy %lx Flush %lx\n", + pSpxConnFile, pNdisPkt, dataLen, copyLen, BytesToFlush)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + BytesToFlush -= (ULONG)copyLen; + +#if DBG + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } +#endif + + if (pRecvResd->rr_DataOffset == dataLen) + { + // Packet consumed. Free it up. Check if disc happened. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + numDerefs++; + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: !!!ALL INDICATED on %lx.%lx.%lx.%lx\n", + pSpxConnFile, pNdisPkt, pNdisBuffer, pData)); + + INCREMENT_WINDOW(pSpxConnFile); + fWdwOpen = TRUE; + } + else + { + // Took only part of this packet. Get out. + break; + } + } + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxRecvFlushBytes: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN - Receive was queued => TRUE + +--*/ +{ + ULONG indicateFlags; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PREQUEST pRequest; + PIRP pRecvIrp; + ULONG bytesTaken, totalSize, bufSize; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PSPX_RECV_RESD pRecvResd; + NTSTATUS status; + PBYTE lookaheadData; + ULONG lookaheadSize; + BOOLEAN fLockHeld = TRUE, fRecvQueued = FALSE; + + + while ((pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) && + ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) == 0)) + { + // Once a receive is queued we better get out. + CTEAssert(!fRecvQueued); + + // Initialize lookahead values + lookaheadData = NULL; + lookaheadSize = 0; + + // We have no indicated but pending data, and there is some data to + // indicate. Figure out how much. Indicate upto end of message or as + // much as we have. + + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &lookaheadData, &lookaheadSize); + CTEAssert(lookaheadData != NULL); + CTEAssert(lookaheadSize >= 0); + } + + // Allow for zero data, eom only packets. + lookaheadSize -= pRecvResd->rr_DataOffset; + totalSize = lookaheadSize; + lookaheadData += pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((lookaheadSize > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + +#if DBG + CTEAssert (pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + // Go ahead and walk the list of waiting packets. Get total size. + while ((pRecvResd->rr_Next != NULL) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) == 0)) + { + // Check next packet. + pRecvResd = pRecvResd->rr_Next; + +#if DBG + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, NULL, &bufSize); + CTEAssert(bufSize >= 0); + + // Allow for zero data, eom only packets. + totalSize += bufSize; + } + +#if DBG + pSpxConnFile->scf_IndBytes = totalSize; + pSpxConnFile->scf_IndLine = __LINE__; + + // There better not be any pending receives. If so, we have data + // corruption about to happen. + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + indicateFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_COPY_LOOKAHEAD; + if ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0) + { + indicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + indicateFlags, + lookaheadSize, + totalSize, + &bytesTaken, + lookaheadData, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnIndicatePendingData: IND Flags %lx Size %lx .%lx IND Status %lx\n", + indicateFlags, + totalSize, + bytesTaken, + status)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. Free bytesTaken worth of data packets. + // Sometimes AFD returns STATUS_SUCCESS to just flush the data, so + // we can't assume it took only one packet (since lookahead only + // had that information). + CTEAssert(bytesTaken == totalSize); + SpxRecvFlushBytes(pSpxConnFile, totalSize, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + continue; + } + else if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + return (FALSE); + } + + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + fRecvQueued = TRUE; + } + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return fRecvQueued; +} + + + + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Handle buffered data, complete irp if necessary. Set state to idle + if list becomes empty. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN: More data left to indicate => TRUE + +--*/ +{ + ULONG remainingDataLen, copyLen, bytesCopied; + PREQUEST pRequest; + NTSTATUS status; + BOOLEAN fEom; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + ULONG dataLen; + PBYTE pData; + LIST_ENTRY *p; + BOOLEAN fLockHeld = TRUE, fMoreData = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numDerefs = 0; + + if (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_PROCESS_PKTS); + +ProcessReceives: + + while ((pSpxConnFile->scf_CurRecvReq != NULL) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL)) + { + // A buffering recv packet will have one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + // Allow for zero data, eom only packets. + remainingDataLen = dataLen - pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((remainingDataLen > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + + status = STATUS_SUCCESS; + copyLen = 0; + if (remainingDataLen > 0) + { + copyLen = MIN(remainingDataLen, pSpxConnFile->scf_CurRecvSize); + status = TdiCopyBufferToMdl( + pData, + pRecvResd->rr_DataOffset, + copyLen, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_CurRecvReq), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + copyLen = pSpxConnFile->scf_CurRecvSize; + } + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, pData, copyLen, pRecvResd->rr_State)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + pSpxConnFile->scf_CurRecvSize -= (USHORT)copyLen; + pSpxConnFile->scf_CurRecvOffset += (USHORT)copyLen; + +#if DBG + // If this packet was part of indicated data count, decrement. + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } + } +#endif + + // Set fEom/discState (init to 0) only if all of packet was consumed. + fEom = FALSE; + if (pRecvResd->rr_DataOffset == dataLen) + { + fEom = (BOOLEAN)((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + + // Remember if disconnect needed to happen. If set, this better be + // last packet received. Again, only if entire pkt was consumed. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + // Packet consumed. Free it up. + numDerefs++; + + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + INCREMENT_WINDOW(pSpxConnFile); + + fWdwOpen = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx DEQUEUED\n", + pSpxConnFile, pNdisPkt, pData)); + + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + } + else + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx PARTIAL USE %lx.%lx\n", + pSpxConnFile, pNdisPkt, pRecvResd->rr_DataOffset, dataLen)); + } + + // Don't complete until we are out of all packets and stream mode or... + if (((pSpxConnFile->scf_RecvListHead == NULL) && + SPX_CONN_STREAM(pSpxConnFile)) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + // Done with receive, move to completion or complete depending on + // call level. + pRequest = pSpxConnFile->scf_CurRecvReq; + + // Set status. Complete with error from TdiCopy if so. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + REQUEST_STATUS(pRequest) = status; + + // Ensure we dont overwrite an error status. + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom && + NT_SUCCESS(status)) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + // Dequeue this request, set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Recv %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + // Request is done. Move to receive completion list. There + // could already be previously queued requests in here. + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + } + + CTEAssert((discState == 0) || + (pSpxConnFile->scf_RecvListHead == NULL)); + } + + // Complete any completed receives + while ((p = pSpxConnFile->scf_RecvDoneLinkage.Flink) != + &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + + while (fMoreData) + { + // Bug #21036 + // If there is a receive waiting to be processed, we better not + // indicate data before we finish it. + if (pSpxConnFile->scf_CurRecvReq != NULL) + goto ProcessReceives; + + // If a receive was queued the goto beginning again. + if (SpxRecvIndicatePendingData(pSpxConnFile, LockHandleConn)) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + goto ProcessReceives; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + } + + // Set state + SPX_RECV_SETSTATE( + pSpxConnFile, + (pSpxConnFile->scf_CurRecvReq == NULL) ? + SPX_RECV_IDLE : SPX_RECV_POSTED); + } +#if DBG + else + { + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Already processing pkts %lx\n", + pSpxConnFile)); + } +#endif + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered IDISC %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxreg.c b/private/ntos/tdi/isnp/spx/spxreg.c new file mode 100644 index 000000000..4389dca5f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxreg.c @@ -0,0 +1,400 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXREG + +// Local functions used to access the registry. +NTSTATUS +SpxInitReadIpxDeviceName( + VOID); + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitGetConfiguration) +#pragma alloc_text(INIT, SpxInitFreeConfiguration) +#pragma alloc_text(INIT, SpxInitGetConfigValue) +#pragma alloc_text(INIT, SpxInitReadIpxDeviceName) +#pragma alloc_text(INIT, SpxInitSetIpxDeviceName) +#endif + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + RegistryPath - The name of ST's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + NTSTATUS Status; + UINT i; + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + + ULONG Zero = 0; + ULONG Two = 2; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Twelve = 12; + ULONG Fifteen = 15; + ULONG Thirty = 30; + ULONG FiveHundred = 500; + ULONG Hex4000 = 0x4000; + ULONG Hex7FFF = 0x7FFF; + ULONG FourK = 4096; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"ConnectionCount", &Five }, + { L"ConnectionTimeout", &Two }, + { L"InitPackets", &Five }, + { L"MaxPackets", &Thirty}, + { L"InitialRetransmissionTime", &FiveHundred}, + { L"KeepAliveCount", &Eight}, + { L"KeepAliveTimeout", &Twelve}, + { L"WindowSize", &Four}, + { L"SpxSocketRangeStart", &Hex4000}, + { L"SpxSocketRangeEnd", &Hex7FFF}, + { L"SpxSocketUniqueness", &Eight}, + { L"MaxPacketSize", &FourK}, + { L"RetransmissionCount", &Eight}, + { L"DisableSpx2", &Zero}, + { L"RouterMtu", &Zero}, + { L"BackCompSpx", &Zero} + }; + + if (!NT_SUCCESS(SpxInitReadIpxDeviceName())) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Allocate memory for the main config structure. + Config = CTEAllocMem (sizeof(CONFIG)); + if (Config == NULL) { + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->cf_DeviceName.Buffer = NULL; + + // SpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + RegistryPathBuffer = (PWSTR)CTEAllocMem(RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory ( + RegistryPathBuffer, + RegistryPath->Buffer, + RegistryPath->Length); + + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->cf_RegistryPathBuffer = RegistryPathBuffer; + + // Read the per-transport (as opposed to per-binding) + // parameters. + // + // Set up QueryTable to do the following: + // 1) Switch to the Parameters key below SPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // 2-14) Call SpxSetBindingValue for each of the keys we + // care about. + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = SpxInitGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + } + + // 15) Stop + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->cf_RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return Status; + } + + CTEFreeMem (RegistryPathBuffer); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} // SpxInitGetConfiguration + + + + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get free any storage that was allocated + by SpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + CTEFreeMem (Config); + +} // SpxInitFreeConfig + + + + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + DBGPRINT(CONFIG, INFO, + ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + + Config->cf_Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + return STATUS_SUCCESS; + +} // SpxInitGetConfigValue + + + + +NTSTATUS +SpxInitReadIpxDeviceName( + VOID + ) + +{ + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = IPX_REG_PATH; + + // Set up QueryTable to do the following: + // + // 1) Call SetIpxDeviceName for the string in "Export" + QueryTable[0].QueryRoutine = SpxInitSetIpxDeviceName; + QueryTable[0].Flags = 0; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + + // 2) Stop + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + + + + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - NULL. + + EntryContext - NULL. + +Return Value: + + status + +--*/ + +{ + PWSTR fileName; + NTSTATUS status = STATUS_SUCCESS; + + fileName = (PWSTR)CTEAllocMem(ValueLength); + if (fileName != NULL) { + RtlCopyMemory(fileName, ValueData, ValueLength); + RtlInitUnicodeString (&IpxDeviceName, fileName); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return(status); +} + diff --git a/private/ntos/tdi/isnp/spx/spxsend.c b/private/ntos/tdi/isnp/spx/spxsend.c new file mode 100644 index 000000000..6b856953d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxsend.c @@ -0,0 +1,262 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.c + +Abstract: + + This module contains code that implements the send engine for the + SPX transport provider. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXSEND + +VOID +SpxSendComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pNdisBuffer; + CTELockHandle lockHandle; + UINT bufCount; + PREQUEST pRequest = NULL; + BOOLEAN completeReq = FALSE, freePkt = FALSE, + orphaned = FALSE, lockHeld = FALSE; + + pSendResd = (PSPX_SEND_RESD)(pNdisPkt->ProtocolReserved); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: For %lx with status **%lx**\n", + pNdisPkt, NdisStatus)); + } +#endif + + // IPX changes the length set for the first ndis buffer descriptor. + // Change it back to its original value here. + NdisQueryPacket(pNdisPkt, NULL, &bufCount, &pNdisBuffer, NULL); + NdisAdjustBufferLength(pNdisBuffer, IpxMacHdrNeeded + MIN_IPXSPX2_HDRSIZE); + + do + { + pSpxConnFile = pSendResd->sr_ConnFile; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; +#if defined(__PNP) + // + // if IPX gave us a new LocalTarget, use for our next send. + // + // But if we are sending connect requests by iterating over NicIds, + // dont update the local target bcoz that will screw up our iteration + // logic. + // + if ( DEVICE_NETWORK_PATH_NOT_FOUND == NdisStatus + && + !( + SPX_CONN_CONNECTING(pSpxConnFile) && + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) + ) ) { + + pSpxConnFile->scf_LocalTarget = pSendResd->LocalTarget; + + // + // Renegotiate the max packet size if we have an active SPX2 + // session going on and we negotiated the max size originally. + // + if ( SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG) ) { + + // + // this call will get the local max size on this new local target + // from IPX. + // + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr ); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + + } +#endif __PNP + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0); + + // IPX dont own this packet nomore. + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + + // If a send packet has been aborted, then we need to call + // abort send to go ahead and free up this packet, and deref associated + // request, if there is one, potentially completing it. + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) != 0) + { + spxConnAbortSendPkt( + pSpxConnFile, + pSendResd, + SPX_CALL_TDILEVEL, + lockHandle); + + lockHeld = FALSE; + break; + } + + // If there is an associated request, remove reference on it. BUT for a + // sequenced packet only if it has been acked and is waiting for the request + // to be dereferenced. It is already dequeued from queue, just free it up. + if ((((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) && + ((pSendResd->sr_State & SPX_SENDPKT_SEQ) == 0)) || + ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0)) + { + freePkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0); + + pRequest = pSendResd->sr_Request; + CTEAssert(pRequest != NULL); + + DBGPRINT(SEND, DBG, + ("IpxSendComplete: ReqRef before dec %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest))); + + // Deref the request and see if we complete it now. We always have our + // own reference on the request. + // !!! Status should already have been set in request...!!! + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + + completeReq = TRUE; + + // If this is acked already, request is not on list. + // BUG #11626 + if ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) == 0) + { + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + } + } + } + + // Do we destroy this packet? + if ((pSendResd->sr_State & SPX_SENDPKT_DESTROY) != 0) + { + // Remove this packet from the send list in the connection. + DBGPRINT(SEND, INFO, + ("IpxSendComplete: destroy packet...\n")); + + SpxConnDequeueSendPktLock(pSpxConnFile, pNdisPkt); + freePkt = TRUE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (freePkt) + { + DBGPRINT(SEND, INFO, + ("IpxSendComplete: free packet...\n")); + + SpxPktSendRelease(pNdisPkt); + } + + if (completeReq) + { + // If this is a send request, set info to data sent, else it will be + // zero. + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_SEND) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + DBGPRINT(SEND, DBG, + ("IpxSendComplete: complete req %lx.%lx...\n", + REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(pRequest != NULL); + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + SpxCompleteRequest(pRequest); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx DISC Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx.%lx.%lx\n", + pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags, + pSpxConnFile->scf_Flags2)); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; + +} // SpxSendComplete + + + diff --git a/private/ntos/tdi/isnp/spx/spxtimer.c b/private/ntos/tdi/isnp/spx/spxtimer.c new file mode 100644 index 000000000..bdb4e1d7f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxtimer.c @@ -0,0 +1,637 @@ +/* + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.c + +Abstract: + + This file implements the timer routines used by the stack. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + + +Revision History: + 23 Feb 1993 Initial Version + +Notes: Tab stop: 4 +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXTIMER + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxTimerInit) +#endif + +// Globals for this module +PTIMERLIST spxTimerList = NULL; +PTIMERLIST spxTimerTable[TIMER_HASH_TABLE] = {0}; +PTIMERLIST spxTimerActive = NULL; +CTELock spxTimerLock = {0}; +LARGE_INTEGER spxTimerTick = {0}; +KTIMER spxTimer = {0}; +KDPC spxTimerDpc = {0}; +ULONG spxTimerId = 1; +LONG spxTimerCount = 0; +USHORT spxTimerDispatchCount = 0; +BOOLEAN spxTimerStopped = FALSE; + + +NTSTATUS +SpxTimerInit( + VOID + ) +/*++ + +Routine Description: + + Initialize the timer component for the appletalk stack. + +Arguments: + + +Return Value: + + +--*/ +{ +#if !defined(_PNP_POWER) + BOOLEAN TimerStarted; +#endif !_PNP_POWER + + // Initialize the timer and its associated Dpc. timer will be kicked + // off when we get the first card arrival notification from ipx + KeInitializeTimer(&spxTimer); + CTEInitLock(&spxTimerLock); + KeInitializeDpc(&spxTimerDpc, spxTimerDpcRoutine, NULL); + spxTimerTick = RtlConvertLongToLargeInteger(SPX_TIMER_TICK); +#if !defined(_PNP_POWER) + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); +#endif !_PNP_POWER + return STATUS_SUCCESS; +} + + + + +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG MsTime, // Schedule after this much time + IN PVOID pContext // Context(s) to pass to the routine + ) +/*++ + +Routine Description: + + Insert an event in the timer event list. If the list is empty, then + fire off a timer. The time is specified in ms. We convert to ticks. + Each tick is currently 100ms. It may not be zero or negative. The internal + timer fires at 100ms granularity. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + ULONG DeltaTime; + ULONG Id = 0; + + // Convert to ticks. + DeltaTime = MsTime/SPX_MS_TO_TICKS; + if (DeltaTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + DeltaTime = 1; + } + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + // Negative or Zero DeltaTime is invalid. + CTEAssert (DeltaTime > 0); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Routine %lx, Time %d, Context %lx\n", + Worker, DeltaTime, pContext)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, FATAL, + ("SpxTimerScheduleEvent: Called after Flush !!\n")); + } + + else do + { + pList = SpxBPAllocBlock(BLKID_TIMERLIST); + + if (pList == NULL) + { + break; + } + +#if DBG + pList->tmr_Signature = TMR_SIGNATURE; +#endif + pList->tmr_Cancelled = FALSE; + pList->tmr_Worker = Worker; + pList->tmr_AbsTime = DeltaTime; + pList->tmr_Context = pContext; + + Id = pList->tmr_Id = spxTimerId++; + + // Take care of wrap around + if (spxTimerId == 0) + spxTimerId = 1; + + // Enqueue this handler + spxTimerEnqueue(pList); + } while (FALSE); + + CTEFreeLock(&spxTimerLock, lockHandle); + + return Id; +} + + + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This is called in at DISPATCH_LEVEL when the timer expires. The entry at + the head of the list is decremented and if ZERO unlinked and dispatched. + If the list is non-empty, the timer is fired again. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + BOOLEAN TimerStarted; + ULONG ReEnqueueTime; + CTELockHandle lockHandle; + + pKDpc; pContext; SystemArgument1; SystemArgument2; + +#if defined(_PNP_POWER) + CTEGetLock(&spxTimerLock, &lockHandle); + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + + CTEFreeLock(&spxTimerLock, lockHandle); + return; + } +#else + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + return; + } + + CTEGetLock(&spxTimerLock, &lockHandle); +#endif _PNP_POWER + + SpxTimerCurrentTime ++; // Update our relative time + +#ifdef PROFILING + // This is the only place where this is changed. And it always increases. + SpxStatistics.stat_ElapsedTime = SpxTimerCurrentTime; +#endif + + // We should never be here if we have no work to do + if ((spxTimerList != NULL)) + { + // Careful here. If two guys wanna go off together - let them !! + if (spxTimerList->tmr_RelDelta != 0) + (spxTimerList->tmr_RelDelta)--; + + // Dispatch the entry if it is ready to go + if (spxTimerList->tmr_RelDelta == 0) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + + // Unlink from the list + spxTimerList = pList->tmr_Next; + if (spxTimerList != NULL) + spxTimerList->tmr_Prev = &spxTimerList; + + // Unlink from the hash table now + for (ppList = &spxTimerTable[pList->tmr_Id % TIMER_HASH_TABLE]; + *ppList != NULL; + ppList = &((*ppList)->tmr_Overflow)) + { + CTEAssert(VALID_TMR(*ppList)); + if (*ppList == pList) + { + *ppList = pList->tmr_Overflow; + break; + } + } + + CTEAssert (*ppList == pList->tmr_Overflow); + + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Dispatching %lx\n", + pList->tmr_Worker)); + + spxTimerDispatchCount ++; + spxTimerCount --; + spxTimerActive = pList; + CTEFreeLock(&spxTimerLock, lockHandle); + + // If reenqueue time is 0, do not requeue. If 1, then requeue with + // current value, else use value specified. + ReEnqueueTime = (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Reenequeu time %lx.%lx\n", + ReEnqueueTime, pList->tmr_AbsTime)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerActive = NULL; + spxTimerDispatchCount --; + + if (ReEnqueueTime != TIMER_DONT_REQUEUE) + { + // If this chappie was cancelled while it was running + // and it wants to be re-queued, do it right away. + if (pList->tmr_Cancelled) + { + (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + else + { + if (ReEnqueueTime != TIMER_REQUEUE_CUR_VALUE) + { + pList->tmr_AbsTime = ReEnqueueTime/SPX_MS_TO_TICKS; + if (pList->tmr_AbsTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld\n", + pList->tmr_AbsTime)); + } + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld.%ld\n", + ReEnqueueTime, pList->tmr_AbsTime)); + } + + spxTimerEnqueue(pList); + } + } + else + { + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + } + } + +#if defined(_PNP_POWER) + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + // it is possible that while we were here in Dpc, PNP_ADD_DEVICE + // restarted the timer, so this assert is commented out for PnP +// CTEAssert(!TimerStarted); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +#else + CTEFreeLock(&spxTimerLock, lockHandle); + + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); + } +#endif _PNP_POWER +} + + +VOID +spxTimerEnqueue( + IN PTIMERLIST pListNew + ) +/*++ + +Routine Description: + + Here is a thesis on the code that follows. + + The timer events are maintained as a list which the timer dpc routine + looks at every timer tick. The list is maintained in such a way that only + the head of the list needs to be updated every tick i.e. the entire list + is never scanned. The way this is achieved is by keeping delta times + relative to the previous entry. + + Every timer tick, the relative time at the head of the list is decremented. + When that goes to ZERO, the head of the list is unlinked and dispatched. + + To give an example, we have the following events queued at time slots + X Schedule A after 10 ticks. + X+3 Schedule B after 5 ticks. + X+5 Schedule C after 4 ticks. + X+8 Schedule D after 6 ticks. + + So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and + D at X+14 (X+8+6). + + The above example covers all the situations. + + - NULL List. + - Inserting at head of list. + - Inserting in the middle of the list. + - Appending to the list tail. + + The list will look as follows. + + BEFORE AFTER + ------ ----- + + X Head -->| Head -> A(10) ->| + A(10) + + X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->| + B(5) + + X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->| + C(4) + + X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->| + D(6) + + The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + ULONG DeltaTime = pListNew->tmr_AbsTime; + + // The DeltaTime is adjusted in every pass of the loop to reflect the + // time after the previous entry that the new entry will schedule. + for (ppList = &spxTimerList; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Next) + { + CTEAssert(VALID_TMR(pList)); + if (DeltaTime <= pList->tmr_RelDelta) + { + pList->tmr_RelDelta -= DeltaTime; + break; + } + DeltaTime -= pList->tmr_RelDelta; + } + + + // Link this in the chain + pListNew->tmr_RelDelta = DeltaTime; + pListNew->tmr_Next = pList; + pListNew->tmr_Prev = ppList; + *ppList = pListNew; + if (pList != NULL) + { + pList->tmr_Prev = &pListNew->tmr_Next; + } + + // Now link it in the hash table + pListNew->tmr_Overflow = spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE]; + spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE] = pListNew; + spxTimerCount ++; +} + + + + +VOID +SpxTimerFlushAndStop( + VOID + ) +/*++ + +Routine Description: + + Force all entries in the timer queue to be dispatched immediately. No + more queue'ing of timer routines is permitted after this. The timer + essentially shuts down. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + + CTEAssert (KeGetCurrentIrql() == LOW_LEVEL); + + DBGPRINT(SYSTEM, ERR, + ("SpxTimerFlushAndStop: Entered\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerStopped = TRUE; + + KeCancelTimer(&spxTimer); + + if (spxTimerList != NULL) + { + // Dispatch all entries right away + while (spxTimerList != NULL) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + spxTimerList = pList->tmr_Next; + + DBGPRINT(SYSTEM, INFO, + ("spxTimerFlushAndStop: Dispatching %lx\n", + pList->tmr_Worker)); + + // The timer routines assume they are being called at DISPATCH + // level. This is OK since we are calling with SpinLock held. + + (*pList->tmr_Worker)(pList->tmr_Context, TRUE); + + spxTimerCount --; + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + RtlZeroMemory(spxTimerTable, sizeof(spxTimerTable)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + // Wait for all timer routines to complete + while (spxTimerDispatchCount != 0) + { + SpxSleep(SPX_TIMER_WAIT); + } +} + + + + +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue + ) +/*++ + +Routine Description: + + Cancel a previously scheduled timer event, if it hasn't fired already. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + CTELockHandle lockHandle; + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: Entered for TimerId %ld\n", TimerId)); + + CTEAssert(TimerId != 0); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (ppList = &spxTimerTable[TimerId % TIMER_HASH_TABLE]; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Overflow) + { + CTEAssert(VALID_TMR(pList)); + // If we find it, cancel it + if (pList->tmr_Id == TimerId) + { + // Unlink this from the hash table + *ppList = pList->tmr_Overflow; + + // ... and from the list + if (pList->tmr_Next != NULL) + { + pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta; + pList->tmr_Next->tmr_Prev = pList->tmr_Prev; + } + *(pList->tmr_Prev) = pList->tmr_Next; + + spxTimerCount --; + if (ReEnqueue) + spxTimerEnqueue(pList); + else SpxBPFreeBlock(pList, BLKID_TIMERLIST); + break; + } + } + + // If we could not find it in the list, see if it currently running. + // If so mark him to not reschedule itself, only if reenqueue was false. + if (pList == NULL) + { + if ((spxTimerActive != NULL) && + (spxTimerActive->tmr_Id == TimerId) && + !ReEnqueue) + { + spxTimerActive->tmr_Cancelled = TRUE; + } + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: %s for Id %ld\n", + (pList != NULL) ? "Success" : "Failure", TimerId)); + + return (pList != NULL); +} + + + + +#if DBG + +VOID +SpxTimerDumpList( + VOID + ) +{ + PTIMERLIST pList; + ULONG CumTime = 0; + CTELockHandle lockHandle; + + DBGPRINT(DUMP, FATAL, + ("TIMER LIST: (Times are in %dms units\n", 1000)); + DBGPRINT(DUMP, FATAL, + ("\tTimerId Time(Abs) Time(Rel) Routine Address\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (pList = spxTimerList; + pList != NULL; + pList = pList->tmr_Next) + { + CumTime += pList->tmr_RelDelta; + DBGPRINT(DUMP, FATAL, + ("\t% 6lx %5d %5ld %lx\n", + pList->tmr_Id, pList->tmr_AbsTime, CumTime, pList->tmr_Worker)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +} + +#endif diff --git a/private/ntos/tdi/isnp/spx/spxutils.c b/private/ntos/tdi/isnp/spx/spxutils.c new file mode 100644 index 000000000..024a36988 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxutils.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.c + +Abstract: + + This contains all utility routines for the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXUTILS + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + UINT length = 0; + + while (*Wstr++) + { + length += sizeof(WCHAR); + } + + return length; +} + + + + +LONG +SpxRandomNumber( + VOID + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LARGE_INTEGER Li; + static LONG seed = 0; + + // Return a positive pseudo-random number; simple linear congruential + // algorithm. ANSI C "rand()" function. + + if (seed == 0) + { + KeQuerySystemTime(&Li); + seed = Li.LowPart; + } + + seed *= (0x41C64E6D + 0x3039); + + return (seed & 0x7FFFFFFF); +} + + + + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType + ) +/*++ + +Routine Description: + + For PROTO_SPX, i'd return a device name from the dll of the form + \Device\IsnSpx\SpxStream (for SOCK_STREAM) or + \Device\IsnSpx\Spx (for SOCK_SEQPKT) + + and for PROTO_SPXII (the more common case we hope, even if + internally we degrade to SPX1 cause of the remote client's + limitations) + \Device\IsnSpx\Stream (for SOCK_STREAM) or + \Device\IsnSpx (for SOCK_SEQPKT) + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING typeString; + + *SocketType = SOCKET2_TYPE_SEQPKT; + + // Check for the socket type + do + { + if (RemainingFileName->Length == 0) + { + break; + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_STREAM; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_SEQPKT; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET2STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET2STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET2_TYPE_STREAM; + break; + } + } + + status = STATUS_NO_SUCH_DEVICE; + + } while (FALSE); + + return(status); +} + + + + +#define ONE_MS_IN_100ns -10000L // 1ms in 100ns units + +VOID +SpxSleep( + IN ULONG TimeInMs + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + KTIMER SleepTimer; + + ASSERT (KeGetCurrentIrql() == LOW_LEVEL); + + KeInitializeTimer(&SleepTimer); + + KeSetTimer(&SleepTimer, + RtlConvertLongToLargeInteger(TimeInMs * ONE_MS_IN_100ns), + NULL); + + KeWaitForSingleObject(&SleepTimer, UserRequest, KernelMode, FALSE, NULL); + return; +} + + + + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + for (i=0;iTAAddressCount;i++) + { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} // SpxParseTdiAddress + + + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: runt address\n")); + + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) + { + if (addressName->Address > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + return TRUE; + +} // SpxValidateTdiAddress + + + + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. It will write + less than the full address if the buffer is too short. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + AddressBufferLength - The length of the buffer. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + The number of bytes written into AddressBuffer. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + TA_IPX_ADDRESS TempAddress; + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + } + else + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)&TempAddress; + } + + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)Network; + SpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(SpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + return sizeof(TA_IPX_ADDRESS); + } + else + { + RtlCopyMemory(AddressBuffer, &TempAddress, AddressBufferLength); + return AddressBufferLength; + } + +} // SpxBuildTdiAddress + + + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr + ) +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)pIpxAddr; + RtlCopyMemory( + SpxAddress->Address[0].Address[0].NodeAddress, + pIpxAddr+4, + 6); + + GETSHORT2SHORT( + &SpxAddress->Address[0].Address[0].Socket, + pIpxAddr + 10); + + return; +} + + + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1 + ) +/*++ + +Routine Description: + + +Arguments: + + NewT1 - New value for the RTT in ms. + +Return Value: + + +--*/ +{ + int baseT1, error; + + // + // VAN JACOBSEN Algorithm. From Internetworking with Tcp/ip + // (Comer) book. + // + + error = NewT1 - (pSpxConnFile->scf_AveT1 >> 3); + pSpxConnFile->scf_AveT1 += error; + if (pSpxConnFile->scf_AveT1 <= 0) // Make sure not too small + { + pSpxConnFile->scf_AveT1 = SPX_T1_MIN; + } + + if (error < 0) + error = -error; + + error -= (pSpxConnFile->scf_DevT1 >> 2); + pSpxConnFile->scf_DevT1 += error; + if (pSpxConnFile->scf_DevT1 <= 0) + pSpxConnFile->scf_DevT1 = 1; + + baseT1 = (((pSpxConnFile->scf_AveT1 >> 2) + pSpxConnFile->scf_DevT1) >> 1); + + // If less then min - set it + if (baseT1 < SPX_T1_MIN) + baseT1 = SPX_T1_MIN; + + // Set the new value + DBGPRINT(TDI, DBG, + ("SpxCalculateNewT1: Old value %lx New %lx\n", + pSpxConnFile->scf_BaseT1, baseT1)); + + pSpxConnFile->scf_BaseT1 = baseT1; + + // At the time of restarting the timer,we convert this to a tick value. + return; +} + diff --git a/private/ntos/tdi/isnp/spx/up/makefile b/private/ntos/tdi/isnp/spx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/up/sources b/private/ntos/tdi/isnp/spx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/loopback/connect.c b/private/ntos/tdi/loopback/connect.c new file mode 100644 index 000000000..10376e3e8 --- /dev/null +++ b/private/ntos/tdi/loopback/connect.c @@ -0,0 +1,1593 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module implements connection logic for the loopback Transport + Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 15-Aug-1991 + +Revision History: + +--*/ + +#include "loopback.h" + +// +// Local declarations +// + +STATIC +NTSTATUS +CompleteConnection ( + IN PIRP ListenIrp, + IN PIRP ConnectIrp + ); + +STATIC +VOID +IndicateConnect ( + IN PLOOP_ENDPOINT Endpoint + ); + + +NTSTATUS +LoopAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes an Accept request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_ACCEPT acceptRequest; + PLOOP_CONNECTION connection; + PLOOP_ENDPOINT endpoint; + + IF_DEBUG(LOOP1) DbgPrint( " Accept request\n" ); + + acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&IrpSp->Parameters; + + ACQUIRE_LOOP_LOCK( "Accept initial" ); + + // + // Verify that the connection is in the proper state. + // + + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Accept control channel" ); + IF_DEBUG(LOOP2) DbgPrint( " Can't Accept on control channel\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_STATE(connection) != BlockStateBound ) { + RELEASE_LOOP_LOCK( "Accept conn not bound" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // There must be an indication in progress on the endpoint. + // + // *** The loopback driver does not support delayed accept on + // TdiListen, but does on connect indications. + // + + endpoint = connection->Endpoint; + + if ( endpoint->IndicatingConnectIrp == NULL ) { + RELEASE_LOOP_LOCK( "Endpoint not indicating connect" ); + IF_DEBUG(LOOP2) DbgPrint( " Endpoint not indicating connect\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Link the connections together. Indicate that the connect + // indication is no longer in progress. + // + + CompleteConnection( Irp, endpoint->IndicatingConnectIrp ); + + endpoint->IndicatingConnectIrp = NULL; + + RELEASE_LOOP_LOCK( "Accept connect completed" ); + + IF_DEBUG(LOOP1) DbgPrint( " Accept request complete\n" ); + return STATUS_SUCCESS; + +complete: + + // + // Complete the Accept request. + // + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) DbgPrint( " Accept request complete\n" ); + return status; + +} // LoopAccept + + +NTSTATUS +LoopAssociateAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes an Associate Address request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_ASSOCIATE associateRequest; + PLOOP_CONNECTION connection; + PFILE_OBJECT endpointFileObject = NULL; + PLOOP_ENDPOINT endpoint; + + IF_DEBUG(LOOP1) DbgPrint( " Associate request\n" ); + + associateRequest = (PTDI_REQUEST_KERNEL_ASSOCIATE)&IrpSp->Parameters; + + // + // Translate the address handle to a pointer to the address endpoint + // file object. Get a pointer to the loopback endpoint block. + // Verify that it is really a loopback endpoint. + // + + status = ObReferenceObjectByHandle ( + associateRequest->AddressHandle, + 0, + 0, + KernelMode, + (PVOID *)&endpointFileObject, + NULL); + + if ( !NT_SUCCESS(status) ) { + IF_DEBUG(LOOP2) DbgPrint( " Invalid endpoint handle\n" ); + goto complete; + } + + ACQUIRE_LOOP_LOCK( "Associate initial" ); + + endpoint = (PLOOP_ENDPOINT)endpointFileObject->FsContext; + status = LoopVerifyEndpoint( endpoint ); + + if ( !NT_SUCCESS(status) ) { + RELEASE_LOOP_LOCK( "Associate bad endpoint pointer" ); + IF_DEBUG(LOOP2) DbgPrint( " Invalid endpoint pointer\n" ); + goto complete; + } + + // + // Verify that the connection is in the proper state. + // + + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Associate control channel" ); + IF_DEBUG(LOOP2) DbgPrint( " Can't Associate on control channel\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_STATE(connection) != BlockStateUnbound ) { + RELEASE_LOOP_LOCK( "Associate conn not unbound" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not unbound\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Verify that the endpoint is in the proper state. + // + + if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) { + RELEASE_LOOP_LOCK( "Associate endpoint not active" ); + IF_DEBUG(LOOP2) DbgPrint( " Endpoint not active\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Link this connection into the endpoint's connection list. + // Reference the endpoint block. Indicate that the connection has + // been bound to an address. Dereference the endpoint file object. + // + + InsertTailList( + &endpoint->ConnectionList, + &connection->EndpointListEntry + ); + + endpoint->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on endpoint %lx is %lx\n", + endpoint, endpoint->BlockHeader.ReferenceCount ); + } + + SET_BLOCK_STATE( connection, BlockStateBound ); + + connection->Endpoint = endpoint; + + RELEASE_LOOP_LOCK( "Associate done" ); + + status = STATUS_SUCCESS; + +complete: + + // + // Dereference the endpoint file object, if necessary. + // + + if ( endpointFileObject != NULL ) { + ObDereferenceObject( endpointFileObject ); + } + + // + // Complete the Associate request. + // + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) DbgPrint( " Associate request complete\n" ); + return status; + +} // LoopAssociateAddress + + +NTSTATUS +LoopConnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Connect request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_CONNECT connectRequest; + CHAR netbiosName[NETBIOS_NAME_LENGTH+1]; + PLOOP_CONNECTION connection; + PLOOP_ENDPOINT remoteEndpoint; + BOOLEAN firstConnect; + + IF_DEBUG(LOOP1) DbgPrint( " Connect request\n" ); + + // + // Parse the remote address. + // + + connectRequest = (PTDI_REQUEST_KERNEL_CONNECT)&IrpSp->Parameters; + + status = LoopParseAddress( + (PTA_NETBIOS_ADDRESS)connectRequest-> + RequestConnectionInformation->RemoteAddress, + netbiosName + ); + + if ( !NT_SUCCESS(status) ) { + IF_DEBUG(LOOP2) DbgPrint( " Invalid remote address\n" ); + goto complete; + } + + netbiosName[NETBIOS_NAME_LENGTH] = 0; // ensure null-termination + IF_DEBUG(LOOP2) { + DbgPrint( " Address to connect to: \"%s\"\n", netbiosName ); + } + + // + // Make sure that the connection is in the right state. + // + + ACQUIRE_LOOP_LOCK( "Connect initial" ); + + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + IF_DEBUG(LOOP2) { + DbgPrint( " Connection address: %lx\n", connection ); + } + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Connect control channel" ); + IF_DEBUG(LOOP2) DbgPrint( " Can't Connect on control channel\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_STATE(connection) != BlockStateBound ) { + RELEASE_LOOP_LOCK( "Connection not bound" ); + IF_DEBUG(LOOP2) DbgPrint( " Local endpoint not bound\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Find an endpoint with the target address. + // + + remoteEndpoint = LoopFindBoundAddress( netbiosName ); + + if ( remoteEndpoint == NULL ) { + RELEASE_LOOP_LOCK( "Connect no such address" ); + IF_DEBUG(LOOP2) DbgPrint( " Address does not exist\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + IF_DEBUG(LOOP2) { + DbgPrint( " Target endpoint address: %lx\n", remoteEndpoint ); + } + + // + // Set the connection state to Connecting, to prevent further + // connects or listens. + // + + SET_BLOCK_STATE( connection, BlockStateConnecting ); + + // + // Determine whether this is the first active incoming Connect for + // the remote endpoint. + // + + firstConnect = (BOOLEAN)( remoteEndpoint->IncomingConnectList.Flink == + &remoteEndpoint->IncomingConnectList ); + + // + // Queue the Connect to the remote endpoint's Incoming Connect list, + // in order to prevent another Connect from getting ahead of this + // one. + // + + InsertTailList( + &remoteEndpoint->IncomingConnectList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + if ( !firstConnect || (remoteEndpoint->IndicatingConnectIrp != NULL) ) { + + // + // A pending Connect already exists, or a Connect indication is + // in progress. This Connect remains behind the already pending + // Connect. + // + + IF_DEBUG(LOOP2) DbgPrint( " Connect already pending\n" ); + + RELEASE_LOOP_LOCK( "Connect connects pending" ); + + } else { + + // + // Indicate the incoming Connect. + // + // *** Note that IndicateConnect returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopConnect indicating connect\n" ); + IndicateConnect( remoteEndpoint ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Connect request complete\n" ); + return status; + +complete: + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) DbgPrint( " Connect request complete\n" ); + return status; + +} // LoopConnect + + +VOID +LoopDereferenceConnection ( + IN PLOOP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to dereference a connection block. If the + reference count on the block goes to one, and the connection is in + the process of disconnecting, the connection block is reset to the + Bound state. If the reference count on the block goes to zero, the + connection block is deleted. + + The Loopback device object's spin lock must be held when this + routine is called. + +Arguments: + + Connection - Supplies a pointer to a connection block + +Return Value: + + None. + +--*/ + +{ + PIRP disconnectIrp = NULL; + PIRP closeIrp = NULL; + + IF_DEBUG(LOOP3) { + DbgPrint( " Dereferencing connection %lx; old refcnt %lx\n", + Connection, Connection->BlockHeader.ReferenceCount ); + } + + ASSERT( (LONG)Connection->BlockHeader.ReferenceCount > 0 ); + + if ( --Connection->BlockHeader.ReferenceCount != 0 ) { + return; + } + + // + // The reference count has gone to zero. Save the address of the + // pending Disconnect IRP, if any -- we'll complete the IRP later. + // If the connection state is Disconnecting, reset it to Bound. If + // the connection state is Closed, delete it. + // + + disconnectIrp = Connection->DisconnectIrp; + Connection->DisconnectIrp = NULL; + + // + // If the connection state is Disconnecting, reset it to Bound or + // Unbound, depending on the state of the endpoint. If the + // connection state is Closed, delete the connection. Note that the + // connection state may also be Closing, which means that an + // inactive connection's handle has been closed. We do nothing in + // this case; instead we wait until the Close IRP arrives. + // + + if ( GET_BLOCK_STATE(Connection) == BlockStateDisconnecting ) { + + if ( GET_BLOCK_STATE(Connection->Endpoint) == BlockStateActive ) { + + IF_DEBUG(LOOP3) { + DbgPrint( " Resetting connection %lx to Bound\n", + Connection ); + } + SET_BLOCK_STATE( Connection, BlockStateBound ); + + } else { + + PLOOP_ENDPOINT endpoint; + + IF_DEBUG(LOOP3) { + DbgPrint( " Resetting connection %lx to Unbound\n", + Connection ); + } + endpoint = Connection->Endpoint; + ASSERT( endpoint != NULL ); + Connection->Endpoint = NULL; + + SET_BLOCK_STATE( Connection, BlockStateUnbound ); + + RemoveEntryList( &Connection->EndpointListEntry ); + LoopDereferenceEndpoint( endpoint ); + + } + + RELEASE_LOOP_LOCK( "Reset connection done" ); + + } else if ( GET_BLOCK_STATE(Connection) == BlockStateClosed ) { + + // + // The connection file object has been closed, so we can + // deallocate the connection block now. + // + + IF_DEBUG(LOOP3) { + DbgPrint( " Deleting connection %lx\n", Connection ); + } + + // + // Save the address of the pending Close IRP, if any -- we'll + // complete the IRP later. + // + + closeIrp = Connection->CloseIrp; + Connection->CloseIrp = NULL; + + // + // Unlink the connection from the endpoint's connection list. + // + + if ( Connection->Endpoint != NULL ) { + RemoveEntryList( &Connection->EndpointListEntry ); + LoopDereferenceEndpoint( Connection->Endpoint ); + } + + // + // Unlink the connection from the device's connection list. + // + + RemoveEntryList( &Connection->DeviceListEntry ); + + RELEASE_LOOP_LOCK( "DerefConn deleting" ); + + ObDereferenceObject( Connection->DeviceObject ); + + // + // Deallocate the connection block. + // + + DEBUG SET_BLOCK_TYPE( Connection, BlockTypeGarbage ); + DEBUG SET_BLOCK_STATE( Connection, BlockStateDead ); + DEBUG SET_BLOCK_SIZE( Connection, -1 ); + DEBUG Connection->BlockHeader.ReferenceCount = -1; + DEBUG Connection->Endpoint = NULL; + DEBUG Connection->RemoteConnection = NULL; + + ExFreePool( Connection ); + + } else { + + ASSERT( GET_BLOCK_STATE(Connection) == BlockStateClosing ); + + RELEASE_LOOP_LOCK( "DerefConn closing -- no action" ); + + } + + // + // If a Disconnect IRP is pending, complete it now. + // + + if ( disconnectIrp != NULL ) { + + IF_DEBUG(LOOP3) { + DbgPrint( " Completing Disconnect IRP %lx\n", + disconnectIrp ); + } + disconnectIrp->IoStatus.Status = STATUS_SUCCESS; + disconnectIrp->IoStatus.Information = 0; + + IoCompleteRequest( disconnectIrp, 2 ); + + } + + // + // If a Close IRP is pending, complete it now. + // + + if ( closeIrp != NULL ) { + + IF_DEBUG(LOOP3) { + DbgPrint( " Completing Close IRP %lx\n", closeIrp ); + } + closeIrp->IoStatus.Status = STATUS_SUCCESS; + closeIrp->IoStatus.Information = 0; + + IoCompleteRequest( closeIrp, 2 ); + + } + + ACQUIRE_LOOP_LOCK( "DerefConn done" ); + + return; + +} // LoopDereferenceConnection + + +NTSTATUS +LoopDisassociateAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Disassociate Address request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PLOOP_CONNECTION connection; + PLOOP_ENDPOINT endpoint; + + IF_DEBUG(LOOP1) DbgPrint( " Disassociate request\n" ); + + ACQUIRE_LOOP_LOCK( "Disassociate initial" ); + + // + // Verify that the connection is in the proper state. + // + + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Disassociate control channel" ); + IF_DEBUG(LOOP2) { + DbgPrint( " Can't Disassociate on control channel\n" ); + } + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_STATE(connection) != BlockStateBound ) { + RELEASE_LOOP_LOCK( "Associate conn not bound" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Remove this connection from the endpoint's connection list. + // Dereference the endpoint block. Indicate that the connection is + // no longer bound to an address. + // + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + connection->Endpoint = NULL; + + SET_BLOCK_STATE( connection, BlockStateUnbound ); + + RemoveEntryList( &connection->EndpointListEntry ); + LoopDereferenceEndpoint( endpoint ); + + RELEASE_LOOP_LOCK( "Disassociate done" ); + + status = STATUS_SUCCESS; + +complete: + + // + // Complete the Disassociate request. + // + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) DbgPrint( " Disassociate request complete\n" ); + return status; + +} // LoopDisassociate + + +NTSTATUS +LoopDisconnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Disconnect request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest; + PLOOP_CONNECTION connection; + + IF_DEBUG(LOOP1) DbgPrint( " Disconnect request\n" ); + + disconnectRequest = (PTDI_REQUEST_KERNEL_DISCONNECT)&IrpSp->Parameters; + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + IF_DEBUG(LOOP2) DbgPrint( " Connection: %lx\n", connection ); + + ACQUIRE_LOOP_LOCK( "Disconnect initial" ); + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Disconnect control channel" ); + IF_DEBUG(LOOP2) { + DbgPrint( " Can't Disconnect on control channel\n" ); + } + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // If the connection state is Bound, then this Disconnect must have + // been issued from within a Connect indication. + // + + if ( GET_BLOCK_STATE(connection) == BlockStateBound ) { + + PIRP connectIrp = connection->Endpoint->IndicatingConnectIrp; + + if ( connectIrp != NULL ) { + + // + // Abort the indicating Connect. + // + + connection->Endpoint->IndicatingConnectIrp = NULL; + + RELEASE_LOOP_LOCK( "Disconnect abort indication" ); + + connectIrp->IoStatus.Status = STATUS_DISCONNECTED; + IoCompleteRequest( connectIrp, 2 ); + + } else { + + RELEASE_LOOP_LOCK( "Disconnect bound, no indication" ); + + } + + status = STATUS_SUCCESS; + goto complete; + + } + + if ( GET_BLOCK_STATE(connection) != BlockStateActive ) { + + RELEASE_LOOP_LOCK( "Disconnect closing" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection already disconnected\n" ); + + status = STATUS_SUCCESS; + goto complete; + + } + + // + // Set the disconnect IRP pointer in the connection. The IRP will + // be completed when the connection is actually deleted. + // + + IoMarkIrpPending( Irp ); + connection->DisconnectIrp = Irp; + + SET_BLOCK_STATE( connection, BlockStateDisconnecting ); + LoopDoDisconnect( connection, TRUE ); + + RELEASE_LOOP_LOCK( "Disconnect done" ); + + return STATUS_PENDING; + +complete: + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 2 ); + + IF_DEBUG(LOOP1) DbgPrint( " Disconnect request complete\n" ); + return status; + +} // LoopDisconnect + + +VOID +LoopDoDisconnect ( + IN PLOOP_CONNECTION Connection, + IN BOOLEAN ClientInitiated + ) + +/*++ + +Routine Description: + + This routine does the work in disconnecting a connection. It is + called by LoopDisconnect and LoopDispatchClose. + + The loopback device object's spin lock must be held when this + function is called. The lock is still held when the function + returns. + +Arguments: + + Connection - Pointer to connection block + + ClientInitiated - Indicates whether the local client initiated the + disconnect, either by issuing a TdiDisconnect or by closing the + endpoint + +Return Value: + + None. + +--*/ + +{ + PLOOP_CONNECTION remoteConnection; + PLOOP_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + PIRP pendingIrp; + PTDI_IND_DISCONNECT disconnectHandler; + PVOID disconnectContext; + + IF_DEBUG(LOOP3) { + DbgPrint( " DoDisconnect called for connection %lx\n", + Connection ); + } + + // + // If there is a remote connection, run it down first. + // + + remoteConnection = Connection->RemoteConnection; + Connection->RemoteConnection = NULL; + + if ( (remoteConnection != NULL) && + (GET_BLOCK_STATE(remoteConnection) == BlockStateActive) ) { + SET_BLOCK_STATE( remoteConnection, BlockStateDisconnecting ); + LoopDoDisconnect( remoteConnection, FALSE ); + } + + // + // Abort pending receives and sends. + // + + listEntry = RemoveHeadList( &Connection->PendingReceiveList ); + + while ( listEntry != &Connection->PendingReceiveList ) { + + LoopDereferenceConnection( Connection ); + + RELEASE_LOOP_LOCK( "DoDisc complete Receive" ); + + pendingIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + pendingIrp->IoStatus.Status = STATUS_DISCONNECTED; + IoCompleteRequest( pendingIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "DoDisc complete Receive done" ); + + listEntry = RemoveHeadList( &Connection->PendingReceiveList ); + } + + listEntry = RemoveHeadList( &Connection->IncomingSendList ); + + while ( listEntry != &Connection->IncomingSendList ) { + + LoopDereferenceConnection( remoteConnection ); + LoopDereferenceConnection( Connection ); + + RELEASE_LOOP_LOCK( "DoDisc complete Send" ); + + pendingIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + pendingIrp->IoStatus.Status = STATUS_DISCONNECTED; + IoCompleteRequest( pendingIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "DoDisc complete Send done" ); + + listEntry = RemoveHeadList( &Connection->IncomingSendList ); + } + + // + // If this is a remotely-initiated disconnect, and the local client + // has established a disconnect event handler, call that handler + // now. + // + + endpoint = Connection->Endpoint; + disconnectHandler = endpoint->DisconnectHandler; + disconnectContext = endpoint->DisconnectContext; + + if ( !ClientInitiated && (disconnectHandler != NULL) ) { + RELEASE_LOOP_LOCK( "DoDisc call handler" ); + (VOID)disconnectHandler( + disconnectContext, + Connection->ConnectionContext, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT + ); + ACQUIRE_LOOP_LOCK( "DoDisc call handler done" ); + } + + // + // Dereference the connection. This will result in the completion + // of the disconnect IRP, if the reference count goes to zero. + // + + LoopDereferenceConnection( Connection ); + + return; + +} // LoopDoDisconnect + + +NTSTATUS +LoopListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Listen request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PLOOP_CONNECTION connection; + PLOOP_ENDPOINT endpoint; + + IrpSp; // prevent compiler warnings + + IF_DEBUG(LOOP1) DbgPrint( " Listen request\n" ); + + connection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection ); + + // + // !!! When the DELAYED_ACCEPT bit is defined, check here to ensure + // that it isn't set by the client. The loopback driver doesn't + // support delayed accept on Listen requests. + // + + // + // Make sure the connection is in the right state. + // + + ACQUIRE_LOOP_LOCK( "Listen initial" ); + + if ( connection == NULL ) { + RELEASE_LOOP_LOCK( "Listen control channel" ); + IF_DEBUG(LOOP2) DbgPrint( " Can't Listen on control channel\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_STATE(connection) != BlockStateBound ) { + RELEASE_LOOP_LOCK( "Listen not bound" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not bound\n" ); + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Set the connection state to Connecting, to prevent further + // connects or listens. + // + + SET_BLOCK_STATE( connection, BlockStateConnecting ); + + endpoint = connection->Endpoint; + IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint ); + + // + // Queue the Listen to the endpoint's Pending Listen list. + // + + InsertTailList( + &endpoint->PendingListenList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + // + // Check for a pending connect. + // + + if ( (endpoint->IndicatingConnectIrp != NULL) || + (endpoint->IncomingConnectList.Flink == + &endpoint->IncomingConnectList) ) { + + // + // There is no pending Connect, or a Connect indication is + // already in progress. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " No pending Connect; leaving IRP %lx queued \n", + Irp ); + } + + RELEASE_LOOP_LOCK( "Listen no Connect" ); + + } else { + + // + // There is a pending Connect. Call IndicateConnect to + // satisfy the Listen. + // + // *** Note that IndicateConnect returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopListen indicating connect\n" ); + IndicateConnect( endpoint ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Listen request complete\n" ); + return status; + +complete: + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 2 ); + + IF_DEBUG(LOOP1) DbgPrint( " Listen request complete\n" ); + return status; + +} // LoopListen + + +NTSTATUS +CompleteConnection ( + IN PIRP ListenIrp, + IN PIRP ConnectIrp + ) + +/*++ + +Routine Description: + + This routine completes the process of creating a connection. It + creates a connection block for each end of the connection and links + them together. The connection blocks are also linked off of their + respective endpoint blocks. + + This routine must be called with the loopback device spin lock held. + The lock remains held when the function returns, although it is + released and reacquired during the function's operation. + +Arguments: + + ListenIrp - Pointer to IRP used for Listen request + + ConnectIrp - Pointer to IRP used for Connect request + +Return Value: + + NTSTATUS - Indicates whether the connection was successfully created + +--*/ + +{ + NTSTATUS status; + PLOOP_CONNECTION listenerConnection; + PLOOP_CONNECTION connectorConnection; + PLOOP_ENDPOINT listenerEndpoint; + PLOOP_ENDPOINT connectorEndpoint; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + PTDI_CONNECTION_INFORMATION connInfo; + TA_NETBIOS_ADDRESS connectorAddress; + TA_NETBIOS_ADDRESS listenerAddress; + int length; + + // + // Get pointers. + // + + irpSp = IoGetCurrentIrpStackLocation( ListenIrp ); + listenerConnection = (PLOOP_CONNECTION)irpSp->FileObject->FsContext; + listenerEndpoint = listenerConnection->Endpoint; + irpSp = IoGetCurrentIrpStackLocation( ConnectIrp ); + connectorConnection = (PLOOP_CONNECTION)irpSp->FileObject->FsContext; + connectorEndpoint = connectorConnection->Endpoint; + + // + // Update the connection blocks. + // + + listenerConnection->RemoteConnection = connectorConnection; + SET_BLOCK_STATE( listenerConnection, BlockStateActive ); + + connectorConnection->RemoteConnection = listenerConnection; + SET_BLOCK_STATE( connectorConnection, BlockStateActive ); + + // + // Increment the reference counts on the connections to account for + // the active link. + // + + listenerConnection->BlockHeader.ReferenceCount++; + connectorConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + listenerConnection, + listenerConnection->BlockHeader.ReferenceCount ); + DbgPrint( " New refcnt on connection %lx is %lx\n", + connectorConnection, + connectorConnection->BlockHeader.ReferenceCount ); + } + + // + // Save the addresses of the connector and the listener. + // + + connectorAddress.TAAddressCount = 1; + connectorAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + connectorAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS); + connectorAddress.Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + RtlMoveMemory( + connectorAddress.Address[0].Address[0].NetbiosName, + connectorEndpoint->NetbiosName, + NETBIOS_NAME_LENGTH + ); + + listenerAddress.TAAddressCount = 1; + listenerAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + listenerAddress.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS); + listenerAddress.Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + RtlMoveMemory( + listenerAddress.Address[0].Address[0].NetbiosName, + listenerEndpoint->NetbiosName, + NETBIOS_NAME_LENGTH + ); + + // + // Complete the Listen (or Accept) and Connect I/O requests. + // + + RELEASE_LOOP_LOCK( "CompleteConnection complete IRPs" ); + + irpSp = IoGetCurrentIrpStackLocation( ListenIrp ); + parameters = (PTDI_REQUEST_KERNEL)&irpSp->Parameters; + connInfo = parameters->ReturnConnectionInformation; + + status = STATUS_SUCCESS; + + if ( connInfo != NULL ) { + + length = connInfo->RemoteAddressLength; + + if ( length != 0 ) { + length = MIN( sizeof(connectorAddress), length ); + RtlMoveMemory( + connInfo->RemoteAddress, + &connectorAddress, + length + ); + if ( length < sizeof(connectorAddress) ) { + status = STATUS_BUFFER_OVERFLOW; + } + } + + connInfo->UserDataLength = 0; + connInfo->OptionsLength = 0; + + } + + ListenIrp->IoStatus.Status = status; + + irpSp = IoGetCurrentIrpStackLocation( ConnectIrp ); + parameters = (PTDI_REQUEST_KERNEL)&irpSp->Parameters; + connInfo = parameters->ReturnConnectionInformation; + + status = STATUS_SUCCESS; + + if ( connInfo != NULL ) { + + length = connInfo->RemoteAddressLength; + + if ( length != 0 ) { + length = MIN( sizeof(listenerAddress), length ); + RtlMoveMemory( + connInfo->RemoteAddress, + &listenerAddress, + length + ); + if ( length < sizeof(listenerAddress) ) { + status = STATUS_BUFFER_OVERFLOW; + } + } + + connInfo->UserDataLength = 0; + connInfo->OptionsLength = 0; + + } + + ConnectIrp->IoStatus.Status = status; + + IoCompleteRequest( ListenIrp, 2 ); + IoCompleteRequest( ConnectIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "CompleteConnection complete IRPs done" ); + + return STATUS_SUCCESS; + +} // CompleteConnection + + +VOID +IndicateConnect ( + IN PLOOP_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + This routine does the work of indicating an incoming connect. + + The loopback device object's spin lock must be held when this + function is called. + + *** The lock is released when the function returns. + +Arguments: + + Endpoint - Pointer to receiving (listening) endpoint block + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PLIST_ENTRY listEntry; + PIRP listenIrp; + PIRP connectIrp; + PTDI_IND_CONNECT connectHandler; + PVOID connectContext; + PVOID connectionContext; + PIO_STACK_LOCATION connectIrpSp; + PLOOP_ENDPOINT connectingEndpoint; + TA_NETBIOS_ADDRESS address; + + // + // Reference the endpoint to prevent it from going away while this + // routine is running. + // + + Endpoint->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on endpoint %lx is %lx\n", + Endpoint, + Endpoint->BlockHeader.ReferenceCount ); + } + + // + // If the endpoint has a pending Listen, satisfy it with this + // Connect. If there is no pending Listen, and a Connect handler + // has been enabled on the endpoint, call it. + // + + while ( TRUE ) { + + // + // We have a Connect pending. Is there a pending Listen? + // + + listEntry = RemoveHeadList( &Endpoint->PendingListenList ); + + if ( listEntry != &Endpoint->PendingListenList ) { + + // + // Found a pending Listen. Use it to satisfy the first + // incoming Connect. + // + + listenIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Listen IRP pending: %lx\n", listenIrp ); + } + + listEntry = RemoveHeadList( + &Endpoint->IncomingConnectList + ); + ASSERT( listEntry != &Endpoint->IncomingConnectList ); + connectIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Connect IRP pending: %lx\n", connectIrp ); + } + + CompleteConnection( listenIrp, connectIrp ); + + // + // Fall to bottom of loop to handle more incoming Connects. + // + + } else { + + // + // No pending Listen. Is there a Connection handler? + // + + connectHandler = Endpoint->ConnectHandler; + connectContext = Endpoint->ConnectContext; + + if ( connectHandler == NULL ) { + + // + // No Connect handler. The Connect must remain queued. + // + + IF_DEBUG(LOOP2) DbgPrint( " No Connect handler\n" ); + + break; + + } + + // + // The endpoint has a Connect handler. Call it. If it + // returns STATUS_EVENT_DONE, it handled the event by + // issuing a TdiAccept or TdiDisconnect from within the + // handler. If it returns STATUS_EVENT_PENDING, it also + // returns a connection context value indicating which + // connection it will use to issue a TdiAccept or + // TdiDisconect at a later time. If it returns + // STATUS_INSUFFICIENT_RESOURCES, it can't accept a new + // connection at this time, so we need to return an error to + // the Connect. + // + // First, remove the first Connect from the Incoming Connect + // list, and make it the Indicating Connect. It must be + // removed from the list to ensure that it isn't completed + // by LoopCleanup while we're indicating it. + // + + listEntry = RemoveHeadList( + &Endpoint->IncomingConnectList + ); + ASSERT( listEntry != &Endpoint->IncomingConnectList ); + connectIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + Endpoint->IndicatingConnectIrp = connectIrp; + + RELEASE_LOOP_LOCK( "IndicateConnect calling handler" ); + + IF_DEBUG(LOOP2) { + DbgPrint( " Connect handler: %lx\n", connectHandler ); + } + + // + // Build a TRANSPORT_ADDRESS describing the connector. + // + + connectIrpSp = IoGetCurrentIrpStackLocation( connectIrp ); + connectingEndpoint = + ((PLOOP_CONNECTION)connectIrpSp->FileObject->FsContext)-> + Endpoint; + + address.TAAddressCount = 1; + address.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + address.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS); + address.Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + RtlMoveMemory( + address.Address[0].Address[0].NetbiosName, + connectingEndpoint->NetbiosName, + NETBIOS_NAME_LENGTH + ); + + // + // Call the Connect handler. + // + + status = connectHandler( + connectContext, + sizeof(address), + &address, + 0, + NULL, + 0, + NULL, + &connectionContext + ); + + ACQUIRE_LOOP_LOCK( "IndicateConnect after calling handler" ); + + if ( status == STATUS_EVENT_DONE ) { + + // + // The Connect handler issued a TdiAccept or a + // TdiDisconnect, so the indicating Connect has already + // been completed. + // + // *** Note that Endpoint->IndicatingConnectIrp is + // cleared within LoopAccept or LoopDisconnect. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Connect handler handled event\n" ); + } + + // + // Fall to bottom of loop to handle more incoming + // Connects. + // + + } else if ( status == STATUS_EVENT_PENDING ) { + + // + // The Connect handler has delayed the issuance of the + // TdiAccept or TdiDisconnect. Leave the indication + // pending. + // + // *** Note that Endpoint->IndicatingConnectIrp is + // cleared within LoopAccept or LoopDisconnect. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Connect handler pended event\n" ); + } + + break; + + } else { + + // + // The Connect handler couldn't accept the connection, + // because it was out of resources. Abort the Connect. + // + + ASSERT( status == STATUS_INSUFFICIENT_RESOURCES ); + + IF_DEBUG(LOOP2) DbgPrint( " No resources\n" ); + + Endpoint->IndicatingConnectIrp = NULL; + + RELEASE_LOOP_LOCK( "IndicateConnect aborting connect" ); + + connectIrp->IoStatus.Status = status; + IoCompleteRequest( connectIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "IndicateConnect connect aborted" ); + + // + // Fall to bottom of loop to handle more incoming + // Connects. + // + + } + + } // pending listen? + + // + // If we get here, we need to indicate the next incoming + // Connect, if there is one. + // + + if ( (GET_BLOCK_STATE(Endpoint) != BlockStateActive) || + (Endpoint->IncomingConnectList.Flink == + &Endpoint->IncomingConnectList) ) { + + // + // No more Connects, or endpoint no longer active. Leave. + // + + break; + + } + + // + // Process the next Connect. + // + + } // while ( TRUE ) + + // + // Remote the endpoint reference acquired at the start of this + // routine. + // + + LoopDereferenceEndpoint( Endpoint ); + + RELEASE_LOOP_LOCK( "IndicateConnect done" ); + + return; + +} // IndicateConnect diff --git a/private/ntos/tdi/loopback/datagram.c b/private/ntos/tdi/loopback/datagram.c new file mode 100644 index 000000000..0c5ec7c02 --- /dev/null +++ b/private/ntos/tdi/loopback/datagram.c @@ -0,0 +1,848 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + datagram.c + +Abstract: + + This module implements datagram logic for the loopback Transport + Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 15-Aug-1991 + +Revision History: + +--*/ + +#include "loopback.h" + +// +// Local declarations +// + +STATIC +VOID +CompleteReceiveDatagram ( + IN PIRP ReceiveIrp, + IN PIRP SendIrp + ); + +STATIC +VOID +IndicateReceiveDatagram ( + IN PLOOP_CONNECTION ReceivingConnection, + IN PIRP InitialSendIrp + ); + + +NTSTATUS +LoopReceiveDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Receive Datagram request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + PLOOP_ENDPOINT receivingEndpoint; + PLOOP_CONNECTION sendingConnection; + + IF_DEBUG(LOOP1) DbgPrint( " Receive Datagram request\n" ); + + // + // Verify that the receiving endpoint is connected. + // + + receivingEndpoint = (PLOOP_ENDPOINT)IrpSp->FileObject->FsContext; + + if ( receivingConnection == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Can't Receive on control channel\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" ); + return STATUS_INVALID_PARAMETER; + } + + sendingConnection = receivingConnection->RemoteConnection; + IF_DEBUG(LOOP2) { + DbgPrint( " Receiving connection: %lx\n", receivingConnection ); + DbgPrint( " Sending connection: %lx\n", sendingConnection ); + } + + ACQUIRE_LOOP_LOCK( "Receive initial" ); + + if ( (sendingConnection == NULL) || + (GET_BLOCK_STATE(receivingConnection) != BlockStateActive) ) { + RELEASE_LOOP_LOCK( "Receive closing" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" ); + return STATUS_DISCONNECTED; + } + + // + // Queue the Receive to the connection's Pending Receive list. + // + + InsertTailList( + &receivingConnection->PendingReceiveList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + // + // Check for pending data. + // + + if ( (receivingConnection->IndicatingSendIrp != NULL) || + (receivingConnection->IncomingSendList.Flink == + &receivingConnection->IncomingSendList) ) { + + // + // There is no pending Send, or an indication is already in + // progress. Reference the connection to account for the + // Receive IRP. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " No pending Send; leaving IRP %lx queued \n", Irp ); + } + receivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + receivingConnection, + receivingConnection->BlockHeader.ReferenceCount ); + } + + RELEASE_LOOP_LOCK( "Receive no Send" ); + + } else { + + // + // There is pending data. Call IndicateReceive to satisfy + // the Receive. + // + // *** Note that IndicateReceive returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopReceive indicating receive\n" ); + IndicateReceive( receivingConnection, NULL ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Receive request %lx complete\n", Irp ); + return STATUS_PENDING; + +} // LoopReceive + + +NTSTATUS +LoopSend ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Send request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + PLOOP_CONNECTION sendingConnection; + PLOOP_CONNECTION receivingConnection; + BOOLEAN firstSend; + + IF_DEBUG(LOOP1) DbgPrint( " Send request\n" ); + + // + // Verify that the sending connection is connected. + // + + sendingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( sendingConnection == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Can't Send on control channel\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" ); + return STATUS_INVALID_PARAMETER; + } + + receivingConnection = sendingConnection->RemoteConnection; + IF_DEBUG(LOOP2) { + DbgPrint( " Sending connection: %lx\n", sendingConnection ); + DbgPrint( " Receiving connection: %lx\n", receivingConnection ); + } + + ACQUIRE_LOOP_LOCK( "Send initial" ); + + if ( (receivingConnection == NULL) || + (GET_BLOCK_STATE(sendingConnection) != BlockStateActive) ) { + RELEASE_LOOP_LOCK( "Send closing" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" ); + return STATUS_DISCONNECTED; + } + + // + // Reference both ends. + // + + sendingConnection->BlockHeader.ReferenceCount++; + receivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + sendingConnection, + sendingConnection->BlockHeader.ReferenceCount ); + DbgPrint( " New refcnt on connection %lx is %lx\n", + receivingConnection, + receivingConnection->BlockHeader.ReferenceCount ); + } + + // + // Determine whether this is the first active incoming Send for the + // receiving connection. + // + + firstSend = (BOOLEAN)( receivingConnection->IncomingSendList.Flink == + &receivingConnection->IncomingSendList ); + + // + // Queue the Send to the receiving connection's Incoming Send list, + // in order to prevent another Send from getting ahead of this one. + // + + InsertTailList( + &receivingConnection->IncomingSendList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + if ( !firstSend || (receivingConnection->IndicatingSendIrp != NULL) ) { + + // + // Pending data already exists, or an indication is already in + // progress. This Send remains behind the already pending data. + // + + IF_DEBUG(LOOP2) DbgPrint( " Data already pending\n" ); + + RELEASE_LOOP_LOCK( "Send sends pending" ); + + } else { + + // + // Indicate the incoming data. + // + // *** Note that IndicateReceive returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopSend indicating receive\n" ); + IndicateReceive( receivingConnection, Irp ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Send request %lx complete\n", Irp ); + return STATUS_PENDING; + +} // LoopSend + + +VOID +CompleteReceive ( + IN PIRP ReceiveIrp, + IN PIRP SendIrp + ) + +/*++ + +Routine Description: + + This routine completes the process of sending a message. It copies + the message from the source buffer into the destination buffer. + + The reference counts on the owning connections must have been + incremented to account for the requesting IRPs in order to prevent + their deletion. + +Arguments: + + ReceiveIrp - Pointer to IRP used for Receive request + + SendIrp - Pointer to IRP used for Send request + +Return Value: + + NTSTATUS - Indicates whether the connection was successfully created + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION sendIrpSp; + PIO_STACK_LOCATION receiveIrpSp; + ULONG copyLength; + PTDI_REQUEST_KERNEL_SEND sendRequest; + PTDI_REQUEST_KERNEL_RECEIVE receiveRequest; + PLOOP_CONNECTION sendingConnection; + PLOOP_CONNECTION receivingConnection; + + IF_DEBUG(LOOP2) { + DbgPrint( " Send IRP %lx completes receive IRP %lx\n", + SendIrp, ReceiveIrp ); + } + + // + // Copy the data from the source buffer into the destination buffer. + // + // *** Note that we special-case zero-length copies. We don't + // bother to call LoopCopyData. Part of the reason for this + // is that a zero-length send or receive issued from user mode + // yields an IRP that has a NULL MDL address. + // + + sendIrpSp = IoGetCurrentIrpStackLocation( SendIrp ); + sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters; + copyLength = sendRequest->SendLength; + IF_DEBUG(LOOP4) { + DbgPrint( " Send request at %lx, send length %lx\n", + sendRequest, copyLength ); + } + receiveIrpSp = IoGetCurrentIrpStackLocation( ReceiveIrp ); + receiveRequest = (PTDI_REQUEST_KERNEL_RECEIVE)&receiveIrpSp->Parameters; + IF_DEBUG(LOOP4) { + DbgPrint( " Receive request at %lx, receive length %lx\n", + receiveRequest, receiveRequest->ReceiveLength ); + } + status = STATUS_SUCCESS; + if ( copyLength > receiveRequest->ReceiveLength ) { + status = STATUS_BUFFER_OVERFLOW; + copyLength = receiveRequest->ReceiveLength; + } + + if ( copyLength != 0 ) { + + ASSERT( ReceiveIrp->MdlAddress != NULL ); + ASSERT( SendIrp->MdlAddress != NULL ); + + LoopCopyData( + ReceiveIrp->MdlAddress, + SendIrp->MdlAddress, + copyLength + ); + + } + + // + // Complete the Receive and Send requests. + // + + receivingConnection = + (PLOOP_CONNECTION)receiveIrpSp->FileObject->FsContext; + sendingConnection = + (PLOOP_CONNECTION)sendIrpSp->FileObject->FsContext; + + ReceiveIrp->IoStatus.Status = status; + ReceiveIrp->IoStatus.Information = copyLength; + + SendIrp->IoStatus.Status = STATUS_SUCCESS; + SendIrp->IoStatus.Information = copyLength; + + IoCompleteRequest( ReceiveIrp, 2 ); + IoCompleteRequest( SendIrp, 2 ); + + // + // Dereference the connections. + // + + ACQUIRE_LOOP_LOCK( "CompleteReceive dereference" ); + LoopDereferenceConnection( receivingConnection ); + LoopDereferenceConnection( sendingConnection ); + RELEASE_LOOP_LOCK( "CompleteReceive dereference" ); + + return; + +} // CompleteReceive + + +VOID +IndicateReceive ( + IN PLOOP_CONNECTION ReceivingConnection, + IN PIRP InitialSendIrp + ) +{ + NTSTATUS status; + PLOOP_ENDPOINT receivingEndpoint; + PLOOP_CONNECTION sendingConnection; + PLIST_ENTRY listEntry; + PIRP receiveIrp; + PIRP sendIrp; + PTDI_IND_RECEIVE receiveHandler; + PVOID receiveContext; + PIO_STACK_LOCATION sendIrpSp; + PTDI_REQUEST_KERNEL_SEND sendRequest; + ULONG length; + PMDL mdl; + PVOID address; + ULONG bytesTaken; + + receivingEndpoint = ReceivingConnection->Endpoint; + IF_DEBUG(LOOP2) { + DbgPrint( " Receiving endpoint: %lx\n", receivingEndpoint ); + } + + // + // Reference the receiving connection to prevent it from going away + // while this routine is running. + // + + ReceivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + ReceivingConnection, + ReceivingConnection->BlockHeader.ReferenceCount ); + } + + // + // Capture the address of the sending connection, as the receiving + // connection's pointer can be zeroed if a Disconnect occurs. + // + + sendingConnection = ReceivingConnection->RemoteConnection; + ASSERT( sendingConnection != NULL ); + + // + // If the receiving connection has a pending Receive, satisfy it + // with this Send. If there is no pending Receive, and a Receive + // handler has been enabled on the receiving connection, call it. + // If the Receive handler returns with a Receive IRP, use it to + // satisfy this Send. If the Receive handler doesn't return an IRP, + // leave this Send pending. + // + // !!! Note that the current implementation only works because + // partial sends are not supported. + // + + while ( TRUE ) { + + // + // We have a Send pending. Is there a pending Receive? + // + + listEntry = RemoveHeadList( &ReceivingConnection->PendingReceiveList ); + + if ( listEntry != &ReceivingConnection->PendingReceiveList ) { + + // + // Found a pending Receive. Use it to satisfy the first + // incoming Send. + // + + receiveIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Receive IRP pending: %lx\n", receiveIrp ); + } + + listEntry = RemoveHeadList( + &ReceivingConnection->IncomingSendList + ); + ASSERT( listEntry != &ReceivingConnection->IncomingSendList ); + sendIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Send IRP pending: %lx\n", sendIrp ); + } + + // + // If this is the first time through the loop, and we were + // called to process a newly queued Send, dereference the + // receiving connection -- LoopSend referenced the + // connection an extra time in case the Send had to remain + // queued. + // + + if ( InitialSendIrp != NULL ) { + LoopDereferenceConnection( ReceivingConnection ); + } + + RELEASE_LOOP_LOCK( "IndicateReceive pending Receive" ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive pending Receive completed" ); + + // + // Fall to bottom of loop to handle more incoming Sends. + // + + } else { + + // + // No pending Receive. Is there a Receive handler? + // + + receiveHandler = receivingEndpoint->ReceiveHandler; + receiveContext = receivingEndpoint->ReceiveContext; + + if ( receiveHandler == NULL ) { + + // + // No Receive handler. The Send must remain queued. + // + + IF_DEBUG(LOOP2) DbgPrint( " No Receive handler\n" ); + + break; + + } + + // + // The receiving endpoint has a Receive handler. Call it. + // If it returns STATUS_SUCCESS, it completely handled the + // data. If it returns STATUS_MORE_PROCESSING_REQUIRED, it + // also returns a Receive IRP describing where to put the + // data. Any other return status means the receiver can't + // take the data just now, so we leave the Send queued and + // wait for the receiver to post a Receive IRP. + // + // !!! Note that we don't currently handle partial data + // acceptance. + // + // First, remove the first Send from the Incoming Send list, + // and make it the Indicating Send. It must be removed from + // the list to ensure that it isn't completed by + // LoopDoDisconnection while we're indicating it. + // + + listEntry = RemoveHeadList( + &ReceivingConnection->IncomingSendList + ); + ASSERT( listEntry != &ReceivingConnection->IncomingSendList ); + sendIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + ReceivingConnection->IndicatingSendIrp = sendIrp; + + RELEASE_LOOP_LOCK( "IndicateReceive calling Receive handler" ); + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler: %lx\n", receiveHandler ); + DbgPrint( " Send IRP: %lx\n", sendIrp ); + } + + sendIrpSp = IoGetCurrentIrpStackLocation( sendIrp ); + sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters; + + length = sendRequest->SendLength; + ASSERTMSG( + "Loopback driver doesn't handle partial or expedited sends", + sendRequest->SendFlags == 0 + ); + + // + // Map the send buffer, if necessary. + // + + mdl = sendIrp->MdlAddress; + if ( MmGetMdlByteCount(mdl) == 0 ) { + address = NULL; + } else { + address = MmGetSystemAddressForMdl( mdl ); + } + + // + // Call the Receive handler. + // + + status = receiveHandler( + receiveContext, + ReceivingConnection->ConnectionContext, + 0, + MmGetMdlByteCount( mdl ), + length, + &bytesTaken, + address, + &receiveIrp + ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive after calling handler" ); + IF_DEBUG(LOOP2) { + DbgPrint( " Indication for send IRP %lx done\n", sendIrp ); + } + + ReceivingConnection->IndicatingSendIrp = NULL; + + if ( status == STATUS_SUCCESS ) { + + // + // The Receive handler completely handled the data. + // Complete the Send. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler handled data\n" ); + } + + ASSERTMSG( + "Loopback driver doesn't handle partial acceptance " + "of indications", + bytesTaken == length + ); + + // + // Dereference the sending and receiving connections -- + // LoopSend referenced the connections when it queued + // the Send. + // + + LoopDereferenceConnection( ReceivingConnection ); + LoopDereferenceConnection( sendingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive completely handled" ); + + // + // Complete the Send IRP. + // + + sendIrp->IoStatus.Status = STATUS_SUCCESS; + sendIrp->IoStatus.Information = sendRequest->SendLength; + + IoCompleteRequest( sendIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive send completed" ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else if ( status == STATUS_MORE_PROCESSING_REQUIRED ) { + + // + // The Receive handler returned a Receive IRP to be used + // to satisfy the Send. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler returned IRP: %lx\n", + receiveIrp ); + } + + ASSERTMSG( + "Loopback driver doesn't handle partial acceptance " + "of indications", + bytesTaken == 0 + ); + + // + // Complete the Receive using the current Send. + // + // *** Note that the pending Send references both the + // sending and receiving connections. + + RELEASE_LOOP_LOCK( "IndicateReceive complete new Receive" ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive new receive completed" ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else { + + // + // The Receive handler couldn't take the data. This + // Send will have to wait until receiver can post a + // Receive IRP. + // + // *** Because we didn't hold the spin lock around the + // call to the Receive handler, it's possible that + // the receiver has already posted a Receive IRP. + // Because we were in the middle of an indication, + // that Receive would have been queued to the + // Pending Receive list, and we should go get it + // now. If the receiver hasn't posted a Receive + // yet, then this Send will be put back on the + // Incoming Send list before the Receive does come + // in (since we're now holding the lock). + // + + ASSERT( status == STATUS_DATA_NOT_ACCEPTED ); + + IF_DEBUG(LOOP2) DbgPrint( " Data not accepted\n" ); + + if ( GET_BLOCK_STATE(ReceivingConnection) != + BlockStateActive ) { + + // + // The connection is closing. Abort the current + // Send and leave. + // + + LoopDereferenceConnection( ReceivingConnection ); + LoopDereferenceConnection( sendingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive disconnecting" ); + + sendIrp->IoStatus.Status = STATUS_DISCONNECTED; + IoCompleteRequest( sendIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive send aborted" ); + + break; + + } + + listEntry = RemoveHeadList( + &ReceivingConnection->PendingReceiveList + ); + + if ( listEntry != + &ReceivingConnection->PendingReceiveList ) { + + // + // A Receive has been posted. Use it to satisfy + // this Send. + // + + receiveIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Receive IRP pending: %lx\n", + receiveIrp ); + } + + // + // Complete the Receive using the current Send. + // + + RELEASE_LOOP_LOCK( + "IndicateReceive complete posted Receive" + ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( + "IndicateReceive posted receive completed" + ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else { + + // + // The handler didn't take the data, and it didn't + // post a Receive IRP. Requeue the current send and + // get out. + // + + InsertHeadList( + &ReceivingConnection->IncomingSendList, + &sendIrp->Tail.Overlay.ListEntry + ); + + break; + + } + + } + + } // pending receive? + + // + // If we get here, we need to indicate the next incoming Send, + // if there is one. + // + + InitialSendIrp = NULL; + + if ( (GET_BLOCK_STATE(ReceivingConnection) != BlockStateActive) || + (ReceivingConnection->IncomingSendList.Flink == + &ReceivingConnection->IncomingSendList) ) { + + // + // No more Sends, or connection no longer active. Leave. + // + + break; + + } + + // + // Process the next Send. + // + + } // while ( TRUE ) + + // + // Remove the connection reference acquired at the start of this + // routine. + // + + LoopDereferenceConnection( ReceivingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive done" ); + + return; + +} // IndicateReceive + diff --git a/private/ntos/tdi/loopback/endpoint.c b/private/ntos/tdi/loopback/endpoint.c new file mode 100644 index 000000000..fd252d291 --- /dev/null +++ b/private/ntos/tdi/loopback/endpoint.c @@ -0,0 +1,373 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + endpoint.c + +Abstract: + + This module implements endpoint logic for the loopback Transport + Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 15-Aug-1991 + +Revision History: + +--*/ + +#include "loopback.h" + + +VOID +LoopDereferenceEndpoint ( + IN PLOOP_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + This routine is called to dereference an endpoint block. If the + reference count on the block goes to zero, the endpoint block is + deleted. + + The Loopback device object's spin lock must be held when this + routine is called. + +Arguments: + + Endpoint - Supplies a pointer to an endpoint block + +Return Value: + + None. + +--*/ + +{ + PIRP closeIrp; + + IF_DEBUG(LOOP3) { + DbgPrint( " Dereferencing endpoint %lx; old refcnt %lx\n", + Endpoint, Endpoint->BlockHeader.ReferenceCount ); + } + + ASSERT( (LONG)Endpoint->BlockHeader.ReferenceCount > 0 ); + + if ( --Endpoint->BlockHeader.ReferenceCount != 0 ) { + return; + } + + IF_DEBUG(LOOP3) DbgPrint( " Deleting endpoint %lx\n", Endpoint ); + + RemoveEntryList( &Endpoint->DeviceListEntry ); + + RELEASE_LOOP_LOCK( "DerefEndp deleting" ); + + ObDereferenceObject( Endpoint->DeviceObject ); + + // + // If a Close IRP is pending, complete it now. + // + + closeIrp = Endpoint->CloseIrp; + + if ( closeIrp != NULL ) { + + IF_DEBUG(LOOP3) { + DbgPrint( " Completing Close IRP %lx\n", closeIrp ); + } + closeIrp->IoStatus.Status = STATUS_SUCCESS; + closeIrp->IoStatus.Information = 0; + + IoCompleteRequest( closeIrp, 2 ); + + } + + DEBUG SET_BLOCK_TYPE( Endpoint, BlockTypeGarbage ); + DEBUG SET_BLOCK_STATE( Endpoint, BlockStateDead ); + DEBUG SET_BLOCK_SIZE( Endpoint, -1 ); + DEBUG Endpoint->BlockHeader.ReferenceCount = -1; + DEBUG Endpoint->DeviceObject = NULL; + + ExFreePool( Endpoint ); + + ACQUIRE_LOOP_LOCK( "DerefEndp done" ); + + return; + +} // LoopDereferenceEndpoint + + +PLOOP_ENDPOINT +LoopFindBoundAddress ( + IN PCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine searches the list of endpoints for the loopback device + for one that has the specified address bound to it. + + The loopback device object's spin lock must be owned when this + function is called. + +Arguments: + + NetbiosName - Pointer to NetBIOS name string, formatted according + to TDI specification. + +Return Value: + + PLOOP_OBJECT - Pointer to LOOP_ENDPOINT block if matching address + found, else NULL + +--*/ + +{ + PLIST_ENTRY listEntry; + PLOOP_ENDPOINT endpoint; + LONG compareResult; + CLONG i; + + listEntry = LoopDeviceObject->EndpointList.Flink; + + while ( listEntry != &LoopDeviceObject->EndpointList ) { + + endpoint = CONTAINING_RECORD( + listEntry, + LOOP_ENDPOINT, + DeviceListEntry + ); + + if ( GET_BLOCK_STATE(endpoint) == BlockStateActive ) { + + compareResult = 0; + for ( i = 0; i < NETBIOS_NAME_LENGTH; i++ ) { + if ( endpoint->NetbiosName[i] != NetbiosName[i] ) { + compareResult = 1; + break; + } + } + + if ( compareResult == 0 ) { + return endpoint; + } + + } + + listEntry = listEntry->Flink; + + } + + return NULL; + +} // LoopFindBoundAddress + + +NTSTATUS +LoopSetEventHandler ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Set Receive Handler request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PTDI_REQUEST_KERNEL_SET_EVENT setEventRequest; + PLOOP_ENDPOINT endpoint; + PVOID handler; + PVOID context; + PSZ s; + + IrpSp; // prevent compiler warnings + + IF_DEBUG(LOOP1) DbgPrint( " SetEventHandler request\n" ); + + setEventRequest = (PTDI_REQUEST_KERNEL_SET_EVENT)&IrpSp->Parameters; + + handler = setEventRequest->EventHandler; + context = setEventRequest->EventContext; + + // + // Get the endpoint address and update the handler address. + // + + ACQUIRE_LOOP_LOCK( "SetEventHandler initial" ); + + endpoint = (PLOOP_ENDPOINT)IrpSp->FileObject->FsContext; + IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint ); + + if ( endpoint == NULL ) { + IF_DEBUG(LOOP2) { + DbgPrint( " Can't SetEventHandler on control channel\n" ); + } + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if ( GET_BLOCK_TYPE(endpoint) != BlockTypeLoopEndpoint ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + switch ( setEventRequest->EventType ) { + + case TDI_EVENT_CONNECT: + + IF_DEBUG(LOOP2) s = "connect"; + + endpoint->ConnectHandler = (PTDI_IND_CONNECT)handler; + endpoint->ConnectContext = context; + + break; + + case TDI_EVENT_DISCONNECT: + + IF_DEBUG(LOOP2) s = "disconnect"; + + endpoint->DisconnectHandler = (PTDI_IND_DISCONNECT)handler; + endpoint->DisconnectContext = context; + + break; + + case TDI_EVENT_ERROR: + + IF_DEBUG(LOOP2) s = "error"; + + endpoint->ErrorHandler = (PTDI_IND_ERROR)handler; + endpoint->ErrorContext = context; + + break; + + case TDI_EVENT_RECEIVE: + + IF_DEBUG(LOOP2) s = "receive"; + + endpoint->ReceiveHandler = (PTDI_IND_RECEIVE)handler; + endpoint->ReceiveContext = context; + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + + IF_DEBUG(LOOP2) s = "receive datagram"; + + endpoint->ReceiveDatagramHandler = (PTDI_IND_RECEIVE_DATAGRAM)handler; + endpoint->ReceiveDatagramContext = context; + + break; + + case TDI_EVENT_RECEIVE_EXPEDITED: + + IF_DEBUG(LOOP2) s = "receive expedited"; + + endpoint->ReceiveExpeditedHandler = + (PTDI_IND_RECEIVE_EXPEDITED)handler; + endpoint->ReceiveExpeditedContext = context; + + break; + + default: + + DEBUG { + DbgPrint( " Invalid event type: %lx\n", + setEventRequest->EventType ); + } + ASSERT( FALSE ); + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + IF_DEBUG(LOOP2) { + DbgPrint( " New %s handler address: %lx, context: %lx\n", + s, handler, context ); + } + +complete: + + RELEASE_LOOP_LOCK( "SetEventHandler final" ); + + // + // Complete the I/O request. + // + + Irp->IoStatus.Status = status; + + IoCompleteRequest( Irp, 2 ); + + IF_DEBUG(LOOP1) DbgPrint( " SetReceiveHandler request complete\n" ); + return status; + +} // LoopSetEventHandler + + +NTSTATUS +LoopVerifyEndpoint ( + IN PLOOP_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + This routine verifies that the specified endpoint block is really + an endpoint created by the loopback driver. + + The loopback device object's spin lock must be owned when this + function is called. + +Arguments: + + Endpoint - Pointer to endpoint block + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_INVALID_PARAMETER + +--*/ + +{ + PLIST_ENTRY listEntry; + + listEntry = LoopDeviceObject->EndpointList.Flink; + + while ( listEntry != &LoopDeviceObject->EndpointList ) { + + if ( CONTAINING_RECORD( listEntry, LOOP_ENDPOINT, DeviceListEntry ) == + Endpoint ) { + return STATUS_SUCCESS; + } + + listEntry = listEntry->Flink; + + } + + return STATUS_INVALID_PARAMETER; + +} // LoopVerifyEndpoint + diff --git a/private/ntos/tdi/loopback/info.c b/private/ntos/tdi/loopback/info.c new file mode 100644 index 000000000..6d71c41f6 --- /dev/null +++ b/private/ntos/tdi/loopback/info.c @@ -0,0 +1,183 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + info.c + +Abstract: + + This module implements query/set information logic for the loopback + Transport Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 6-Nov-1991 + +Revision History: + +--*/ + +#include "loopback.h" + +#include +#include + + +NTSTATUS +LoopQueryInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This function handles the TdiQueryInformation request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryRequest; + + IF_DEBUG(LOOP1) DbgPrint( " Query Information request\n" ); + + queryRequest = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters; + + switch ( queryRequest->QueryType ) { + + case TDI_QUERY_BROADCAST_ADDRESS: + { + PTA_NETBIOS_ADDRESS address; + + if ( Irp->MdlAddress->ByteCount < sizeof(TA_NETBIOS_ADDRESS) ) { + + status = STATUS_BUFFER_TOO_SMALL; + + } else { + + address = MmGetSystemAddressForMdl( Irp->MdlAddress ); + + address->TAAddressCount = 1; + address->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + address->Address[0].AddressLength = 0; + + Irp->IoStatus.Information = sizeof(TA_NETBIOS_ADDRESS); + + status = STATUS_SUCCESS; + + } + + break; + } + + case TDI_QUERY_PROVIDER_INFORMATION: + { + PTDI_PROVIDER_INFO providerInfo; + + if ( Irp->MdlAddress->ByteCount < sizeof(TDI_PROVIDER_INFO) ) { + + status = STATUS_BUFFER_TOO_SMALL; + + } else { + + providerInfo = MmGetSystemAddressForMdl( Irp->MdlAddress ); + + ACQUIRE_LOOP_LOCK( "Query Information copy provider info" ); + RtlMoveMemory( + providerInfo, + &LoopProviderInfo, + sizeof(TDI_PROVIDER_INFO) + ); + RELEASE_LOOP_LOCK( "Query Information copy provider info done" ); + + Irp->IoStatus.Information = sizeof(TDI_PROVIDER_INFO); + + status = STATUS_SUCCESS; + + } + + break; + } + + case TDI_QUERY_ADAPTER_STATUS: + { + PADAPTER_STATUS adapterStatus; + PNAME_BUFFER name; + + if ( Irp->MdlAddress->ByteCount < + (sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER)) ) { + + status = STATUS_BUFFER_TOO_SMALL; + + } else { + + adapterStatus = MmGetSystemAddressForMdl( Irp->MdlAddress ); + + RtlZeroMemory( + adapterStatus, + sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER) + ); + + adapterStatus->rev_major = 3; + adapterStatus->rev_minor = 0x02; + adapterStatus->free_ncbs = 0xffff; + adapterStatus->max_cfg_ncbs = 0xffff; + adapterStatus->max_ncbs = 0xffff; + adapterStatus->max_dgram_size = 0xffff; + adapterStatus->max_cfg_sess = 0xffff; + adapterStatus->max_sess = 0xffff; + adapterStatus->max_sess_pkt_size = 0xffff; + adapterStatus->name_count = 1; + + name = (PNAME_BUFFER)(adapterStatus + 1); + name->name_num = 1; + name->name_flags = REGISTERED | UNIQUE_NAME; + + Irp->IoStatus.Information = + sizeof(ADAPTER_STATUS) + sizeof(NAME_BUFFER); + + status = STATUS_SUCCESS; + + } + + break; + } + + case TDI_QUERY_SESSION_STATUS: + + status = STATUS_NOT_IMPLEMENTED; + + break; + + default: + + status = STATUS_INVALID_PARAMETER; + + } + + // + // Complete the Query Information request. + // + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) DbgPrint( " Query Information request complete\n" ); + return status; + +} // LoopQueryInformation + diff --git a/private/ntos/tdi/loopback/loopback.c b/private/ntos/tdi/loopback/loopback.c new file mode 100644 index 000000000..00880f56d --- /dev/null +++ b/private/ntos/tdi/loopback/loopback.c @@ -0,0 +1,1284 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + loopback.c + +Abstract: + + This module implements a loopback Transport Provider driver for NT + LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 8-Oct-1989 + +Revision History: + +--*/ + +#include "loopback.h" + +extern POBJECT_TYPE *IoDeviceObjectType; + +// +// Global variables +// + +ULONG LoopDebug = 0; + +// +// The address of the loopback device object (there's only one) is kept +// in global storage to avoid having to pass it from routine to routine. +// + +PLOOP_DEVICE_OBJECT LoopDeviceObject; + +// +// LoopProviderInfo is a structure containing information that may be +// obtained using TdiQueryInformation. +// + +TDI_PROVIDER_INFO LoopProviderInfo; + +// +// I/O system forward declarations +// + +STATIC +NTSTATUS +LoopDispatchCleanup ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +STATIC +NTSTATUS +LoopDispatchClose ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +STATIC +NTSTATUS +LoopDispatchCreate ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +STATIC +NTSTATUS +LoopDispatchDeviceControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +STATIC +NTSTATUS +LoopDispatchInternalDeviceControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +STATIC +VOID +LoopUnload ( + IN PDRIVER_OBJECT DriverObject + ); + + +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This is the initialization routine for the LAN Manager loopback + driver. This routine creates the device object for the loopback + device and performs all other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + STRING deviceName; + UNICODE_STRING unicodeString; + +#ifdef MEMPRINT + MemPrintInitialize( ); +#endif + + IF_DEBUG(LOOP1) DbgPrint( "LoopInitialize entered\n" ); + + // + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + // + + RtlInitString( &deviceName, LOOPBACK_DEVICE_NAME ); + + status = RtlAnsiStringToUnicodeString( + &unicodeString, + &deviceName, + TRUE + ); + + ASSERT( NT_SUCCESS(status) ); + + status = IoCreateDevice( + DriverObject, // DriverObject + LOOP_DEVICE_EXTENSION_LENGTH, // DeviceExtension + &unicodeString, // DeviceName + FILE_DEVICE_NETWORK, // DeviceType + 0, // DeviceCharacteristics + FALSE, // Exclusive + (PDEVICE_OBJECT *) &LoopDeviceObject // DeviceObject + ); + + RtlFreeUnicodeString( &unicodeString ); + + if ( !NT_SUCCESS(status) ) { + return status; + } + + IF_DEBUG(LOOP1) DbgPrint( " Loop device object: %lx\n", LoopDeviceObject ); + + // + // Initialize the driver object for this driver's entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = LoopDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = LoopDispatchCleanup; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = LoopDispatchClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + LoopDispatchDeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + LoopDispatchInternalDeviceControl; + DriverObject->DriverUnload = LoopUnload; + + // + // Allocate the spin lock. + // + + KeInitializeSpinLock( &LoopDeviceObject->SpinLock ); + + DEBUG LoopDeviceObject->SavedIrql = (KIRQL)-1; + + // + // Initialize the address and connection endpoint list heads. + // + + InitializeListHead( &LoopDeviceObject->EndpointList ); + InitializeListHead( &LoopDeviceObject->ConnectionList ); + + // + // Initialize the provider information structure. + // + + RtlZeroMemory( &LoopProviderInfo, sizeof(LoopProviderInfo) ); + LoopProviderInfo.Version = 2; // !!! Need to get this into tdi2.h + LoopProviderInfo.MaxTsduSize = MAXULONG; + LoopProviderInfo.MaxDatagramSize = MAXULONG; + LoopProviderInfo.ServiceFlags = TDI_SERVICE_CONNECTION_MODE | + TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_MULTICAST_SUPPORTED; + LoopProviderInfo.MinimumLookaheadData = 256; + LoopProviderInfo.MaximumLookaheadData = 256; + + IF_DEBUG(LOOP1) DbgPrint( "LoopInitialize complete\n" ); + + return STATUS_SUCCESS; + +} // LoopInitialize + + +NTSTATUS +LoopDispatchCleanup( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Cleanup functions for the LAN + Manager loopback driver. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PBLOCK_HEADER blockHeader; + PLOOP_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + PIRP pendingIrp; + PLOOP_CONNECTION connection; + BLOCK_STATE oldState; + PLOOP_CONNECTION previousConnection; + + DeviceObject; // not otherwise referenced if !DBG + + ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject ); + // only one loopback device + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCleanup entered for IRP %lx\n", Irp ); + } + + // + // Initialize the I/O status block. + // + + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( irpSp->MajorFunction == IRP_MJ_CLEANUP ); + + ACQUIRE_LOOP_LOCK( "DispatchCleanup initial" ); + + blockHeader = (PBLOCK_HEADER)irpSp->FileObject->FsContext; + + if ( blockHeader == NULL ) { + + // + // A control channel is being cleaned up. We need do nothing. + // + + IF_DEBUG(LOOP2) DbgPrint( " cleaning up control channel\n" ); + + } else if ( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopConnection ) { + + // + // A connection file object is being cleaned up. Reference the + // connection to keep it around while we perform the cleanup. + // + + connection = (PLOOP_CONNECTION)blockHeader; + IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection ); + + connection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + connection, connection->BlockHeader.ReferenceCount ); + } + + // + // Acquire the spin lock. Set the connection state to Closing. + // This will prevent other requests from being initiated. + // + // *** Note the assumption that this routine is only entered + // once. (That's the way the I/O system is supposed to + // work.) We don't check to see if the cleanup has already + // been initiated. + // + // *** In the rundown code, we release the lock, then reacquire + // it temporarily to remove things from lists and + // dereference the connection. We do this because we don't + // want to hold a spin lock for a long time. + // + + oldState = GET_BLOCK_STATE( connection ); + SET_BLOCK_STATE( connection, BlockStateClosing ); + + // + // If a Connect or Listen is active, abort it now. + // + + if ( oldState == BlockStateConnecting ) { + + pendingIrp = connection->ConnectOrListenIrp; + connection->ConnectOrListenIrp = NULL; + + ASSERT( pendingIrp != NULL ); + + RemoveEntryList( &pendingIrp->Tail.Overlay.ListEntry ); + + RELEASE_LOOP_LOCK( "DispatchCleanup complete conn/listen" ); + + pendingIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( pendingIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "DispatchCleanup conn/listen completed" ); + + } + + // + // Disconnect the connection, if necessary. + // + + if ( oldState == BlockStateActive ) { + LoopDoDisconnect( connection, TRUE ); + } + + // + // If the connection was bound, unbind it now. + // + + if ( oldState == BlockStateBound ) { + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + connection->Endpoint = NULL; + RemoveEntryList( &connection->EndpointListEntry ); + LoopDereferenceEndpoint( endpoint ); + } + + // + // Dereference the connection. + // + + LoopDereferenceConnection( connection ); + + RELEASE_LOOP_LOCK( "DispatchCleanup(conn) done" ); + + } else { + + // + // An endpoint file object is being cleaned up. + // + + endpoint = (PLOOP_ENDPOINT)blockHeader; + IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint ); + + // + // Acquire the spin lock. Set the endpoint state to Closing. + // This will prevent other requests (Listens and Connects) from + // being initiated. + // + // *** Note the assumption that this routine is only entered + // once. (That's the way the I/O system is supposed to + // work.) We don't check to see if the cleanup has already + // been initiated. + // + // *** In the rundown code, we release the lock, then reacquire + // it temporarily to remove things from lists and + // dereference the endpoint. We do this because we don't + // want to hold a spin lock for a long time. + // + + SET_BLOCK_STATE( endpoint, BlockStateClosing ); + + // + // Abort pending listens. + // + + listEntry = RemoveHeadList( &endpoint->PendingListenList ); + + while ( listEntry != &endpoint->PendingListenList ) { + + // + // A pending listen was found. Complete the listen with an + // error status. Get the next listen. + // + + RELEASE_LOOP_LOCK( "DispatchCleanup complete Listen" ); + + pendingIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + pendingIrp->IoStatus.Status = STATUS_ENDPOINT_CLOSED; + IoCompleteRequest( pendingIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "DispatchCleanup dequeue Listen" ); + + listEntry = RemoveHeadList( &endpoint->PendingListenList ); + } + + // + // Abort pending connects. + // + + listEntry = RemoveHeadList( &endpoint->IncomingConnectList ); + + while ( listEntry != &endpoint->IncomingConnectList ) { + + // + // A pending connect was found. Complete the connect with + // an error status. Get the next connect. + // + + RELEASE_LOOP_LOCK( "DispatchCleanup complete Connect" ); + + pendingIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + pendingIrp->IoStatus.Status = STATUS_ENDPOINT_CLOSED; + IoCompleteRequest( pendingIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "DispatchCleanup complete Connect" ); + + listEntry = RemoveHeadList( &endpoint->IncomingConnectList ); + } + + // + // Disconnect or unbind all bound connections. + // + // *** This loop is complicated by the fact that we can't remove + // connections from the list before disconnecting them, yet + // we want to keep the list consistent while we walk it. Be + // careful making changes to this loop! + // + + previousConnection = NULL; + + listEntry = endpoint->ConnectionList.Flink; + + while ( listEntry != &endpoint->ConnectionList ) { + + // + // A bound connection was found. If the connection's + // reference count is not already 0, reference it to keep it + // from going away. If the count is 0, skip to the next + // connection. + // + + connection = CONTAINING_RECORD( + listEntry, + LOOP_CONNECTION, + EndpointListEntry + ); + + if ( connection->BlockHeader.ReferenceCount == 0 ) { + + // + // Find the next connection in the list and loop. + // + + listEntry = listEntry->Flink; + continue; + + } + + connection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + connection, connection->BlockHeader.ReferenceCount ); + } + + // + // Dereference the previous connection, if any. + // + + if ( previousConnection != NULL ) { + LoopDereferenceConnection( previousConnection ); + } + previousConnection = connection; + + // + // Disconnect or unbind the current connection. It won't be + // deleted. + // + + if ( GET_BLOCK_STATE(connection) == BlockStateActive ) { + + // + // Disconnect the connection. + // + + SET_BLOCK_STATE( connection, BlockStateDisconnecting ); + LoopDoDisconnect( connection, TRUE ); + + // + // Find the next connection in the list. + // + + listEntry = listEntry->Flink; + + } else if ( GET_BLOCK_STATE(connection) == BlockStateBound ) { + + // + // Find the next connection in the list. + // + + listEntry = listEntry->Flink; + + // + // Unbind the connection. + // + + ASSERT( connection->Endpoint == endpoint ); + connection->Endpoint = NULL; + RemoveEntryList( &connection->EndpointListEntry ); + SET_BLOCK_STATE( connection, BlockStateUnbound ); + LoopDereferenceEndpoint( connection->Endpoint ); + + } else { + + // + // Find the next connection in the list. + // + + listEntry = listEntry->Flink; + + } + + } + + // + // Dereference the previous connection, if any. + // + + if ( previousConnection != NULL ) { + LoopDereferenceConnection( previousConnection ); + } + + // + // The spin lock is still held here. Cancel the receive handler. + // + // *** Note that we do not dereference the endpoint here. That is + // done in the Close handler. We have already set the state + // of the endpoint to closing, which will prevent any further + // activity from occurring. + // + + endpoint->FileObject = NULL; + endpoint->ReceiveHandler = NULL; + + RELEASE_LOOP_LOCK( "DispatchCleanup final" ); + + } // connection vs. endpoint + + // + // Successful completion. Complete the I/O request. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( Irp, 2 ); + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCleanup complete for IRP %lx\n", Irp ); + } + + return STATUS_SUCCESS; + +} // LoopDispatchCleanup + + +NTSTATUS +LoopDispatchClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Close functions for the LAN + Manager loopback driver. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PBLOCK_HEADER blockHeader; + PLOOP_ENDPOINT endpoint; + PLOOP_CONNECTION connection; + + DeviceObject; // not otherwise referenced if !DBG + + ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject ); + // only one loopback device + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchClose entered for IRP %lx\n", Irp ); + } + + // + // Initialize the I/O status block. + // + + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( irpSp->MajorFunction == IRP_MJ_CLOSE ); + + ACQUIRE_LOOP_LOCK( "DispatchClose initial" ); + + blockHeader = (PBLOCK_HEADER)irpSp->FileObject->FsContext; + + if ( blockHeader == NULL ) { + + // + // A control channel is being closed. We need do nothing. + // + + IF_DEBUG(LOOP2) DbgPrint( " closing control channel\n" ); + + } else if ( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopConnection ) { + + // + // A connection file object is being closed. + // + + connection = (PLOOP_CONNECTION)blockHeader; + IF_DEBUG(LOOP2) DbgPrint( " Connection address: %lx\n", connection ); + + // + // All external references to the file object (and thus the + // connection) are gone, but the connection block hasn't been + // deleted. This implies that a Disconnect is in progress, + // and when that operation completes, the connection block will + // be deleted. We need to set up for the Close IRP to be + // completed at that time. Reference and dereference the + // connection to allow it to be deleted. + // + + connection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + connection, connection->BlockHeader.ReferenceCount ); + } + + SET_BLOCK_STATE( connection, BlockStateClosed ); + + connection->CloseIrp = Irp; + IoMarkIrpPending( Irp ); + + LoopDereferenceConnection( connection ); + + RELEASE_LOOP_LOCK( "DispatchClose(conn) final" ); + + } else { + + ASSERT( GET_BLOCK_TYPE(blockHeader) == BlockTypeLoopEndpoint ); + + endpoint = (PLOOP_ENDPOINT)irpSp->FileObject->FsContext; + IF_DEBUG(LOOP2) DbgPrint( " Endpoint address: %lx\n", endpoint ); + + // + // All external references to the file object (and thus the + // endpoint) are gone. Normally, the only remaining internal + // reference to the endpoint is the one that keeps the endpoint + // "open". Eliminate that reference. The CloseIrp field in the + // endpoint is used to remember the IRP that must be completed + // when the reference count goes to 0. + // + // *** Because LoopDereferenceEndpoint may or may not complete + // the Close IRP, we return STATUS_PENDING from the service + // call. We must mark this fact in the IRP before calling + // LoopDereferenceEndpoint. + // + + endpoint->CloseIrp = Irp; + IoMarkIrpPending( Irp ); + + LoopDereferenceEndpoint( endpoint ); + + RELEASE_LOOP_LOCK( "DispatchClose final" ); + + } + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchClose complete (pending) for IRP %lx\n", + Irp ); + } + + return STATUS_PENDING; + +} // LoopDispatchClose + + +NTSTATUS +LoopDispatchCreate( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Create functions for the LAN + Manager loopback driver. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + BLOCK_TYPE type; + PIO_STACK_LOCATION irpSp; + PLOOP_ENDPOINT endpoint; + PLOOP_ENDPOINT existingEndpoint; + PLOOP_CONNECTION connection; + + DeviceObject; // not otherwise referenced if !DBG + + ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject ); + // only one loopback device + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCreate entered for IRP %lx\n", Irp ); + } + + // + // Initialize the I/O status block. + // + + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( irpSp->MajorFunction == IRP_MJ_CREATE ); + + // + // Determine whether an address endpoint or a connection endpoint is + // being created, or if a control channel is being opened. + // + + if ( Irp->AssociatedIrp.SystemBuffer == NULL ) { + + // + // A control channel is being opened. This channel is used + // only to get provider information and to determine the + // provider's broadcast address. + // + + IF_DEBUG(LOOP2) DbgPrint( " opening control channel\n" ); + + irpSp->FileObject->FsContext = NULL; + + } else { + + status = LoopGetEndpointTypeFromEa( + (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer, + &type + ); + + if ( !NT_SUCCESS(status) ) { + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + return status; + } + + if ( type == BlockTypeLoopEndpoint ) { + + // + // An address endpoint is being created. + // + // Allocate a LOOP_ENDPOINT block to describe the transport + // endpoint. Initialize it. + // + + endpoint = ExAllocatePool( NonPagedPool, sizeof(LOOP_ENDPOINT) ); + if ( endpoint == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Unable to allocate pool\n" ); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCreate complete for IRP %lx\n", + Irp ); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + IF_DEBUG(LOOP2) { + DbgPrint( " Endpoint allocated: %lx\n", endpoint ); + } + SET_BLOCK_TYPE( endpoint, BlockTypeLoopEndpoint ); + SET_BLOCK_STATE( endpoint, BlockStateActive ); + SET_BLOCK_SIZE( endpoint, sizeof(LOOP_ENDPOINT) ); + endpoint->BlockHeader.ReferenceCount = 1; // for file object + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on endpoint %lx is %lx\n", + endpoint, endpoint->BlockHeader.ReferenceCount ); + } + + InitializeListHead( &endpoint->ConnectionList ); + InitializeListHead( &endpoint->PendingListenList ); + InitializeListHead( &endpoint->IncomingConnectList ); + endpoint->IndicatingConnectIrp = NULL; + + endpoint->FileObject = irpSp->FileObject; + endpoint->ConnectHandler = NULL; + endpoint->ReceiveHandler = NULL; + endpoint->ReceiveDatagramHandler = NULL; + endpoint->ReceiveExpeditedHandler = NULL; + endpoint->DisconnectHandler = NULL; + endpoint->ErrorHandler = NULL; + endpoint->CloseIrp = NULL; + + // + // Save a pointer to the endpoint block in the file object + // so that we can find it when file-based requests are + // issued. + // + + irpSp->FileObject->FsContext = (PVOID)endpoint; + + // + // Reference the loopback device object. + // + + ObReferenceObject( LoopDeviceObject ); + + endpoint->DeviceObject = LoopDeviceObject; + + // + // The EA contains the address to be bound to the endpoint. + // Verify that the address is not already bound. + // + // !!! This should really be a share-mode/SECURITY_DESCRIPTOR + // check. + // + + LoopParseAddressFromEa( + (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer, + endpoint->NetbiosName + ); + endpoint->NetbiosName[NETBIOS_NAME_LENGTH] = 0; + + IF_DEBUG(LOOP2) { + DbgPrint( " Address to bind: \"%s\"\n", + endpoint->NetbiosName ); + } + + ACQUIRE_LOOP_LOCK( "DispatchCreate(endp) initial" ); + + existingEndpoint = LoopFindBoundAddress( endpoint->NetbiosName ); + + if ( existingEndpoint != NULL ) { + + IF_DEBUG(LOOP2) { + DbgPrint( " Duplicate address at endpoint %lx\n", + existingEndpoint ); + } + + RELEASE_LOOP_LOCK( "DispatchCreate duplicate address" ); + + ObDereferenceObject( LoopDeviceObject ); + + DEBUG SET_BLOCK_TYPE( endpoint, BlockTypeGarbage ); + DEBUG SET_BLOCK_STATE( endpoint, BlockStateDead ); + DEBUG SET_BLOCK_SIZE( endpoint, -1 ); + DEBUG endpoint->BlockHeader.ReferenceCount = -1; + DEBUG endpoint->DeviceObject = NULL; + ExFreePool( endpoint ); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCreate complete for IRP %lx\n", + Irp ); + } + return STATUS_INVALID_PARAMETER; + } + + // + // Link the new endpoint into the loopback device's endpoint + // list. + // + + InsertTailList( + &LoopDeviceObject->EndpointList, + &endpoint->DeviceListEntry + ); + + RELEASE_LOOP_LOCK( "DispatchCreate(endp) final" ); + + } else { + + // + // A connection endpoint is being created. + // + // Allocate a LOOP_CONNECTION block to describe the connection. + // Initialize it. + // + + connection = ExAllocatePool( + NonPagedPool, + sizeof(LOOP_CONNECTION) + ); + if ( connection == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Unable to allocate pool\n" ); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCreate complete for IRP %lx\n", + Irp ); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + IF_DEBUG(LOOP2) { + DbgPrint( " Connection allocated: %lx\n", connection ); + } + SET_BLOCK_TYPE( connection, BlockTypeLoopConnection ); + SET_BLOCK_STATE( connection, BlockStateUnbound ); + SET_BLOCK_SIZE( connection, sizeof(LOOP_CONNECTION) ); + connection->BlockHeader.ReferenceCount = 0; // not connected + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + connection, + connection->BlockHeader.ReferenceCount ); + } + + connection->Endpoint = NULL; + connection->RemoteConnection = NULL; + connection->ConnectionContext = LoopGetConnectionContextFromEa( + Irp->AssociatedIrp.SystemBuffer + ); + + InitializeListHead( &connection->PendingReceiveList ); + InitializeListHead( &connection->IncomingSendList ); + + connection->FileObject = irpSp->FileObject; + + connection->IndicatingSendIrp = NULL; + connection->ConnectOrListenIrp = NULL; + connection->CloseIrp = NULL; + connection->DisconnectIrp = NULL; + + // + // Save a pointer to the connection block in the file object + // so that we can find it when file-based requests are + // issued. + // + + irpSp->FileObject->FsContext = (PVOID)connection; + + // + // Reference the loopback device object. + // + + ObReferenceObject( LoopDeviceObject ); + + connection->DeviceObject = LoopDeviceObject; + + // + // Link the new connection into the loopback device's connection + // list. + // + + ExInterlockedInsertTailList( + &LoopDeviceObject->ConnectionList, + &connection->DeviceListEntry, + &LoopDeviceObject->SpinLock + ); + + } + + } + + // + // Successful completion. Complete the I/O request. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( Irp, 2 ); + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchCreate complete for IRP %lx\n", Irp ); + } + + return STATUS_SUCCESS; + +} // LoopDispatchCreate + + +NTSTATUS +LoopDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Device Control functions for the + LAN Manager loopback driver. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + + ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject ); + // only one loopback device + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchDeviceControl entered for IRP %lx\n", Irp ); + } + + // + // Initialize the I/O status block. + // + + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ); + + // + // Convert the (external) device control into internal format, then + // treat it as if it had arrived that way. + // + + status = TdiMapUserRequest( DeviceObject, Irp, irpSp ); + + if ( !NT_SUCCESS(status) ) { + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + return status; + + } + + return LoopDispatchInternalDeviceControl( DeviceObject, Irp ); + +} // LoopDispatchDeviceControl + + +NTSTATUS +LoopDispatchInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Internal Device Control functions + for the LAN Manager loopback driver. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + + DeviceObject; // not otherwise referenced if !DBG + + ASSERT( DeviceObject == (PDEVICE_OBJECT)LoopDeviceObject ); + // only one loopback device + + IF_DEBUG(LOOP1) { + DbgPrint( "LoopDispatchInternalDeviceControl entered for IRP %lx\n", + Irp ); + } + + // + // Initialize the I/O status block. + // + + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( irpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ); + + // + // Case on the control code. + // + + switch ( irpSp->MinorFunction ) { + + case TDI_ACCEPT: + + return LoopAccept( Irp, irpSp ); + + case TDI_ASSOCIATE_ADDRESS: + + return LoopAssociateAddress( Irp, irpSp ); + + case TDI_CONNECT: + + return LoopConnect( Irp, irpSp ); + + case TDI_DISASSOCIATE_ADDRESS: + + return LoopDisassociateAddress( Irp, irpSp ); + + case TDI_DISCONNECT: + + return LoopDisconnect( Irp, irpSp ); + + case TDI_LISTEN: + + return LoopListen( Irp, irpSp ); + + case TDI_QUERY_INFORMATION: + + return LoopQueryInformation( Irp, irpSp ); + + case TDI_RECEIVE: + + return LoopReceive( Irp, irpSp ); + + case TDI_SEND: + + return LoopSend( Irp, irpSp ); + + case TDI_SET_EVENT_HANDLER: + + return LoopSetEventHandler( Irp, irpSp ); + + case TDI_SEND_DATAGRAM: + + // + // !!! Need to implement this request. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( Irp, 0 ); + + return STATUS_SUCCESS; + + case TDI_RECEIVE_DATAGRAM: + case TDI_SET_INFORMATION: + + // + // !!! Need to implement these requests. + // + + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest( Irp, 0 ); + + return STATUS_NOT_IMPLEMENTED; + + default: + + IF_DEBUG(LOOP2) { + DbgPrint( " Invalid device control function: %lx\n", + irpSp->Parameters.DeviceIoControl.IoControlCode ); + } + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + + return STATUS_INVALID_PARAMETER; + + } + + return STATUS_INVALID_PARAMETER; // can't get here + +} // LoopDispatchInternalDeviceControl + + +VOID +LoopUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This is the unload routine for the LAN Manager loopback driver. + +Arguments: + + DriverObject - Pointer to driver object for this driver. + +Return Value: + + None. + +--*/ + +{ + DriverObject; // prevent compiler warnings + + return; + +} // LoopUnload diff --git a/private/ntos/tdi/loopback/loopback.h b/private/ntos/tdi/loopback/loopback.h new file mode 100644 index 000000000..150a26ffb --- /dev/null +++ b/private/ntos/tdi/loopback/loopback.h @@ -0,0 +1,433 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + loopback.h + +Abstract: + + This module is the main include file for the LAN Manager loopback + driver. + +Author: + + Chuck Lenzmeier (chuckl) 27-Jun-1991 + +Revision History: + +--*/ + +#ifndef _LOOP_ +#define _LOOP_ + +// +// "System" include files +// + +#include + +#include + +// +// Network include files. +// + +#include "status.h" + +// +// Local, independent include files +// + +#include "loopdbg.h" + +#define LOOPBACK_DEVICE_NAME "\\Device\\Loop" + +// +// The length of a NetBIOS name. Fixed by the protocol. +// + +#define NETBIOS_NAME_LENGTH 16 + +// +// Simple MIN and MAX macros. Watch out for side effects! +// + +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) +#define MAX(a,b) ( ((a) < (b)) ? (b) : (a) ) + +// +// Macros for accessing the block header structure. +// +// *** Note that the existing usage of these macros assumes that the block +// header is the first element in the block! +// + +#define GET_BLOCK_STATE(block) ( ((PBLOCK_HEADER)(block))->State ) +#define SET_BLOCK_STATE(block,state) ( ((PBLOCK_HEADER)(block))->State = state ) + +#define GET_BLOCK_TYPE(block) ( ((PBLOCK_HEADER)(block))->Type ) +#define SET_BLOCK_TYPE(block,type) ( ((PBLOCK_HEADER)(block))->Type = type ) + +#define GET_BLOCK_SIZE(block) ( ((PBLOCK_HEADER)(block))->Size ) +#define SET_BLOCK_SIZE(block,size) ( ((PBLOCK_HEADER)(block))->Size = size ) + +// +// Local macros +// + +// +// Macros for lock debugging. +// +// *** Note that the test for recursion only works on uniprocessors. +// + +#if LOOPDBG && defined(LOOPLOCK) + +#define ACQUIRE_LOOP_LOCK(instance) { \ + IF_DEBUG(LOOP5) \ + DbgPrint( "Acquire loop lock, %s\n", \ + (instance) ); \ + if ( LoopDeviceObject->SavedIrql != (KIRQL)-1 ) { \ + DbgPrint( "Recursive lock acquisition attempt\n" ); \ + DbgBreakPoint( ); \ + } \ + KeAcquireSpinLock( \ + &LoopDeviceObject->SpinLock, \ + &LoopDeviceObject->SavedIrql \ + ); \ + } + +#define RELEASE_LOOP_LOCK(instance) { \ + KIRQL oldIrql; \ + IF_DEBUG(LOOP5) \ + DbgPrint( "Release loop lock, %s\n", \ + (instance) ); \ + ASSERT( LoopDeviceObject->SavedIrql != (KIRQL)-1 ); \ + oldIrql = LoopDeviceObject->SavedIrql; \ + LoopDeviceObject->SavedIrql = (KIRQL)-1; \ + KeReleaseSpinLock( \ + &LoopDeviceObject->SpinLock, \ + oldIrql \ + ); \ + } + +#else // LOOPDBG && defined(LOOPLOCK) + +#define ACQUIRE_LOOP_LOCK(instance) \ + KeAcquireSpinLock( \ + &LoopDeviceObject->SpinLock, \ + &LoopDeviceObject->SavedIrql \ + ) \ + +#define RELEASE_LOOP_LOCK(instance) \ + KeReleaseSpinLock( \ + &LoopDeviceObject->SpinLock, \ + LoopDeviceObject->SavedIrql \ + ) \ + +#endif // else LOOPDBG && defined(LOOPLOCK) + +// +// Local types +// + +// +// The loopback driver's device object is a standard I/O system device +// object followed by fields specific to the device. +// + +typedef struct _LOOP_DEVICE_OBJECT { + + DEVICE_OBJECT DeviceObject; + + // + // List of active address endpoints. + // + + LIST_ENTRY EndpointList; + + // + // List of active connection endpoints. + // + + LIST_ENTRY ConnectionList; + + // + // Spin lock synchronizing access to fields in the device object + // and to structures maintained by the device driver. + // + + KSPIN_LOCK SpinLock; + + // + // SavedIrql is used so that one routine can call another with the + // lock held and the called routine can release (and possibly + // reacquire it). SavedIrql is set *after* the lock is acquired. + // + + KIRQL SavedIrql; + +} LOOP_DEVICE_OBJECT, *PLOOP_DEVICE_OBJECT; + +#define LOOP_DEVICE_EXTENSION_LENGTH (sizeof(LOOP_DEVICE_OBJECT) - \ + sizeof(DEVICE_OBJECT)) + +// +// BLOCK_TYPE is an enumerated type defining the various types of +// data blocks used by the driver. +// + +typedef enum _BLOCK_TYPE { + BlockTypeGarbage = 0, + BlockTypeLoopConnection = 0x29290001, + BlockTypeLoopEndpoint = 0x29290002 +} BLOCK_TYPE, *PBLOCK_TYPE; + +// +// BLOCK_STATE is an enumerated type defining the various states that +// blocks can be in. Initializing is used (relatively rarely) to +// indicate that creation/initialization of a block is in progress. +// Active is the state blocks are usually in. Closing is used to +// indicate that a block is being prepared for deletion; when the +// reference count on the block reaches 0, the block will be deleted. +// Dead is used when debugging code is enabled to indicate that the +// block has been deleted. +// + +typedef enum _BLOCK_STATE { + BlockStateDead, + BlockStateUnbound, + BlockStateBound, + BlockStateConnecting, + BlockStateActive, + BlockStateDisconnecting, + BlockStateClosing, + BlockStateClosed, + // The following is defined just to know how many states there are + BlockStateMax +} BLOCK_STATE, *PBLOCK_STATE; + + +// +// BLOCK_HEADER is the standard block header that appears at the +// beginning of most driver-private data structures. This header is +// used primarily for debugging and tracing. The Type and State fields +// are described above. The Size field indicates how much space was +// allocated for the block. ReferenceCount indicates the number of +// reasons why the block should not be deallocated. The count is set to +// 2 by the allocation routine, to account for 1) the fact that the +// block is "open" and 2) the pointer returned to the caller. When the +// block is closed, State is set to Closing, and the ReferenceCount is +// decremented. When all references (pointers) to the block are +// deleted, and the reference count reaches 0, the block is deleted. +// + +typedef struct _BLOCK_HEADER { + BLOCK_TYPE Type; + BLOCK_STATE State; + ULONG ReferenceCount; + CLONG Size; +} BLOCK_HEADER, *PBLOCK_HEADER; + +// +// The file object obtained when the loopback Transport Provider is +// opened points to a LOOP_ENDPOINT record, which has context consisting +// of a pointer to the file object and a list of connections created +// over the endpoint. +// + +typedef struct _LOOP_ENDPOINT { + BLOCK_HEADER BlockHeader; + LIST_ENTRY DeviceListEntry; + LIST_ENTRY ConnectionList; + LIST_ENTRY PendingListenList; + LIST_ENTRY IncomingConnectList; + PIRP IndicatingConnectIrp; + PLOOP_DEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + PTDI_IND_CONNECT ConnectHandler; + PVOID ConnectContext; + PTDI_IND_RECEIVE ReceiveHandler; + PVOID ReceiveContext; + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramContext; + PTDI_IND_RECEIVE_EXPEDITED ReceiveExpeditedHandler; + PVOID ReceiveExpeditedContext; + PTDI_IND_DISCONNECT DisconnectHandler; + PVOID DisconnectContext; + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorContext; + PIRP CloseIrp; + CHAR NetbiosName[NETBIOS_NAME_LENGTH+1]; +} LOOP_ENDPOINT, *PLOOP_ENDPOINT; + +// +// Each connection is represented by two LOOP_CONNECTION structures, one +// for each end of the connection. +// + +typedef struct _LOOP_CONNECTION { + BLOCK_HEADER BlockHeader; + LIST_ENTRY DeviceListEntry; + LIST_ENTRY EndpointListEntry; + PLOOP_ENDPOINT Endpoint; + PLOOP_DEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + struct _LOOP_CONNECTION *RemoteConnection; + PVOID ConnectionContext; + LIST_ENTRY PendingReceiveList; + LIST_ENTRY IncomingSendList; + PIRP IndicatingSendIrp; + PIRP ConnectOrListenIrp; + PIRP DisconnectIrp; + PIRP CloseIrp; +} LOOP_CONNECTION, *PLOOP_CONNECTION; + +// +// Global variables +// + +// +// The address of the loopback device object (there's only one) is kept +// in global storage to avoid having to pass it from routine to routine. +// + +extern PLOOP_DEVICE_OBJECT LoopDeviceObject; + +// +// LoopProviderInfo is a structure containing information that may be +// obtained using TdiQueryInformation. +// + +extern TDI_PROVIDER_INFO LoopProviderInfo; + + +// +// Global declarations +// + +NTSTATUS +LoopAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopAssociateAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopConnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +LoopCopyData ( + IN PMDL Destination, + IN PMDL Source, + IN ULONG Length + ); + +NTSTATUS +LoopCreate ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +LoopDereferenceConnection ( + IN PLOOP_CONNECTION Connection + ); + +VOID +LoopDereferenceEndpoint ( + IN PLOOP_ENDPOINT Endpoint + ); + +NTSTATUS +LoopDisassociateAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopDisconnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +LoopDoDisconnect ( + IN PLOOP_CONNECTION Conection, + IN BOOLEAN ClientInitiated + ); + +PLOOP_ENDPOINT +LoopFindBoundAddress ( + IN PCHAR NetbiosName + ); + +PVOID +LoopGetConnectionContextFromEa ( + PFILE_FULL_EA_INFORMATION Ea + ); + +NTSTATUS +LoopGetEndpointTypeFromEa ( + PFILE_FULL_EA_INFORMATION Ea, + PBLOCK_TYPE Type + ); + +NTSTATUS +LoopListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopParseAddress ( + IN PTA_NETBIOS_ADDRESS Address, + OUT PCHAR NetbiosName + ); + +NTSTATUS +LoopParseAddressFromEa ( + IN PFILE_FULL_EA_INFORMATION Ea, + OUT PCHAR NetbiosName + ); + +NTSTATUS +LoopQueryInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopSend ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopSetEventHandler ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +LoopVerifyEndpoint ( + IN PLOOP_ENDPOINT Endpoint + ); + +#endif // def _LOOP_ diff --git a/private/ntos/tdi/loopback/loopdbg.h b/private/ntos/tdi/loopback/loopdbg.h new file mode 100644 index 000000000..4024f5e4e --- /dev/null +++ b/private/ntos/tdi/loopback/loopdbg.h @@ -0,0 +1,58 @@ +#ifndef _LOOPDBG_ +#define _LOOPDBG_ + +#ifdef MEMPRINT +#include +#endif + +// +// Debugging macros +// + +#ifndef DBG +#define DBG 0 +#endif + +#if !DBG + +#undef LOOPDBG +#define LOOPDBG 0 + +#else + +#ifndef LOOPDBG +#define LOOPDBG 0 +#endif + +#endif + +#undef IF_DEBUG + +#if !DEVL +#define STATIC static +#else +#define STATIC +#endif + +#if !LOOPDBG + +#define DEBUG if (FALSE) +#define IF_DEBUG(flag) if (FALSE) + +#else + +#define DEBUG if (TRUE) +#define IF_DEBUG(flag) if (LoopDebug & (DEBUG_ ## flag)) +extern ULONG LoopDebug; + +#define PRINT_LITERAL(literal) DbgPrint( #literal" = %lx\n", (literal) ) + +#define DEBUG_LOOP1 0x00000001 +#define DEBUG_LOOP2 0x00000002 +#define DEBUG_LOOP3 0x00000004 +#define DEBUG_LOOP4 0x00000008 +#define DEBUG_LOOP5 0x00000010 + +#endif // else !LOOPDBG + +#endif // ndef _LOOPDBG_ diff --git a/private/ntos/tdi/loopback/loopsub.c b/private/ntos/tdi/loopback/loopsub.c new file mode 100644 index 000000000..003161e5f --- /dev/null +++ b/private/ntos/tdi/loopback/loopsub.c @@ -0,0 +1,379 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + loopsub.c + +Abstract: + + This module implements common functions for the loopback Transport + Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 15-Aug-1991 + +Revision History: + +--*/ + +#include "loopback.h" + + +VOID +LoopCopyData ( + IN PMDL Destination, + IN PMDL Source, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine copies data from the storage described by one MDL chain + into the storage described by another MDL chain. + +Arguments: + + Destination - Pointer to first MDL in Destination chain + + Source - Pointer to first MDL in Source chain + + Length - Amount of data to copy. Caller must ensure that the Source + and Destination chains are at least this long. + +Return Value: + + None. + +--*/ + +{ + PCHAR sourceAddress; + ULONG sourceLength; + + PCHAR destinationAddress; + ULONG destinationLength; + + ULONG copyLength; + + // + // Get the virtual address of the first source buffer, mapping it + // if necessary. Also get the length of the buffer. + // + + sourceAddress = MmGetSystemAddressForMdl( Source ); + sourceLength = MmGetMdlByteCount( Source ); + + // + // Get the virtual address of the first destination buffer, mapping + // it if necessary. Also get the length of the buffer. + // + + destinationAddress = MmGetSystemAddressForMdl( Destination ); + destinationLength = MmGetMdlByteCount( Destination ); + + // + // Loop copying data. + // + + do { + + // + // The amount to copy in this pass is the minimum of 1) the + // amount remaining in the current source buffer, 2) the amount + // remaining in the current destination buffer, and 3) the + // amount remaining in the overall copy operation. + // + + copyLength = sourceLength; + if ( copyLength > destinationLength ) copyLength = destinationLength; + if ( copyLength > Length ) copyLength = Length; + + // + // Copy from the source buffer into the destination buffer. + // + +#ifndef TIMING + IF_DEBUG(LOOP4) { + DbgPrint( " copying %lx bytes from %lx to %lx\n", + copyLength, sourceAddress, destinationAddress ); + DbgPrint( " source data: %lx, %lx\n", + *(PULONG)sourceAddress, *((PULONG)sourceAddress + 1) ); + } + RtlMoveMemory( destinationAddress, sourceAddress, copyLength ); +#else + if ( (NtGlobalFlag & 0x20000000) == 0 ) { + RtlMoveMemory( destinationAddress, sourceAddress, copyLength ); + } else { + RtlMoveMemory( + destinationAddress, + sourceAddress, + (copyLength > 200) ? 200 : copyLength + ); + } +#endif + + // + // If all of the requested data has been copied, leave. + // + + Length -= copyLength; + + if ( Length == 0 ) { + + return; + + } + + // + // If we have used up all of the current source buffer, move to + // the next buffer. Get the virtual address of the next buffer, + // mapping it if necessary. Also get the length of the buffer. + // If we haven't used up the current source buffer, simply + // update the source pointer and the remaining length. + // + + if ( copyLength == sourceLength ) { + + Source = Source->Next; + + sourceAddress = MmGetSystemAddressForMdl( Source ); + sourceLength = MmGetMdlByteCount( Source ); + + } else { + + sourceAddress += copyLength; + sourceLength -= copyLength; + + } + + // + // If we have used up all of the current destination buffer, + // move to the next buffer. Get the virtual address of the next + // buffer, mapping it if necessary. Also get the length of the + // buffer. If we haven't used up the current destination + // buffer, simply update the destination pointer and the + // remaining length. + // + + if ( copyLength == destinationLength ) { + + Destination = Destination->Next; + + destinationAddress = MmGetSystemAddressForMdl( Destination ); + destinationLength = MmGetMdlByteCount( Destination ); + + } else { + + destinationAddress += copyLength; + destinationLength -= copyLength; + + } + + } while ( TRUE ); + + // + // Can't get here. + // + + ASSERTMSG( FALSE, "Can't get here!" ); + +} // LoopCopyData + + +PVOID +LoopGetConnectionContextFromEa ( + PFILE_FULL_EA_INFORMATION Ea + ) + +/*++ + +Routine Description: + + This routine returns the connection context specified in an EA. + +Arguments: + + Ea - Pointer to EA buffer + +Return Value: + + PVOID - Connection context + +--*/ + +{ + PVOID ctx; + + RtlMoveMemory( &ctx, &Ea->EaName[Ea->EaNameLength + 1], sizeof(PVOID) ); + + return ctx; + +} // LoopGetConnectionContextFromEa + + +NTSTATUS +LoopGetEndpointTypeFromEa ( + PFILE_FULL_EA_INFORMATION Ea, + PBLOCK_TYPE Type + ) + +/*++ + +Routine Description: + + This routine determines whether an EA describes an address or a + connection. + +Arguments: + + Ea - Pointer to EA buffer + + Type - Returns block type + +Return Value: + + NTSTATUS - STATUS_INVALID_PARAMETER if EA is not valid + +--*/ + +{ + // + // First check for address type. + // + + if ( (Ea->EaNameLength == TDI_TRANSPORT_ADDRESS_LENGTH) && + (strcmp( Ea->EaName, TdiTransportAddress ) == 0) ) { + *Type = BlockTypeLoopEndpoint; + return STATUS_SUCCESS; + } + + // + // Next check for connection type. + // + + if ( (Ea->EaNameLength == TDI_CONNECTION_CONTEXT_LENGTH) && + (strcmp( Ea->EaName, TdiConnectionContext ) == 0) ) { + *Type = BlockTypeLoopConnection; + return STATUS_SUCCESS; + } + + // + // Invalid type. + // + + return STATUS_INVALID_PARAMETER; + +} // LoopGetEndpointTypeFromEa + + +NTSTATUS +LoopParseAddress ( + IN PTA_NETBIOS_ADDRESS Address, + OUT PCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine parses the input AddressString according to conventions + defined for transport address strings as defined in the TDI + specification. It converts the name from that form into a "standard" + NetBIOS name. + +Arguments: + + Address - Pointer to a transport address in the TDI format. + + NetbiosName - A 16-character space into which the NULL-terminated + NetBIOS name is written. + +Return Value: + + NTSTATUS - Indicates whether the address string was valid. + +--*/ + +{ + // + // If the input address is not a single unique address in NetBIOS + // format, reject it. + // + + if ( (Address->TAAddressCount != 1) || + (Address->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) || + (Address->Address[0].AddressLength != sizeof(TDI_ADDRESS_NETBIOS)) || + (Address->Address[0].Address[0].NetbiosNameType != + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Copy the name into the output buffer. + // + + RtlMoveMemory( + NetbiosName, + Address->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_LENGTH + ); + + return STATUS_SUCCESS; + +} // LoopParseAddress + + +NTSTATUS +LoopParseAddressFromEa ( + IN PFILE_FULL_EA_INFORMATION Ea, + OUT PCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine parses the input EA according to conventions defined + for transport address strings as defined in the TDI specification. + It converts the name from that form into a "standard" NetBIOS name. + +Arguments: + + Ea - Pointer to an EA in the TDI format. + + NetbiosName - A 16-character space into which the NULL-terminated + NetBIOS name is written. + +Return Value: + + NTSTATUS - Indicates whether the address string was valid. + +--*/ + +{ + TA_NETBIOS_ADDRESS nbAddress; + + if ( Ea->EaValueLength != sizeof(TA_NETBIOS_ADDRESS) ) { + return STATUS_INVALID_PARAMETER; + } + + RtlMoveMemory( + &nbAddress, + &Ea->EaName[Ea->EaNameLength + 1], + sizeof(TA_NETBIOS_ADDRESS) + ); + + // + // Pass the value portion of the EA, which is a TRANSPORT_ADDRESS, + // to LoopParseAddress. + // + + return LoopParseAddress( &nbAddress, NetbiosName ); + +} // LoopParseAddressFromEa + diff --git a/private/ntos/tdi/loopback/makefile b/private/ntos/tdi/loopback/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/loopback/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/loopback/sources b/private/ntos/tdi/loopback/sources new file mode 100644 index 000000000..dcbcb8f31 --- /dev/null +++ b/private/ntos/tdi/loopback/sources @@ -0,0 +1,46 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=tdi +MINORCOMP=loopback + +TARGETNAME=loopback +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib + +INCLUDES=..\inc;..\..\inc;..\..\..\inc + +SOURCES= \ + connect.c \ + endpoint.c \ + info.c \ + loopback.c \ + loopsub.c \ + transfer.c + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF diff --git a/private/ntos/tdi/loopback/transfer.c b/private/ntos/tdi/loopback/transfer.c new file mode 100644 index 000000000..d80fb44fb --- /dev/null +++ b/private/ntos/tdi/loopback/transfer.c @@ -0,0 +1,859 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + transfer.c + +Abstract: + + This module implements data transfer logic for the loopback + Transport Provider driver for NT LAN Manager. + +Author: + + Chuck Lenzmeier (chuckl) 15-Aug-1991 + +Revision History: + +--*/ + +#include "loopback.h" + +// +// Local declarations +// + +STATIC +VOID +CompleteReceive ( + IN PIRP ReceiveIrp, + IN PIRP SendIrp + ); + +STATIC +VOID +IndicateReceive ( + IN PLOOP_CONNECTION ReceivingConnection, + IN PIRP InitialSendIrp + ); + + +NTSTATUS +LoopReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Receive request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + PLOOP_CONNECTION receivingConnection; + PLOOP_CONNECTION sendingConnection; + + IF_DEBUG(LOOP1) DbgPrint( " Receive request\n" ); + + // + // Verify that the receiving connection is connected. + // + + receivingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( receivingConnection == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Can't Receive on control channel\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" ); + return STATUS_INVALID_PARAMETER; + } + + sendingConnection = receivingConnection->RemoteConnection; + IF_DEBUG(LOOP2) { + DbgPrint( " Receiving connection: %lx\n", receivingConnection ); + DbgPrint( " Sending connection: %lx\n", sendingConnection ); + } + + ACQUIRE_LOOP_LOCK( "Receive initial" ); + + if ( (sendingConnection == NULL) || + (GET_BLOCK_STATE(receivingConnection) != BlockStateActive) ) { + RELEASE_LOOP_LOCK( "Receive closing" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Receive request complete\n" ); + return STATUS_DISCONNECTED; + } + + // + // Queue the Receive to the connection's Pending Receive list. + // + + InsertTailList( + &receivingConnection->PendingReceiveList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + // + // Check for pending data. + // + + if ( (receivingConnection->IndicatingSendIrp != NULL) || + (receivingConnection->IncomingSendList.Flink == + &receivingConnection->IncomingSendList) ) { + + // + // There is no pending Send, or an indication is already in + // progress. Reference the connection to account for the + // Receive IRP. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " No pending Send; leaving IRP %lx queued \n", Irp ); + } + receivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + receivingConnection, + receivingConnection->BlockHeader.ReferenceCount ); + } + + RELEASE_LOOP_LOCK( "Receive no Send" ); + + } else { + + // + // There is pending data. Call IndicateReceive to satisfy + // the Receive. + // + // *** Note that IndicateReceive returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopReceive indicating receive\n" ); + IndicateReceive( receivingConnection, NULL ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Receive request %lx complete\n", Irp ); + return STATUS_PENDING; + +} // LoopReceive + + +NTSTATUS +LoopSend ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine processes a Send request. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - Pointer to current stack location in IRP + +Return Value: + + NTSTATUS - Status of request + +--*/ + +{ + PLOOP_CONNECTION sendingConnection; + PLOOP_CONNECTION receivingConnection; + BOOLEAN firstSend; + + IF_DEBUG(LOOP1) DbgPrint( " Send request\n" ); + + // + // Verify that the sending connection is connected. + // + + sendingConnection = (PLOOP_CONNECTION)IrpSp->FileObject->FsContext; + + if ( sendingConnection == NULL ) { + IF_DEBUG(LOOP2) DbgPrint( " Can't Send on control channel\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" ); + return STATUS_INVALID_PARAMETER; + } + + receivingConnection = sendingConnection->RemoteConnection; + IF_DEBUG(LOOP2) { + DbgPrint( " Sending connection: %lx\n", sendingConnection ); + DbgPrint( " Receiving connection: %lx\n", receivingConnection ); + } + + ACQUIRE_LOOP_LOCK( "Send initial" ); + + if ( (receivingConnection == NULL) || + (GET_BLOCK_STATE(sendingConnection) != BlockStateActive) ) { + RELEASE_LOOP_LOCK( "Send closing" ); + IF_DEBUG(LOOP2) DbgPrint( " Connection not connected\n" ); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest( Irp, 0 ); + IF_DEBUG(LOOP1) DbgPrint( " Send request complete\n" ); + return STATUS_DISCONNECTED; + } + + // + // Reference both ends. + // + + sendingConnection->BlockHeader.ReferenceCount++; + receivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + sendingConnection, + sendingConnection->BlockHeader.ReferenceCount ); + DbgPrint( " New refcnt on connection %lx is %lx\n", + receivingConnection, + receivingConnection->BlockHeader.ReferenceCount ); + } + + // + // Determine whether this is the first active incoming Send for the + // receiving connection. + // + + firstSend = (BOOLEAN)( receivingConnection->IncomingSendList.Flink == + &receivingConnection->IncomingSendList ); + + // + // Queue the Send to the receiving connection's Incoming Send list, + // in order to prevent another Send from getting ahead of this one. + // + + InsertTailList( + &receivingConnection->IncomingSendList, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + + if ( !firstSend || (receivingConnection->IndicatingSendIrp != NULL) ) { + + // + // Pending data already exists, or an indication is already in + // progress. This Send remains behind the already pending data. + // + + IF_DEBUG(LOOP2) DbgPrint( " Data already pending\n" ); + + RELEASE_LOOP_LOCK( "Send sends pending" ); + + } else { + + // + // Indicate the incoming data. + // + // *** Note that IndicateReceive returns with the loopback + // driver spin lock released. + // + + IF_DEBUG(LOOP2) DbgPrint( " LoopSend indicating receive\n" ); + IndicateReceive( receivingConnection, Irp ); + + } + + IF_DEBUG(LOOP1) DbgPrint( " Send request %lx complete\n", Irp ); + return STATUS_PENDING; + +} // LoopSend + + +VOID +CompleteReceive ( + IN PIRP ReceiveIrp, + IN PIRP SendIrp + ) + +/*++ + +Routine Description: + + This routine completes the process of sending a message. It copies + the message from the source buffer into the destination buffer. + + The reference counts on the owning connections must have been + incremented to account for the requesting IRPs in order to prevent + their deletion. + +Arguments: + + ReceiveIrp - Pointer to IRP used for Receive request + + SendIrp - Pointer to IRP used for Send request + +Return Value: + + NTSTATUS - Indicates whether the connection was successfully created + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION sendIrpSp; + PIO_STACK_LOCATION receiveIrpSp; + ULONG copyLength; + PTDI_REQUEST_KERNEL_SEND sendRequest; + PTDI_REQUEST_KERNEL_RECEIVE receiveRequest; + PLOOP_CONNECTION sendingConnection; + PLOOP_CONNECTION receivingConnection; + + IF_DEBUG(LOOP2) { + DbgPrint( " Send IRP %lx completes receive IRP %lx\n", + SendIrp, ReceiveIrp ); + } + + // + // Copy the data from the source buffer into the destination buffer. + // + // *** Note that we special-case zero-length copies. We don't + // bother to call LoopCopyData. Part of the reason for this + // is that a zero-length send or receive issued from user mode + // yields an IRP that has a NULL MDL address. + // + + sendIrpSp = IoGetCurrentIrpStackLocation( SendIrp ); + sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters; + copyLength = sendRequest->SendLength; + IF_DEBUG(LOOP4) { + DbgPrint( " Send request at %lx, send length %lx\n", + sendRequest, copyLength ); + } + receiveIrpSp = IoGetCurrentIrpStackLocation( ReceiveIrp ); + receiveRequest = (PTDI_REQUEST_KERNEL_RECEIVE)&receiveIrpSp->Parameters; + IF_DEBUG(LOOP4) { + DbgPrint( " Receive request at %lx, receive length %lx\n", + receiveRequest, receiveRequest->ReceiveLength ); + } + status = STATUS_SUCCESS; + if ( copyLength > receiveRequest->ReceiveLength ) { + status = STATUS_BUFFER_OVERFLOW; + copyLength = receiveRequest->ReceiveLength; + } + + if ( copyLength != 0 ) { + + ASSERT( ReceiveIrp->MdlAddress != NULL ); + ASSERT( SendIrp->MdlAddress != NULL ); + + LoopCopyData( + ReceiveIrp->MdlAddress, + SendIrp->MdlAddress, + copyLength + ); + + } + + // + // Complete the Receive and Send requests. + // + + receivingConnection = + (PLOOP_CONNECTION)receiveIrpSp->FileObject->FsContext; + sendingConnection = + (PLOOP_CONNECTION)sendIrpSp->FileObject->FsContext; + + ReceiveIrp->IoStatus.Status = status; + ReceiveIrp->IoStatus.Information = copyLength; + + SendIrp->IoStatus.Status = STATUS_SUCCESS; + SendIrp->IoStatus.Information = copyLength; + + IoCompleteRequest( ReceiveIrp, 2 ); + IoCompleteRequest( SendIrp, 2 ); + + // + // Dereference the connections. + // + + ACQUIRE_LOOP_LOCK( "CompleteReceive dereference" ); + LoopDereferenceConnection( receivingConnection ); + LoopDereferenceConnection( sendingConnection ); + RELEASE_LOOP_LOCK( "CompleteReceive dereference" ); + + return; + +} // CompleteReceive + + +VOID +IndicateReceive ( + IN PLOOP_CONNECTION ReceivingConnection, + IN PIRP InitialSendIrp + ) +{ + NTSTATUS status; + PLOOP_ENDPOINT receivingEndpoint; + PLOOP_CONNECTION sendingConnection; + PLIST_ENTRY listEntry; + PIRP receiveIrp; + PIRP sendIrp; + PTDI_IND_RECEIVE receiveHandler; + PVOID receiveContext; + PIO_STACK_LOCATION sendIrpSp; + PTDI_REQUEST_KERNEL_SEND sendRequest; + ULONG length; + PMDL mdl; + PVOID address; + ULONG bytesTaken; + + receivingEndpoint = ReceivingConnection->Endpoint; + IF_DEBUG(LOOP2) { + DbgPrint( " Receiving endpoint: %lx\n", receivingEndpoint ); + } + + // + // Reference the receiving connection to prevent it from going away + // while this routine is running. + // + + ReceivingConnection->BlockHeader.ReferenceCount++; + IF_DEBUG(LOOP3) { + DbgPrint( " New refcnt on connection %lx is %lx\n", + ReceivingConnection, + ReceivingConnection->BlockHeader.ReferenceCount ); + } + + // + // Capture the address of the sending connection, as the receiving + // connection's pointer can be zeroed if a Disconnect occurs. + // + + sendingConnection = ReceivingConnection->RemoteConnection; + ASSERT( sendingConnection != NULL ); + + // + // If the receiving connection has a pending Receive, satisfy it + // with this Send. If there is no pending Receive, and a Receive + // handler has been enabled on the receiving connection, call it. + // If the Receive handler returns with a Receive IRP, use it to + // satisfy this Send. If the Receive handler doesn't return an IRP, + // leave this Send pending. + // + // !!! Note that the current implementation only works because + // partial sends are not supported. + // + + while ( TRUE ) { + + // + // We have a Send pending. Is there a pending Receive? + // + + listEntry = RemoveHeadList( &ReceivingConnection->PendingReceiveList ); + + if ( listEntry != &ReceivingConnection->PendingReceiveList ) { + + // + // Found a pending Receive. Use it to satisfy the first + // incoming Send. + // + + receiveIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Receive IRP pending: %lx\n", receiveIrp ); + } + + listEntry = RemoveHeadList( + &ReceivingConnection->IncomingSendList + ); + ASSERT( listEntry != &ReceivingConnection->IncomingSendList ); + sendIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Send IRP pending: %lx\n", sendIrp ); + } + + // + // If this is the first time through the loop, and we were + // called to process a newly queued Send, dereference the + // receiving connection -- LoopSend referenced the + // connection an extra time in case the Send had to remain + // queued. + // + + if ( InitialSendIrp != NULL ) { + LoopDereferenceConnection( ReceivingConnection ); + } + + RELEASE_LOOP_LOCK( "IndicateReceive pending Receive" ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive pending Receive completed" ); + + // + // Fall to bottom of loop to handle more incoming Sends. + // + + } else { + + // + // No pending Receive. Is there a Receive handler? + // + + receiveHandler = receivingEndpoint->ReceiveHandler; + receiveContext = receivingEndpoint->ReceiveContext; + + if ( receiveHandler == NULL ) { + + // + // No Receive handler. The Send must remain queued. + // + + IF_DEBUG(LOOP2) DbgPrint( " No Receive handler\n" ); + + break; + + } + + // + // The receiving endpoint has a Receive handler. Call it. + // If it returns STATUS_SUCCESS, it completely handled the + // data. If it returns STATUS_MORE_PROCESSING_REQUIRED, it + // also returns a Receive IRP describing where to put the + // data. Any other return status means the receiver can't + // take the data just now, so we leave the Send queued and + // wait for the receiver to post a Receive IRP. + // + // !!! Note that we don't currently handle partial data + // acceptance. + // + // First, remove the first Send from the Incoming Send list, + // and make it the Indicating Send. It must be removed from + // the list to ensure that it isn't completed by + // LoopDoDisconnection while we're indicating it. + // + + listEntry = RemoveHeadList( + &ReceivingConnection->IncomingSendList + ); + ASSERT( listEntry != &ReceivingConnection->IncomingSendList ); + sendIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + ReceivingConnection->IndicatingSendIrp = sendIrp; + + RELEASE_LOOP_LOCK( "IndicateReceive calling Receive handler" ); + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler: %lx\n", receiveHandler ); + DbgPrint( " Send IRP: %lx\n", sendIrp ); + } + + sendIrpSp = IoGetCurrentIrpStackLocation( sendIrp ); + sendRequest = (PTDI_REQUEST_KERNEL_SEND)&sendIrpSp->Parameters; + + length = sendRequest->SendLength; + ASSERTMSG( + "Loopback driver doesn't handle partial or expedited sends", + sendRequest->SendFlags == 0 + ); + + // + // Map the send buffer, if necessary. + // + + mdl = sendIrp->MdlAddress; + if ( MmGetMdlByteCount(mdl) == 0 ) { + address = NULL; + } else { + address = MmGetSystemAddressForMdl( mdl ); + } + + // + // Call the Receive handler. + // + + status = receiveHandler( + receiveContext, + ReceivingConnection->ConnectionContext, + 0, + MmGetMdlByteCount( mdl ), + length, + &bytesTaken, + address, + &receiveIrp + ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive after calling handler" ); + IF_DEBUG(LOOP2) { + DbgPrint( " Indication for send IRP %lx done\n", sendIrp ); + } + + ReceivingConnection->IndicatingSendIrp = NULL; + + if ( status == STATUS_SUCCESS ) { + + // + // The Receive handler completely handled the data. + // Complete the Send. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler handled data\n" ); + } + + ASSERTMSG( + "Loopback driver doesn't handle partial acceptance " + "of indications", + bytesTaken == length + ); + + // + // Dereference the sending and receiving connections -- + // LoopSend referenced the connections when it queued + // the Send. + // + + LoopDereferenceConnection( ReceivingConnection ); + LoopDereferenceConnection( sendingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive completely handled" ); + + // + // Complete the Send IRP. + // + + sendIrp->IoStatus.Status = STATUS_SUCCESS; + sendIrp->IoStatus.Information = sendRequest->SendLength; + + IoCompleteRequest( sendIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive send completed" ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else if ( status == STATUS_MORE_PROCESSING_REQUIRED ) { + + // + // The Receive handler returned a Receive IRP to be used + // to satisfy the Send. + // + + IF_DEBUG(LOOP2) { + DbgPrint( " Receive handler returned IRP: %lx\n", + receiveIrp ); + } + + ASSERTMSG( + "Loopback driver doesn't handle partial acceptance " + "of indications", + bytesTaken == 0 + ); + + // + // Complete the Receive using the current Send. + // + // *** Note that the pending Send references both the + // sending and receiving connections. + + RELEASE_LOOP_LOCK( "IndicateReceive complete new Receive" ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive new receive completed" ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else { + + // + // The Receive handler couldn't take the data. This + // Send will have to wait until receiver can post a + // Receive IRP. + // + // *** Because we didn't hold the spin lock around the + // call to the Receive handler, it's possible that + // the receiver has already posted a Receive IRP. + // Because we were in the middle of an indication, + // that Receive would have been queued to the + // Pending Receive list, and we should go get it + // now. If the receiver hasn't posted a Receive + // yet, then this Send will be put back on the + // Incoming Send list before the Receive does come + // in (since we're now holding the lock). + // + + ASSERT( status == STATUS_DATA_NOT_ACCEPTED ); + + IF_DEBUG(LOOP2) DbgPrint( " Data not accepted\n" ); + + if ( GET_BLOCK_STATE(ReceivingConnection) != + BlockStateActive ) { + + // + // The connection is closing. Abort the current + // Send and leave. + // + + LoopDereferenceConnection( ReceivingConnection ); + LoopDereferenceConnection( sendingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive disconnecting" ); + + sendIrp->IoStatus.Status = STATUS_DISCONNECTED; + IoCompleteRequest( sendIrp, 2 ); + + ACQUIRE_LOOP_LOCK( "IndicateReceive send aborted" ); + + break; + + } + + listEntry = RemoveHeadList( + &ReceivingConnection->PendingReceiveList + ); + + if ( listEntry != + &ReceivingConnection->PendingReceiveList ) { + + // + // A Receive has been posted. Use it to satisfy + // this Send. + // + + receiveIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + IF_DEBUG(LOOP2) { + DbgPrint( " Receive IRP pending: %lx\n", + receiveIrp ); + } + + // + // Complete the Receive using the current Send. + // + // + // If this is the first time through the loop, and + // we were called to process a newly queued Send, + // dereference the receiving connection -- LoopSend + // referenced the connection an extra time in case + // the Send had to remain queued. + // + + if ( InitialSendIrp != NULL ) { + LoopDereferenceConnection( ReceivingConnection ); + } + + RELEASE_LOOP_LOCK( + "IndicateReceive complete posted Receive" + ); + + CompleteReceive( receiveIrp, sendIrp ); + + ACQUIRE_LOOP_LOCK( + "IndicateReceive posted receive completed" + ); + + // + // Fall to bottom of loop to handle more incoming + // Sends. + // + + } else { + + // + // The handler didn't take the data, and it didn't + // post a Receive IRP. Requeue the current send and + // get out. + // + + InsertHeadList( + &ReceivingConnection->IncomingSendList, + &sendIrp->Tail.Overlay.ListEntry + ); + + break; + + } + + } + + } // pending receive? + + // + // If we get here, we need to indicate the next incoming Send, + // if there is one. + // + + InitialSendIrp = NULL; + + if ( (GET_BLOCK_STATE(ReceivingConnection) != BlockStateActive) || + (ReceivingConnection->IncomingSendList.Flink == + &ReceivingConnection->IncomingSendList) ) { + + // + // No more Sends, or connection no longer active. Leave. + // + + break; + + } + + // + // Process the next Send. + // + + } // while ( TRUE ) + + // + // Remove the connection reference acquired at the start of this + // routine. + // + + LoopDereferenceConnection( ReceivingConnection ); + + RELEASE_LOOP_LOCK( "IndicateReceive done" ); + + return; + +} // IndicateReceive + diff --git a/private/ntos/tdi/nbf/action.c b/private/ntos/tdi/nbf/action.c new file mode 100644 index 000000000..84a51695a --- /dev/null +++ b/private/ntos/tdi/nbf/action.c @@ -0,0 +1,642 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains support for the TdiAction handler. + +Author: + + David Beaver (dbeaver) 2-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#include "precomp.h" +#pragma hdrstop + + +typedef struct _QUERY_INDICATION { + UCHAR Command; + USHORT Data2; + UCHAR DestinationName[16]; + UCHAR SourceName[16]; +} QUERY_INDICATION, *PQUERY_INDICATION; + +typedef struct _ACTION_QUERY_INDICATION { + TDI_ACTION_HEADER Header; + QUERY_INDICATION QueryIndication; +} ACTION_QUERY_INDICATION, *PACTION_QUERY_INDICATION; + + +typedef struct _DATAGRAM_INDICATION { + UCHAR DestinationName[16]; + UCHAR SourceName[16]; + USHORT DatagramBufferLength; + UCHAR DatagramBuffer[1]; +} DATAGRAM_INDICATION, *PDATAGRAM_INDICATION; + +typedef struct _ACTION_DATAGRAM_INDICATION { + TDI_ACTION_HEADER Header; + DATAGRAM_INDICATION DatagramIndication; +} ACTION_DATAGRAM_INDICATION, *PACTION_DATAGRAM_INDICATION; + + +#define QUERY_INDICATION_CODE 1 +#define DATAGRAM_INDICATION_CODE 2 + + + +VOID +NbfCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + + +NTSTATUS +NbfTdiAction( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiAction request for the transport + provider. + +Arguments: + + DeviceContext - The device context for the operation + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + PTDI_ACTION_HEADER ActionHeader; + LARGE_INTEGER timeout = {0,0}; + PTP_REQUEST tpRequest; + KIRQL oldirql, cancelirql; + + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdl (Irp->MdlAddress); + + + // + // Handle the requests based on the action code. + // + + switch (ActionHeader->ActionCode) { + + case QUERY_INDICATION_CODE: + case DATAGRAM_INDICATION_CODE: + + // + // These two requests are sent by RAS to "MABF" + // + + if (!RtlEqualMemory ((PVOID)(&ActionHeader->TransportId), "MABF", 4)) { + return STATUS_NOT_SUPPORTED; + } + + // + // They should be sent on the control channel + // + + if (irpSp->FileObject->FsContext2 != (PVOID)NBF_FILE_TYPE_CONTROL) { + return STATUS_NOT_SUPPORTED; + } + + + // + // Create a request to describe this. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + DeviceContext, // context. + REQUEST_FLAGS_DC, // partial flags. + Irp->MdlAddress, + MmGetMdlByteCount(Irp->MdlAddress), + timeout, + &tpRequest); + + if (NT_SUCCESS (status)) { + + NbfReferenceDeviceContext ("Action", DeviceContext, DCREF_REQUEST); + tpRequest->Owner = DeviceContextType; + tpRequest->FrameContext = (USHORT)irpSp->FileObject->FsContext; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + + // + // Disallow these requests on a stopping device. + // + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); + + } else { + + if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) { + + InsertTailList ( + &DeviceContext->QueryIndicationQueue, + &tpRequest->Linkage); + + } else { + + InsertTailList ( + &DeviceContext->DatagramIndicationQueue, + &tpRequest->Linkage); + + } + + DeviceContext->IndicationQueuesInUse = TRUE; + + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + Irp->CancelIrql = cancelirql; + NbfCancelAction((PDEVICE_OBJECT)DeviceContext, Irp); + return STATUS_PENDING; + } + + IoSetCancelRoutine(Irp, NbfCancelAction); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + } + + status = STATUS_PENDING; + + } + + break; + + default: + + status = STATUS_NOT_IMPLEMENTED; + + } + + + return status; + +} + + +VOID +NbfCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel an Action. + What is done to cancel it is specific to each action. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_REQUEST Request; + PLIST_ENTRY p; + BOOLEAN Found; + PTDI_ACTION_HEADER ActionHeader; + PLIST_ENTRY QueueHead, QueueEnd; + + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_ACTION)); + + ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdl (Irp->MdlAddress); + + switch (ActionHeader->ActionCode) { + + case QUERY_INDICATION_CODE: + case DATAGRAM_INDICATION_CODE: + + // + // Scan through the appropriate queue, looking for this IRP. + // If we find it, we just remove it from the queue; there + // is nothing else involved in cancelling. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) { + QueueHead = DeviceContext->QueryIndicationQueue.Flink; + QueueEnd = &DeviceContext->QueryIndicationQueue; + } else { + QueueHead = DeviceContext->DatagramIndicationQueue.Flink; + QueueEnd = &DeviceContext->DatagramIndicationQueue; + } + + Found = FALSE; + for (p = QueueHead; p != QueueEnd; p = p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if (Request->IoRequestPacket == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + break; + + } + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + NbfCompleteRequest (Request, STATUS_CANCELLED, 0); + + } else { + +#if DBG + DbgPrint("NBF: Tried to cancel action %lx on %lx, not found\n", + Irp, DeviceContext); +#endif + } + + break; + + default: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + break; + + } + + +} + + +VOID +NbfStopControlChannel( + IN PDEVICE_CONTEXT DeviceContext, + IN USHORT ChannelIdentifier + ) + +/*++ + +Routine Description: + + This routine is called when an MJ_CLEANUP IRP is received + on a control channel. It walks the device context's list of + pending action requests and cancels those associated with + this channel (as identified by ChannelIdentifier. + +Arguments: + + DeviceContext - Pointer to our device context. + + ChannelIdentifier - The identifier for this open of the control + channel, which is stored in Request->FrameContext for requests + made on this channel. + +Return Value: + + None + +--*/ + +{ + + KIRQL oldirql, cancelirql; + PTP_REQUEST Request; + PLIST_ENTRY p; + UINT i; + BOOLEAN FoundRequest; + PLIST_ENTRY QueueHead, QueueEnd; + + + // + // Scan both queues, looking for requests. Since the list + // may change, we scan until we find one, then remove it + // and complete it. We then start scanning at the beginning + // again. We continue until we find none on the queue that + // belong to this control channel. + // + // The outer loop only runs twice; the first time it + // processes QueryIndicationQueue, the second time + // DatagramIndicationQueue. + // + + for (i = 0; i < 2; i++) { + + do { + + // + // Loop until we do not find a request on this + // pass through the queue. + // + + FoundRequest = FALSE; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (i == 0) { + QueueHead = DeviceContext->QueryIndicationQueue.Flink; + QueueEnd = &DeviceContext->QueryIndicationQueue; + } else { + QueueHead = DeviceContext->DatagramIndicationQueue.Flink; + QueueEnd = &DeviceContext->DatagramIndicationQueue; + } + + + // + // Scan the appropriate queue for a request on this + // channel. + // + + for (p = QueueHead; p != QueueEnd; p = p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if (Request->FrameContext == ChannelIdentifier) { + + // + // Found it, remove it from the list here. + // + + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + RemoveEntryList (p); + + FoundRequest = TRUE; + break; + + } + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // If we found a request, then complete it and loop + // back to the top of the while loop to rescan the + // list. If not, then we will exit the while loop + // now. + // + + if (FoundRequest) { + + NbfCompleteRequest (Request, STATUS_CANCELLED, 0); + + } + + } while (FoundRequest); + + } + +} + + +VOID +NbfActionQueryIndication( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame + ) + +/*++ + +Routine Description: + + This routine is called after a UI frame of type NAME_QUERY, + ADD_NAME_QUERY, or ADD_GROUP_NAME_QUERY has been processed. + It checks if there is a QUERY.INDICATION IRP waiting to + be completed, and if so completes it. + +Arguments: + + DeviceContext - Pointer to our device context. + + UiFrame - Pointer to the incoming frame. The first byte of + information is the first byte of the NetBIOS connectionless + header. + +Return Value: + + None + +--*/ + +{ + KIRQL oldirql, cancelirql; + PTP_REQUEST Request; + PLIST_ENTRY p; + PMDL Mdl; + PACTION_QUERY_INDICATION ActionHeader; + PQUERY_INDICATION QueryIndication; + + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (!IsListEmpty (&DeviceContext->QueryIndicationQueue)) { + + p = RemoveHeadList (&DeviceContext->QueryIndicationQueue); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(Request->IoRequestPacket,NULL); + IoReleaseCancelSpinLock(cancelirql); + + Mdl = Request->Buffer2; + ActionHeader = (PACTION_QUERY_INDICATION) + (MmGetSystemAddressForMdl(Mdl)); + QueryIndication = &ActionHeader->QueryIndication; + + // + // Copy over data from frame (note that dest and source + // address are copied with one call). + // + + QueryIndication->Command = UiFrame->Command; + RtlCopyMemory ((PUCHAR)(&QueryIndication->Data2), (PUCHAR)(&UiFrame->Data2Low), 2); + RtlCopyMemory ((PUCHAR)(QueryIndication->DestinationName), + (PUCHAR)(UiFrame->DestinationName), + 2 * NETBIOS_NAME_LENGTH); + + NbfCompleteRequest (Request, STATUS_SUCCESS, sizeof(ACTION_QUERY_INDICATION)); + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + } +} + + +VOID +NbfActionDatagramIndication( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine is called after a datagram frame has been + received. It checks if there is a DATAGRAM.INDICATION IRP + waiting to be completed, and if so completes it. + +Arguments: + + DeviceContext - Pointer to our device context. + + UiFrame - Pointer to the incoming frame. The first byte of + information is the first byte of the NetBIOS connectionless + header. + + Length - The length of the frame starting at UiFrame. + +Return Value: + + None + +--*/ + +{ + KIRQL oldirql, cancelirql; + PTP_REQUEST Request; + PLIST_ENTRY p; + PACTION_DATAGRAM_INDICATION ActionHeader; + PDATAGRAM_INDICATION DatagramIndication; + ULONG CopyLength; + PMDL Mdl; + NTSTATUS Status; + + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (!IsListEmpty (&DeviceContext->DatagramIndicationQueue)) { + + p = RemoveHeadList (&DeviceContext->DatagramIndicationQueue); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock(cancelirql); + + Mdl = Request->Buffer2; + ActionHeader = (PACTION_DATAGRAM_INDICATION) + (MmGetSystemAddressForMdl(Mdl)); + DatagramIndication = &ActionHeader->DatagramIndication; + + // + // Copy over data from frame (note that dest and source + // address are copied with one call). + // + + RtlCopyMemory ((PUCHAR)(DatagramIndication->DestinationName), + (PUCHAR)(UiFrame->DestinationName), + 2 * NETBIOS_NAME_LENGTH); + + if ((Length-sizeof(NBF_HDR_CONNECTIONLESS)) <= + (ULONG)DatagramIndication->DatagramBufferLength) { + + CopyLength = Length - sizeof(NBF_HDR_CONNECTIONLESS); + Status = STATUS_SUCCESS; + + } else { + + CopyLength = DatagramIndication->DatagramBufferLength; + Status = STATUS_BUFFER_OVERFLOW; + + } + + + RtlCopyMemory( + (PUCHAR)DatagramIndication->DatagramBuffer, + ((PUCHAR)UiFrame) + sizeof(NBF_HDR_CONNECTIONLESS), + CopyLength); + DatagramIndication->DatagramBufferLength = (USHORT)CopyLength; + + NbfCompleteRequest (Request, Status, CopyLength + + FIELD_OFFSET (ACTION_DATAGRAM_INDICATION, DatagramIndication.DatagramBuffer[0])); + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + } +} + diff --git a/private/ntos/tdi/nbf/address.c b/private/ntos/tdi/nbf/address.c new file mode 100644 index 000000000..f5bda0cc7 --- /dev/null +++ b/private/ntos/tdi/nbf/address.c @@ -0,0 +1,3046 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the TP_ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +#define NbfDbgShowAddr(TNA)\ + { \ + if ((TNA) == NULL) { \ + NbfPrint0("\n"); \ + } else { \ + NbfPrint6("%c %c %c %c %d (%c)\n", \ + (TNA)->NetbiosName[0], \ + (TNA)->NetbiosName[1], \ + (TNA)->NetbiosName[4], \ + (TNA)->NetbiosName[6], \ + (TNA)->NetbiosName[15], \ + (TNA)->NetbiosNameType + 'A'); \ + } \ + } +#else +#define NbfDbgShowAddr(TNA) +#endif + +// +// Map all generic accesses to the same one. +// + +STATIC GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + +VOID +AddressTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the ADD_NAME_QUERY/ADD_NAME_RECOGNIZED protocol expires. + The retry count in the Address object is decremented, and if it reaches 0, + the address is registered. If the retry count has not reached zero, + then the ADD NAME QUERY is retried. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_ADDRESS block representing the + address that is being registered. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + LARGE_INTEGER timeout; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + + address = (PTP_ADDRESS)DeferredContext; + DeviceContext = address->Provider; + + // + // We are waiting for an ADD_NAME_RECOGNIZED indicating that there is a + // conflict. Decrement the retry count, and if it dropped to zero, + // then we've waited a sufficiently long time. If there was no conflict, + // complete all waiting file opens for the address. + // + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + + if ((address->Flags & ADDRESS_FLAGS_QUICK_REREGISTER) != 0) { + + BOOLEAN DuplicateName; + PTP_CONNECTION Connection; + + DuplicateName = ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0); + + for (p=address->ConnectionDatabase.Flink; + p != &address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + continue; + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_W_ADDRESS) != 0) { + + if (DuplicateName) { + + NbfStopConnection (Connection, STATUS_DUPLICATE_NAME); + + } else { + + // + // Continue with the connection attempt. + // + ULONG NameQueryTimeout; + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Connection->Flags2 &= ~CONNECTION_FLAGS2_W_ADDRESS; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + KeQueryTickCount (&Connection->ConnectStartTime); + + NameQueryTimeout = Connection->Provider->NameQueryTimeout; + if (Connection->Provider->MacInfo.MediumAsync && + !Connection->Provider->MediumSpeedAccurate) { + NameQueryTimeout = NAME_QUERY_TIMEOUT / 10; + } + + NbfSendNameQuery ( + Connection, + TRUE); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + NameQueryTimeout); + } + + } + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + + } + + address->Flags &= ~ADDRESS_FLAGS_QUICK_REREGISTER; + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); + + } else if ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0) { + + PIRP irp; + + // + // the address registration has failed. We signal the user in + // the normal way (by failing the open of the address). Now clean up + // the transport's data structures. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("AddressTimeoutHandler %lx: duplicate\n", address); + } + + address->Flags &= ~ADDRESS_FLAGS_REGISTERING; +// address->Flags |= ADDRESS_FLAGS_STOPPING; + + // + // BUGBUG: This is probably all overkill, the + // uframes handler will already have called + // NbfStopAddress, which will tear off all + // the address files etc., and set the + // STOPPING flag which prevents further opens. + // + + p = address->AddressFileDatabase.Flink; + while (p != &address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + p = p->Flink; + + if (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + NbfStopAddressFile (addressFile, address); + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + } + + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + // + // There will be no more timer events happening, so we dereference the + // address to account for the timer. + // + + NbfStopAddress (address); + NbfDereferenceAddress ("Timer, dup address", address, AREF_TIMER); + + } else { + + // + // has the address registration succeeded? + // + + if (--(address->Retries) <= 0) { // if retry count exhausted. + PIRP irp; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("AddressTimeoutHandler %lx: successful.\n", address); + } + + address->Flags &= ~ADDRESS_FLAGS_REGISTERING; + + p = address->AddressFileDatabase.Flink; + + while (p != &address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + p = p->Flink; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3 ("AddressTimeoutHandler %lx: Completing IRP %lx for file %lx\n", + address, + addressFile->Irp, + addressFile); + } + + if (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_SUCCESS; + + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + } + + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + // + // Dereference the address if we're all done. + // + + NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); + + } else { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("AddressTimeoutHandler %lx: step %x.\n", + address, + DeviceContext->AddNameQueryRetries - address->Retries); + } + + // + // restart the timer if we haven't yet completed registration + // + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + timeout.LowPart = (ULONG)(-(LONG)DeviceContext->AddNameQueryTimeout); + timeout.HighPart = -1; + KeSetTimer (&address->Timer,*(PTIME)&timeout, &address->Dpc); + (VOID)NbfSendAddNameQuery (address); // send another ADD_NAME_QUERY. + } + + } + + LEAVE_NBF; + return; + +} /* AddressTimeoutHandler */ + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbfParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk +) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == + sizeof(TDI_ADDRESS_NETBIOS)) { + return((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbfParseTdiAddress */ + + +BOOLEAN +NbfValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength +) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbfPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;iTAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbfValidateTdiAddress */ + + +NTSTATUS +NbfOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the NBF transport. + + Irp - a pointer to the Irp used for the creation of the address. + + IrpSp - a pointer to the Irp stack location. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PNBF_NETBIOS_ADDRESS networkName; // Network name string. + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED *netbiosName; + ULONG DesiredShareAccess; + KIRQL oldirql; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN QuickAdd = FALSE; + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // The network name is in the EA, passed in AssociatedIrp.SystemBuffer + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + if (ea == NULL) { + NbfPrint1("OpenAddress: IRP %lx has no EA\n", Irp); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (TRANSPORT_ADDRESS UNALIGNED *)&ea->EaName[ea->EaNameLength+1]; + + if (!NbfValidateTdiAddress(name, ea->EaValueLength)) { + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // The name can have with multiple entries; we'll use the Netbios one. + // This call returns NULL if not Netbios address is found, (PVOID)-1 + // if it is the broadcast address, and a pointer to a Netbios + // address otherwise. + // + + netbiosName = NbfParseTdiAddress(name, TRUE); + + if (netbiosName != NULL) { + if (netbiosName != (PVOID)-1) { + networkName = (PNBF_NETBIOS_ADDRESS)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (NBF_NETBIOS_ADDRESS), + 'nFBN'); + if (networkName == NULL) { + PANIC ("NbfOpenAddress: PANIC! could not allocate networkName!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 1, + sizeof(TA_NETBIOS_ADDRESS), + ADDRESS_RESOURCE_ID); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get the name to local storage + // + + if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) || + (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; + } else { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + } + RtlCopyMemory (networkName->NetbiosName, netbiosName->NetbiosName, 16); + + if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE) || + (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { + QuickAdd = TRUE; + } + } else { + networkName = NULL; + } + + } else { + NbfPrint1("OpenAddress: IRP %lx has no NETBIOS address\n", Irp); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("OpenAddress %s: ", + ((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + "shared" : "exclusive"); + NbfDbgShowAddr (networkName); + } + + // + // get an address file structure to represent this address. + // + + status = NbfCreateAddressFile (DeviceContext, &addressFile); + + if (!NT_SUCCESS (status)) { + if (networkName != NULL) { + ExFreePool (networkName); + } + return status; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context AddressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&DeviceContext->AddressResource, TRUE); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + address = NbfLookupAddress (DeviceContext, networkName); + + if (address == NULL) { + + // + // This address doesn't exist. Create it, and start the process of + // registering it. + // + + status = NbfCreateAddress ( + DeviceContext, + networkName, + &address); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (NT_SUCCESS (status)) { + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped. BUGBUG: Need to synchronize Assign and Access). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + PagedPool); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3 ("Assign security A %lx AF %lx, status %lx\n", + address, + addressFile, + status); + } + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &address->u.ShareAccess); + ExReleaseResource (&DeviceContext->AddressResource); + NbfDereferenceAddress ("Device context stopping", address, AREF_TEMP_CREATE); + NbfDereferenceAddressFile (addressFile); + return status; + + } + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3("OpenAddress A %lx AF %lx: DeviceContext %lx not open\n", + address, + addressFile, + DeviceContext); + } + NbfDereferenceAddressFile (addressFile); + status = STATUS_DEVICE_NOT_READY; + + } else { + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + addressFile->FileObject = IrpSp->FileObject; + addressFile->Irp = Irp; + addressFile->Address = address; + + NbfReferenceAddress("Opened new", address, AREF_OPEN); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: created.\n", + address, + addressFile); + } + + ExInterlockedInsertTailList( + &address->AddressFileDatabase, + &addressFile->Linkage, + &address->SpinLock); + + + // + // Begin address registration unless this is the broadcast + // address (which is a "fake" address with no corresponding + // Netbios address) or the reserved address, which we know + // is unique since it is based on the adapter address. + // + // Also, for "quick" add names, do not register. + // + + if ((networkName != NULL) && + (!RtlEqualMemory (networkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) && + (!QuickAdd)) { + + NbfRegisterAddress (address); // begin address registration. + status = STATUS_PENDING; + + } else { + + address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } + + NbfDereferenceAddress("temp create", address, AREF_TEMP_CREATE); + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + // + + if (networkName != NULL) { + ExFreePool (networkName); + } + + NbfDereferenceAddressFile (addressFile); + + } + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint4 ("Access check A %lx AF %lx, %s (%lx)\n", + address, + addressFile, + AccessAllowed ? "allowed" : "not allowed", + status); + } + + if (AccessAllowed) { + + // + // Access was successful, make sure Status is right. + // + + status = STATUS_SUCCESS; + + // + // BUGBUG: Compare DesiredAccess to GrantedAccess? + // + + + // + // Check that the name is of the correct type (unique vs. group) + // We don't need to check this for the broadcast address. + // + // BUGBUG: This code is structured funny, the only reason + // this is inside this if is to avoid indenting too much. + // + + if (networkName != NULL) { + if (address->NetworkName->NetbiosNameType != + networkName->NetbiosNameType) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("Address types differ: old %c, new %c\n", + address->NetworkName->NetbiosNameType + 'A', + networkName->NetbiosNameType + 'A'); + } + + status = STATUS_DUPLICATE_NAME; + + } + } + + } + + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: ACL bad.\n", + address, + addressFile); + } + + NbfDereferenceAddressFile (addressFile); + + } else { + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->u.ShareAccess, + TRUE); + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: ShareAccess problem.\n", + address, + addressFile); + } + + NbfDereferenceAddressFile (addressFile); + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // now, if the address registered, we simply return success after + // pointing the file object at the address file (which points to + // the address). If the address registration is pending, we mark + // the registration pending and let the registration completion + // routine complete the open. If the address is bad, we simply + // fail the open. + // + + if ((address->Flags & + (ADDRESS_FLAGS_CONFLICT | + ADDRESS_FLAGS_REGISTERING | + ADDRESS_FLAGS_DEREGISTERING | + ADDRESS_FLAGS_DUPLICATE_NAME | + ADDRESS_FLAGS_NEEDS_REG | + ADDRESS_FLAGS_STOPPING | + ADDRESS_FLAGS_BAD_ADDRESS | + ADDRESS_FLAGS_CLOSED)) == 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = NULL; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + addressFile->State = ADDRESSFILE_STATE_OPEN; + + NbfReferenceAddress("open ready", address, AREF_OPEN); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: address ready.\n", + address, + addressFile); + } + + status = STATUS_SUCCESS; + + } else { + + // + // if the address is still registering, make the open pending. + // + + if ((address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_NEEDS_REG)) != 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = Irp; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + + NbfReferenceAddress("open registering", address, AREF_OPEN); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: address registering.\n", + address, + addressFile); + } + + status = STATUS_PENDING; + + } else { + + if ((address->Flags & ADDRESS_FLAGS_CONFLICT) != 0) { + status = STATUS_DUPLICATE_NAME; + } else { + status = STATUS_DRIVER_INTERNAL_ERROR; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3("OpenAddress A %lx AF %lx: address flags %lx.\n", + address, + addressFile, + address->Flags); + } + + NbfDereferenceAddressFile (addressFile); + + } + } + } + } + + + // + // This isn't needed since it was not used in the + // creation of the address. + // + + if (networkName != NULL) { + ExFreePool (networkName); + } + + // + // Remove the reference from NbfLookupAddress. + // + + NbfDereferenceAddress ("Done opening", address, AREF_LOOKUP); + } + + return status; +} /* NbfOpenAddress */ + + +VOID +NbfAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport address. Some minimal + initialization is done on the address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. Returns NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_ADDRESS Address; + PSEND_PACKET_TAG SendTag; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate address: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 101, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + *TransportAddress = NULL; + return; + } + + Address = (PTP_ADDRESS)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_ADDRESS), + 'aFBN'); + if (Address == NULL) { + PANIC("NBF: Could not allocate address: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 201, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address, sizeof(TP_ADDRESS)); + + NdisAllocatePacketPool( + &NdisStatus, + &Address->UIFramePoolHandle, + 1, + sizeof(SEND_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC("NBF: Could not allocate address UI frame pool: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 311, + sizeof(SEND_PACKET_TAG), + ADDRESS_RESOURCE_ID); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + + + // + // This code is similar to NbfAllocateUIFrame. + // + + Address->UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->UIFrameLength, + 'uFBN'); + if (Address->UIFrame == NULL) { + PANIC("NBF: Could not allocate address UI frame: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 411, + DeviceContext->UIFrameLength, + ADDRESS_RESOURCE_ID); + NdisFreePacketPool (Address->UIFramePoolHandle); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address->UIFrame, DeviceContext->UIFrameLength); + + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + Address->UIFramePoolHandle); + + ASSERT (NdisStatus == NDIS_STATUS_SUCCESS); + + Address->UIFrame->NdisPacket = NdisPacket; + Address->UIFrame->DataBuffer = NULL; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_ADDRESS_FRAME; + SendTag->Owner = (PVOID)Address; + SendTag->Frame = Address->UIFrame; + + // + // Make the packet header known to the packet descriptor + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + Address->UIFrame->Header, + DeviceContext->UIFrameHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + PANIC("NBF: Could not allocate address UI frame buffer: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 511, + 0, + UI_FRAME_RESOURCE_ID); + ExFreePool (Address->UIFrame); + NdisFreePacketPool (Address->UIFramePoolHandle); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + DeviceContext->MemoryUsage += + sizeof(TP_ADDRESS) + + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + + DeviceContext->UIFrameLength; + ++DeviceContext->AddressAllocated; + + Address->Type = NBF_ADDRESS_SIGNATURE; + Address->Size = sizeof (TP_ADDRESS); + + Address->Provider = DeviceContext; + KeInitializeSpinLock (&Address->SpinLock); +// KeInitializeSpinLock (&Address->Interlock); + + InitializeListHead (&Address->ConnectionDatabase); + InitializeListHead (&Address->AddressFileDatabase); + InitializeListHead (&Address->SendDatagramQueue); + + KeInitializeDpc (&Address->Dpc, AddressTimeoutHandler, (PVOID)Address); + KeInitializeTimer (&Address->Timer); + + // + // For each address, allocate a receive packet and a receive buffer. + // + + NbfAddReceivePacket (DeviceContext); + NbfAddReceiveBuffer (DeviceContext); + + *TransportAddress = Address; + +} /* NbfAllocateAddress */ + + +VOID +NbfDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a transport address structure. + +Return Value: + + None. + +--*/ + +{ + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (TransportAddress->UIFrame->NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + ExFreePool (TransportAddress->UIFrame); + NdisFreePacketPool (TransportAddress->UIFramePoolHandle); + + ExFreePool (TransportAddress); + --DeviceContext->AddressAllocated; + + DeviceContext->MemoryUsage -= + sizeof(TP_ADDRESS) + + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + + DeviceContext->UIFrameLength; + + // + // Remove the resources which allocating this caused. + // + + NbfRemoveReceivePacket (DeviceContext); + NbfRemoveReceiveBuffer (DeviceContext); + +} /* NbfDeallocateAddress */ + + +NTSTATUS +NbfCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetworkName - Pointer to an NBF_NETBIOS_ADDRESS type containing the network + name to be associated with this address, if any. + NOTE: This has only the basic NetbiosNameType values, not the + QUICK_ ones. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS pAddress; + PLIST_ENTRY p; + + + p = RemoveHeadList (&DeviceContext->AddressPool); + if (p == &DeviceContext->AddressPool) { + + if ((DeviceContext->AddressMaxAllocated == 0) || + (DeviceContext->AddressAllocated < DeviceContext->AddressMaxAllocated)) { + + NbfAllocateAddress (DeviceContext, &pAddress); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated address at %lx\n", pAddress); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 401, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + pAddress = NULL; + + } + + if (pAddress == NULL) { + ++DeviceContext->AddressExhausted; + PANIC ("NbfCreateAddress: Could not allocate address object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + pAddress = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + } + + ++DeviceContext->AddressInUse; + if (DeviceContext->AddressInUse > DeviceContext->AddressMaxInUse) { + ++DeviceContext->AddressMaxInUse; + } + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + + + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint1 ("NbfCreateAddress %lx: ", pAddress); + NbfDbgShowAddr (NetworkName); + } + + // + // Initialize all of the static data for this address. + // + + pAddress->ReferenceCount = 1; + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_AREFS; Counter++) { + pAddress->RefTypes[Counter] = 0; + } + + // This reference is removed by the caller. + + pAddress->RefTypes[AREF_TEMP_CREATE] = 1; + } +#endif + + pAddress->Flags = ADDRESS_FLAGS_NEEDS_REG; + InitializeListHead (&pAddress->AddressFileDatabase); + + pAddress->NetworkName = NetworkName; + if ((NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) && + (NetworkName->NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP)) { + + pAddress->Flags |= ADDRESS_FLAGS_GROUP; + + } + + if (NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) { + ++DeviceContext->AddressCounts[NetworkName->NetbiosName[0]]; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&DeviceContext->AddressDatabase, &pAddress->Linkage); + pAddress->Provider = DeviceContext; + NbfReferenceDeviceContext ("Create Address", DeviceContext, DCREF_ADDRESS); // count refs to the device context. + + *Address = pAddress; // return the address. + return STATUS_SUCCESS; // not finished yet. +} /* NbfCreateAddress */ + + +VOID +NbfRegisterAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process of the transport address + specified, if it has not already been started. + +Arguments: + + Address - Pointer to a transport address object to begin registering + on the network. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + LARGE_INTEGER Timeout; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + if (!(Address->Flags & ADDRESS_FLAGS_NEEDS_REG)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfRegisterAddress %lx: NEEDS_REG 0.\n", Address); + } + + return; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfRegisterAddress %lx: registering.\n", Address); + } + + + Address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + Address->Flags |= ADDRESS_FLAGS_REGISTERING; + + RtlZeroMemory (Address->UniqueResponseAddress, 6); + + // + // Keep a reference on this address until the registration process + // completes or is aborted. It will be aborted in UFRAMES.C, in + // either the NAME_IN_CONFLICT or ADD_NAME_RESPONSE frame handlers. + // + + NbfReferenceAddress ("start registration", Address, AREF_TIMER); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now start registration process by starting up a retransmission timer + // and begin sending ADD_NAME_QUERY NetBIOS frames. + // + // On an async line that is disconnected, we only send one packet + // with a short timeout. + // + + if (Address->Provider->MacInfo.MediumAsync && !Address->Provider->MediumSpeedAccurate) { + Address->Retries = 1; + Timeout.LowPart = (ULONG)(-(ADD_NAME_QUERY_TIMEOUT / 10)); + } else { + Address->Retries = Address->Provider->AddNameQueryRetries; + Timeout.LowPart = (ULONG)(-(LONG)Address->Provider->AddNameQueryTimeout); + } + Timeout.HighPart = -1; + KeSetTimer (&Address->Timer, *(PTIME)&Timeout, &Address->Dpc); + + (VOID)NbfSendAddNameQuery (Address); // send first ADD_NAME_QUERY. +} /* NbfRegisterAddress */ + + +NTSTATUS +NbfVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a TP_ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + PTP_ADDRESS address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile != (PTP_ADDRESS_FILE)NULL) && + (AddressFile->Size == sizeof (TP_ADDRESS_FILE)) && + (AddressFile->Type == NBF_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + address = AddressFile->Address; + + if ((address != (PTP_ADDRESS)NULL) && + (address->Size == sizeof (TP_ADDRESS)) && + (address->Type == NBF_ADDRESS_SIGNATURE) ) { + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + NbfReferenceAddress ("verify", address, AREF_VERIFY); + + } else { + + NbfPrint1("NbfVerifyAddress: A %lx closing\n", address); + status = STATUS_INVALID_ADDRESS; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + } else { + + NbfPrint1("NbfVerifyAddress: A %lx bad signature\n", address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbfPrint1("NbfVerifyAddress: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbfPrint1("NbfVerifyAddress: AF %lx exception\n", address); + return GetExceptionCode(); + } + + return status; + +} + +VOID +NbfDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool or our lookaside list. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + The routine is called from a worker thread so that the security + descriptor can be accessed. + + This worked thread is only queued by NbfDerefAddress. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_ADDRESS Address = (PTP_ADDRESS)Parameter; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfDestroyAddress %lx:.\n", Address); + } + + DeviceContext = Address->Provider; + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (Address->NetworkName) { + --DeviceContext->AddressCounts[Address->NetworkName->NetbiosName[0]]; + } + + RemoveEntryList (&Address->Linkage); + + if (Address->NetworkName != NULL) { + ExFreePool (Address->NetworkName); + Address->NetworkName = NULL; + } + + // + // Now we can deallocate the transport address object. + // + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + --DeviceContext->AddressInUse; + + if ((DeviceContext->AddressAllocated - DeviceContext->AddressInUse) > + DeviceContext->AddressInitAllocated) { + NbfDeallocateAddress (DeviceContext, Address); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated address at %lx\n", Address); + } + } else { + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + NbfDereferenceDeviceContext ("Destroy Address", DeviceContext, DCREF_ADDRESS); // just housekeeping. + +} /* NbfDestroyAddress */ + + +#if DBG +VOID +NbfRefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Address->ReferenceCount); + +} /* NbfRefAddress */ +#endif + + +VOID +NbfDerefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Address->ReferenceCount); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + if (result == 0) { + + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbfDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); + } +} /* NbfDerefAddress */ + + + +VOID +NbfAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine allocates storage for an address file. Some + minimal initialization is done on the object. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a place where this routine will return + a pointer to a transport address file structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_ADDRESS_FILE AddressFile; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS_FILE)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate address file: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 102, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + *TransportAddressFile = NULL; + return; + } + + AddressFile = (PTP_ADDRESS_FILE)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_ADDRESS_FILE), + 'fFBN'); + if (AddressFile == NULL) { + PANIC("NBF: Could not allocate address file: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 202, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + *TransportAddressFile = NULL; + return; + } + RtlZeroMemory (AddressFile, sizeof(TP_ADDRESS_FILE)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS_FILE); + ++DeviceContext->AddressFileAllocated; + + AddressFile->Type = NBF_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (TP_ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + *TransportAddressFile = AddressFile; + +} /* NbfAllocateAddressFile */ + + +VOID +NbfDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine frees storage for an address file. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a transport address file structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportAddressFile); + --DeviceContext->AddressFileAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS_FILE); + +} /* NbfDeallocateAddressFile */ + + +NTSTATUS +NbfCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AddressFile - Pointer to a place where this routine will return a pointer + to a transport address file structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PTP_ADDRESS_FILE addressFile; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->AddressFilePool); + if (p == &DeviceContext->AddressFilePool) { + + if ((DeviceContext->AddressFileMaxAllocated == 0) || + (DeviceContext->AddressFileAllocated < DeviceContext->AddressFileMaxAllocated)) { + + NbfAllocateAddressFile (DeviceContext, &addressFile); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated address file at %lx\n", addressFile); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 402, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + addressFile = NULL; + + } + + if (addressFile == NULL) { + ++DeviceContext->AddressFileExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateAddressFile: Could not allocate address file object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + } + + ++DeviceContext->AddressFileInUse; + if (DeviceContext->AddressFileInUse > DeviceContext->AddressFileMaxInUse) { + ++DeviceContext->AddressFileMaxInUse; + } + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + InitializeListHead (&addressFile->ConnectionDatabase); + addressFile->Address = NULL; + addressFile->FileObject = NULL; + addressFile->Provider = DeviceContext; + addressFile->State = ADDRESSFILE_STATE_OPENING; + addressFile->ConnectIndicationInProgress = FALSE; + addressFile->ReferenceCount = 1; + addressFile->CloseIrp = (PIRP)NULL; + + // + // Initialize the request handlers. + // + + addressFile->RegisteredConnectionHandler = FALSE; + addressFile->ConnectionHandler = TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + addressFile->DisconnectHandler = TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + addressFile->ReceiveHandler = TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + addressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + addressFile->ExpeditedDataHandler = TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + addressFile->ErrorHandler = TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + + + *AddressFile = addressFile; + return STATUS_SUCCESS; + +} /* NbfCreateAddress */ + + +NTSTATUS +NbfDestroyAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbfDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS address; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + + address = AddressFile->Address; + DeviceContext = AddressFile->Provider; + + if (address) { + + // + // This addressfile was associated with an address. + // + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (address->AddressFileDatabase.Flink == &address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + } + + AddressFile->Address = NULL; + + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + NbfDereferenceAddress ("Close", address, AREF_OPEN); // remove the creation hold + + } + + // + // Save this for later completion. + // + + CloseIrp = AddressFile->CloseIrp; + + // + // return the addressFile to the pool of address files + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + --DeviceContext->AddressFileInUse; + + if ((DeviceContext->AddressFileAllocated - DeviceContext->AddressFileInUse) > + DeviceContext->AddressFileInitAllocated) { + NbfDeallocateAddressFile (DeviceContext, AddressFile); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated address file at %lx\n", AddressFile); + } + } else { + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + if (CloseIrp != (PIRP)NULL) { + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + } + + return STATUS_SUCCESS; + +} /* NbfDestroyAddressFile */ + + +VOID +NbfReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&AddressFile->ReferenceCount); + +} /* NbfReferenceAddressFile */ + + +VOID +NbfDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&AddressFile->ReferenceCount); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyAddressFile (AddressFile); + } +} /* NbfDerefAddressFile */ + + +PTP_ADDRESS +NbfLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + TP_ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device object and its extension. + NetworkName - Pointer to an NBF_NETBIOS_ADDRESS structure containing the + network name. + +Return Value: + + Pointer to the TP_ADDRESS object found, or NULL if not found. + +--*/ + +{ + PTP_ADDRESS address; + PLIST_ENTRY p; + ULONG i; + + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // If the network name is specified and the network names don't match, + // then the addresses don't match. + // + + i = NETBIOS_NAME_LENGTH; // length of a Netbios name + + if (address->NetworkName != NULL) { + if (NetworkName != NULL) { + if (!RtlEqualMemory ( + address->NetworkName->NetbiosName, + NetworkName->NetbiosName, + i)) { + continue; + } + } else { + continue; + } + + } else { + if (NetworkName != NULL) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("NbfLookupAddress DC %lx: found %lx ", DeviceContext, address); + NbfDbgShowAddr (NetworkName); + } + + NbfReferenceAddress ("lookup", address, AREF_LOOKUP); + return address; + + } /* for */ + + // + // The specified address was not found. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfLookupAddress DC %lx: did not find ", address); + NbfDbgShowAddr (NetworkName); + } + + return NULL; + +} /* NbfLookupAddress */ + + +PTP_CONNECTION +NbfLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName, + IN UCHAR RemoteSessionNumber + ) + +/*++ + +Routine Description: + + + This routine scans the connections associated with the + given address, and determines if there is an connection + associated with the specific remote address and session + number which is becoming active. This is used + in determining whether name queries should be processed, + or ignored as duplicates. + +Arguments: + + Address - Pointer to the address object. + + RemoteName - The 16-character Netbios name of the remote. + + RemoteSessionNumber - The session number assigned to this + connection by the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION connection; + BOOLEAN Found = FALSE; + + + // + // Hold the spinlock so the connection database doesn't + // change. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if (((connection->Flags2 & CONNECTION_FLAGS2_REMOTE_VALID) != 0) && + ((connection->Flags & CONNECTION_FLAGS_READY) == 0)) { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + // + // If the remote names match, and the connection's RSN is + // the same (or zero, which is a temporary condition where + // we should err on the side of caution), then return the + // connection, which will cause the NAME_QUERY to be ignored. + // + + if ((RtlEqualMemory(RemoteName, connection->RemoteName, NETBIOS_NAME_LENGTH)) && + ((connection->Rsn == RemoteSessionNumber) || (connection->Rsn == 0))) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + NbfReferenceConnection ("Lookup found", connection, CREF_LISTENING); + Found = TRUE; + + } + + } else { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupRemoteName\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + return (PTP_CONNECTION)NULL; + } + + if (Found) { + return connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return (PTP_CONNECTION)NULL; + +} + + +BOOLEAN +NbfMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN UCHAR NameType, + IN PUCHAR NetBIOSName + ) + +/*++ + +Routine Description: + + This routine is called to compare the addressing information in a + TP_ADDRESS object with the 16-byte NetBIOS name in a frame header. + If they match, then this routine returns TRUE, else it returns FALSE. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + + NameType - One of NETBIOS_NAME_TYPE_GROUP, NETBIOS_NAME_TYPE_UNIQUE, + or NETBIOS_NAME_TYPE_EITHER. Controls what type we are matching + on, if it matters. + + NetBIOSName - Pointer to a 16-byte character string (non-terminated), + or NULL if this is a received broadcast address. + +Return Value: + + BOOLEAN, TRUE if match, FALSE if not. + +--*/ + +{ + + PULONG AddressNamePointer; + ULONG UNALIGNED * NetbiosNamePointer; + + // + // If this is address is the Netbios broadcast address, the comparison + // succeeds only if the passed in address is also NULL. + // + + if (Address->NetworkName == NULL) { + + if (NetBIOSName == NULL) { + return TRUE; + } else { + return FALSE; + } + + } else if (NetBIOSName == NULL) { + + return FALSE; + + } + + // + // Do a quick check of the first character in the names. + // + + if (Address->NetworkName->NetbiosName[0] != NetBIOSName[0]) { + return FALSE; + } + + // + // If name type is important and it doesn't match + // this address' type, fail. + // + + if (NameType != NETBIOS_NAME_TYPE_EITHER) { + + if (Address->NetworkName->NetbiosNameType != (USHORT)NameType) { + + return FALSE; + } + } + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint2 ("MatchNetbiosAddress %lx: compare %.16s to ", Address, NetBIOSName); + NbfDbgShowAddr (Address->NetworkName); + } + + // + // Now compare the 16-character Netbios names as ULONGs + // for speed. We know the one stored in the address + // structure is aligned. + // + + AddressNamePointer = (PULONG)(Address->NetworkName->NetbiosName); + NetbiosNamePointer = (ULONG UNALIGNED *)NetBIOSName; + + if ((AddressNamePointer[0] == NetbiosNamePointer[0]) && + (AddressNamePointer[1] == NetbiosNamePointer[1]) && + (AddressNamePointer[2] == NetbiosNamePointer[2]) && + (AddressNamePointer[3] == NetbiosNamePointer[3])) { + return TRUE; + } else { + return FALSE; + } + +} /* NbfMatchNetbiosAddress */ + + +VOID +NbfStopAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an address and + destroy the object. This is done in a graceful manner; i.e., all + outstanding addressfiles are removed from the addressfile database, and + all their activities are shut down. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS_FILE addressFile; + PLIST_ENTRY p; + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + // + // If we're already stopping this address, then don't try to do it again. + // + + if (!(Address->Flags & ADDRESS_FLAGS_STOPPING)) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddress %lx: stopping\n", Address); + } + + NbfReferenceAddress ("Stopping", Address, AREF_TEMP_STOP); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + Address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + // + // Run down all addressfiles on this address. This + // will leave the address with no references + // potentially, but we don't need a temp one + // because every place that calls NbfStopAddress + // already has a temp reference. + // + + while (!IsListEmpty (&Address->AddressFileDatabase)) { + p = RemoveHeadList (&Address->AddressFileDatabase); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + addressFile->Address = NULL; + addressFile->FileObject->FsContext = NULL; + addressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Run-down this addressFile without the lock on. + // We don't care about removing ourselves from + // the address' ShareAccess because we are + // tearing it down. + // + + NbfStopAddressFile (addressFile, Address); + + // + // return the addressFile to the pool of address files + // + + NbfDereferenceAddressFile (addressFile); + + NbfDereferenceAddress ("stop address", Address, AREF_OPEN); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + NbfDereferenceAddress ("Stopping", Address, AREF_TEMP_STOP); + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddress %lx: already stopping\n", Address); + } + + } + +} /* NbfStopAddress */ + + +NTSTATUS +NbfStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + KIRQL oldirql, oldirql1; + LIST_ENTRY localIrpList; + PLIST_ENTRY p, pFlink; + PIRP irp; + PTP_CONNECTION connection; + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddressFile %lx: already closing.\n", AddressFile); + } + return STATUS_SUCCESS; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddressFile %lx: closing.\n", AddressFile); + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + InitializeListHead (&localIrpList); + + // + // Run down all connections on this addressfile, and + // preform the equivalent of NbfDestroyAssociation + // on them. + // + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressFileList); + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + + // + // It is in the process of being disassociated already. + // + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 |= CONNECTION_FLAGS2_DESTROY; // BUGBUG: Is this needed? + RemoveEntryList (&connection->AddressList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + NbfReferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfStopAddressFile\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TpStopEndpoint stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + KeLowerIrql (oldirql1); + NbfDereferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + + NbfDereferenceAddress ("Destroy association", Address, AREF_CONNECTION); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + // + // now remove all of the datagrams owned by this addressfile + // + + // + // If the address has a datagram send in progress, skip the + // first one, it will complete when the NdisSend completes. + // + + p = Address->SendDatagramQueue.Flink; + if (Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS) { + ASSERT (p != &Address->SendDatagramQueue); + p = p->Flink; + } + + for ( ; + p != &Address->SendDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (IoGetCurrentIrpStackLocation(irp)->FileObject->FsContext == AddressFile) { + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localIrpList, p); + } + + } + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localIrpList, p); + } + + // + // and finally, signal failure if the address file was waiting for a + // registration to complete (Irp is set to NULL when this succeeds). + // + + if (AddressFile->Irp != NULL) { + PIRP irp=AddressFile->Irp; +#if DBG + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + DbgPrint ("NBF: AddressFile %lx closed while opening!!\n", AddressFile); + DbgBreakPoint(); + } +#endif + AddressFile->Irp = NULL; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + // + // cancel all the datagrams on this address file + // + + while (!IsListEmpty (&localIrpList)) { + KIRQL cancelIrql; + + p = RemoveHeadList (&localIrpList); + irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_NETWORK_NAME_DELETED; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Datagram aborted", Address, AREF_REQUEST); + } + +} /* NbfStopAddressFile */ + + +NTSTATUS +NbfCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Irp - the Irp Address - Pointer to a TP_ADDRESS object. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + + addressFile = IrpSp->FileObject->FsContext; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfCloseAddress AF %lx:\n", addressFile); + } + + addressFile->CloseIrp = Irp; + + // + // We assume that addressFile has already been verified + // at this point. + // + + address = addressFile->Address; + ASSERT (address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&addressFile->Provider->AddressResource, TRUE); + IoRemoveShareAccess (addressFile->FileObject, &address->u.ShareAccess); + ExReleaseResource (&addressFile->Provider->AddressResource); + + + NbfStopAddressFile (addressFile, address); + NbfDereferenceAddressFile (addressFile); + + // + // This removes a reference added by our caller. + // + + NbfDereferenceAddress ("IRP_MJ_CLOSE", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* NbfCloseAddress */ + + +NTSTATUS +NbfSendDatagramsOnAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine attempts to acquire a hold on the SendDatagramQueue of + the specified address, prepare the next datagram for shipment, and + call NbfSendUIMdlFrame to actually do the work. When NbfSendUIMdlFrame + is finished, it will cause an I/O completion routine in UFRAMES.C to + be called, at which time this routine is called again to handle the + next datagram in the pipeline. + + NOTE: This routine must be called at a point where the address + has another reference that will keep it around. + +Arguments: + + Address - a pointer to the address object to send the datagram on. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PIRP Irp; + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + PUCHAR LocalName; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfSendDatagramsOnAddress %lx:\n", Address); + } + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (!(Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS)) { + + // + // If the queue is empty, don't do anything. + // + + if (IsListEmpty (&Address->SendDatagramQueue)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + // + // Mark the address's send datagram queue as held so that the + // MDL and NBF header will not be used for two requests at the + // same time. + // + + Address->Flags |= ADDRESS_FLAGS_SEND_IN_PROGRESS; + + // + // We own the hold, and we've released the spinlock. So pick off the + // next datagram to be sent, and ship it. + // + + p = Address->SendDatagramQueue.Flink; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + // + // If there is no remote Address specified (the Address specified has + // length 0), this is a broadcast datagram. If anything is specified, it + // will be used as a netbios address. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + remoteAddress = NbfParseTdiAddress ( + ((PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters))-> + SendDatagramInformation->RemoteAddress, + TRUE); + ASSERT (remoteAddress != NULL); + + // + // Build the MAC header. DATAGRAM frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Address->UIFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS) + + Irp->IoStatus.Information, + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&(Address->UIFrame->Header[HeaderLength])); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the correct Netbios header. + // + + if (Address->NetworkName != NULL) { + LocalName = Address->NetworkName->NetbiosName; + } else { + LocalName = DeviceContext->ReservedNetBIOSAddress; + } + + if (remoteAddress == (PVOID)-1) { + + ConstructDatagramBroadcast ( + (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), + LocalName); + + } else { + + ConstructDatagram ( + (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), + (PNAME)remoteAddress->NetbiosName, + LocalName); + + } + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Update our statistics for this datagram. + // + + ++DeviceContext->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DatagramBytesSent, + Irp->IoStatus.Information); + + + // + // Munge the packet length, append the data, and send it. + // + + NbfSetNdisPacketLength(Address->UIFrame->NdisPacket, HeaderLength); + + if (Irp->MdlAddress) { + NdisChainBufferAtBack (Address->UIFrame->NdisPacket, (PNDIS_BUFFER)Irp->MdlAddress); + } + + NbfSendUIMdlFrame ( + Address); + + + // + // The hold will be released in the I/O completion handler in + // UFRAMES.C. At that time, if there is another outstanding datagram + // to send, it will reset the hold and call this routine again. + // + + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + return STATUS_SUCCESS; +} /* NbfSendDatagramsOnAddress */ diff --git a/private/ntos/tdi/nbf/autodial.c b/private/ntos/tdi/nbf/autodial.c new file mode 100644 index 000000000..85bea057e --- /dev/null +++ b/private/ntos/tdi/nbf/autodial.c @@ -0,0 +1,502 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + This module contains code that interacts with the + automatic connection driver (rasacd.sys): + + o NbfNoteNewConnection + o NbfAcdBind + o NbfAcdUnbind + +Author: + + Anthony Discolo (adiscolo) 6 September 1995 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include +#include + +// +// Global variables. +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbf '; + + + +BOOLEAN +NbfCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + if (nArgs != 1) + return FALSE; + + return (pArgs[0] == pArg); +} // NbfCancelAutoDialRequest + + + +VOID +NbfRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION pConnection = pArgs[0]; + KIRQL irql; + BOOL fStopping; + + // + // Check for a destroyed connection. + // + if (pConnection == NULL) + return; + status = NbfVerifyConnectionObject(pConnection); + if (status != STATUS_SUCCESS) { + DbgPrint( + "NbfRetryTdiConnect: NbfVerifyConnectionObject failed (status=0x%x)\n", + status); + return; + } +#ifdef notdef // DBG + DbgPrint( + "NbfRetryTdiConnect: fSuccess=%d, pConnection=0x%x, STOPPING=%d\n", + fSuccess, + pConnection, + pConnection->Flags2 & CONNECTION_FLAGS2_STOPPING); +#endif + KeRaiseIrql(DISPATCH_LEVEL, &irql); + // + // Check to see if the connection + // is closing. + // + ACQUIRE_DPC_SPIN_LOCK(&pConnection->SpinLock); + fStopping = (pConnection->Flags2 & CONNECTION_FLAGS2_STOPPING); + // + // Clear the automatic connection + // in-progress flag, and set the + // autoconnected flag. + // + pConnection->Flags2 &= ~CONNECTION_FLAGS2_AUTOCONNECTING; + pConnection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECTED; + RELEASE_DPC_SPIN_LOCK(&pConnection->SpinLock); + if (!fStopping) { + if (fSuccess) { + // + // Restart the name query. + // + pConnection->Retries = + (USHORT)pConnection->Provider->NameQueryRetries; + NbfSendNameQuery ( + pConnection, + TRUE); + NbfStartConnectionTimer ( + pConnection, + ConnectionEstablishmentTimeout, + pConnection->Provider->NameQueryTimeout); + } + else { + // + // Terminate the connection with an error. + // + NbfStopConnection(pConnection, STATUS_BAD_NETWORK_PATH); + } + } + KeLowerIrql(irql); + NbfDereferenceConnection ("NbfRetryTdiConnect", pConnection, CREF_BY_ID); +} /* NbfRetryTdiConnect */ + + + +BOOLEAN +NbfCancelTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDeviceObject: a pointer to the device object for this driver + + pIrp: a pointer to the irp to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTP_CONNECTION pConnection; + ACD_ADDR addr; + + UNREFERENCED_PARAMETER(pDeviceObject); + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnection = pIrpSp->FileObject->FsContext; + if (pConnection == NULL) + return FALSE; +#ifdef notdef // DBG + DbgPrint( + "NbfCancelTdiConnect: pIrp=0x%x, pConnection=0x%x\n", + pIrp, + pConnection); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbfCancelAutoDialRequest, + pConnection); +} // NbfCancelTdiConnect + + + +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pConnection - a pointer to the TP_CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pArg - the single parameter to the callback procedure + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[1]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic + // connection on this connection, then + // don't try again. + // + if (pConnection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); +#ifdef notdef // DBG + DbgPrint("NbfAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbfRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pArg; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 1, + pArgs); + if (bSuccess) { + // + // Set the CONNECTION_FLAGS2_AUTOCONNECTING flag on + // the connection. This will prevent it from being + // aborted during the automatic connection process. + // + pConnection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECTING; + } + + return bSuccess; +} // NbfAttemptAutoDial + + + +VOID +NbfNoteNewConnection( + PTP_CONNECTION pConnection, + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + Inform the automatic connection driver of a successful + new connection. + +Arguments: + Connection - a pointer to a connection object + + DeviceContext - a pointer to the device context + +Return Value: + None. + +--*/ + +{ + KIRQL irql; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG ulcChars; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); +#ifdef notdef // DBG + DbgPrint("NbfNoteNewConnection: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Eliminate the "/Device/Nbf_" prefix when + // copying the adapter name. + // + adapter.fType = ACD_ADAPTER_NAME; + ulcChars = DeviceContext->DeviceNameLength - 12; + if (ulcChars >= ACD_ADAPTER_NAME_LEN) + ulcChars = ACD_ADAPTER_NAME_LEN - 1; + RtlCopyMemory( + adapter.szName, + &DeviceContext->DeviceName[12], + ulcChars * sizeof (WCHAR)); + adapter.szName[ulcChars] = L'\0'; + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbfNoteNewConnection + + + +VOID +NbfAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbfAcdBind + + + +VOID +NbfAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbfAcdUnbind + +#endif // RASAUTODIAL + diff --git a/private/ntos/tdi/nbf/connect.c b/private/ntos/tdi/nbf/connect.c new file mode 100644 index 000000000..af2aec64b --- /dev/null +++ b/private/ntos/tdi/nbf/connect.c @@ -0,0 +1,1700 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAccept + o TdiListen + o TdiConnect + o TdiDisconnect + o TdiAssociateAddress + o TdiDisassociateAddress + o OpenConnection + o CloseConnection + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef notdef // RASAUTODIAL +#include +#include +#endif // RASAUTODIAL + +#ifdef notdef // RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported functions. +// +VOID +NbfRetryPreTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); + +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION Connection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ); + +VOID +NbfCancelPreTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); +#endif // RASAUTODIAL + +NTSTATUS +NbfTdiConnectCommon( + IN PIRP Irp + ); + + + +NTSTATUS +NbfTdiAccept( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiAccept request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + KIRQL oldirql; + NTSTATUS status; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiAccept: Entered.\n"); + } + + // + // Get the connection this is associated with; if there is none, get out. + // This adds a connection reference of type BY_ID if successful. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // This adds a connection reference of type BY_ID if successful. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // just set the connection flags to allow reads and writes to proceed. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + // + // Turn off the stopping flag for this connection. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + + if (connection->AddressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + if ((connection->Flags2 & CONNECTION_FLAGS2_WAITING_SC) != 0) { + + // + // We previously completed a listen, now the user is + // coming back with an accept, Set this flag to allow + // the connection to proceed. + // + // BUGBUG: If the connection has gone down in the + // meantime, we have just reenabled it. + // + + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + connection->Flags |= CONNECTION_FLAGS_READY; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + INCREMENT_COUNTER (connection->Provider, OpenConnections); + + // + // Set this flag to enable disconnect indications; once + // the client has accepted he expects those. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfSendSessionConfirm (connection); + + } else { + + // + // This accept is being called at some point before + // the link is up; directly from the connection handler + // or at some point slightly later. We don't set + // FLAGS2_REQ_COMPLETED now because the reference + // count is not high enough; we set it when we get + // the session initialize. + // + // BUGBUG: If the connection goes down in the meantime, + // we won't indicate the disconnect. + // + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + } + + NbfDereferenceConnection ("Temp TdiAccept", connection, CREF_BY_ID); + + KeLowerIrql (oldirql); + + return STATUS_SUCCESS; + +} /* NbfTdiAccept */ + + +NTSTATUS +NbfTdiAssociateAddress( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and the address for + the user. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS oldAddress; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; + PDEVICE_CONTEXT deviceContext; + + KIRQL oldirql, oldirql2; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiAssociateAddress: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + connection = irpSp->FileObject->FsContext; + status = NbfVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + // + // Make sure this connection is ready to be associated. + // + + oldAddress = (PTP_ADDRESS)NULL; + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + + // + // The connection is already associated with + // an active connection...bad! + // + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + + return STATUS_INVALID_CONNECTION; + + } else { + + // + // See if there is an old association hanging around... + // this happens if the connection has been disassociated, + // but not closed. + // + + if (connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiAssociateAddress: removing association.\n"); + } + + // + // Save this; since it is non-null this address + // will be dereferenced after the connection + // spinlock is released. + // + + oldAddress = connection->AddressFile->Address; + + // + // Remove the old association. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RemoveEntryList (&connection->AddressList); + RemoveEntryList (&connection->AddressFileList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + } + + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 1 in NbfTdiAssociateAddress\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + return GetExceptionCode(); + } + + + // + // If we removed an old association, dereference the + // address. + // + + if (oldAddress != (PTP_ADDRESS)NULL) { + + NbfDereferenceAddress("Removed old association", oldAddress, AREF_CONNECTION); + + } + + + deviceContext = connection->Provider; + + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&irpSp->Parameters; + + // + // get a pointer to the address File Object, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + status = ObReferenceObjectByHandle ( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *) &fileObject, + NULL); + + if (NT_SUCCESS(status)) { + + // + // we might have one of our address objects; verify that. + // + + addressFile = fileObject->FsContext; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint3 ("NbfTdiAssociateAddress: Connection %lx Irp %lx AddressFile %lx \n", + connection, Irp, addressFile); + } + + if (NT_SUCCESS (NbfVerifyAddressObject (addressFile))) { + + // + // have an address and connection object. Add the connection to the + // address object database. Also add the connection to the address + // file object db (used primarily for cleaning up). Reference the + // address to account for one more reason for it staying open. + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + if ((addressFile->Address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint2 ("NbfTdiAssociateAddress: Valid Address %lx %lx\n", + addressFile->Address, addressFile); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + NbfReferenceAddress ( + "Connection associated", + addressFile->Address, + AREF_CONNECTION); + +#if DBG + if (!(IsListEmpty(&connection->AddressList))) { + DbgPrint ("NBF: C %lx, new A %lx, in use\n", + connection, addressFile->Address); + DbgBreakPoint(); + } +#endif + InsertTailList ( + &addressFile->Address->ConnectionDatabase, + &connection->AddressList); + +#if DBG + if (!(IsListEmpty(&connection->AddressFileList))) { + DbgPrint ("NBF: C %lx, new AF %lx, in use\n", + connection, addressFile); + DbgBreakPoint(); + } +#endif + InsertTailList ( + &addressFile->ConnectionDatabase, + &connection->AddressFileList); + + connection->AddressFile = addressFile; + connection->Flags2 |= CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 &= ~CONNECTION_FLAGS2_DISASSOCIATED; + + if (addressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + status = STATUS_SUCCESS; + + } else { + + // + // The connection is closing, stop the + // association. + // + + status = STATUS_INVALID_CONNECTION; + + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 2 in NbfTdiAssociateAddress\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + status = GetExceptionCode(); + } + + } else { + + status = STATUS_INVALID_HANDLE; //BUGBUG: should this be more informative? + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + NbfDereferenceAddress ("Temp associate", addressFile->Address, AREF_VERIFY); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + // + // Note that we don't keep a reference to this file object around. + // That's because the IO subsystem manages the object for us; we simply + // want to keep the association. We only use this association when the + // IO subsystem has asked us to close one of the file object, and then + // we simply remove the association. + // + + ObDereferenceObject (fileObject); + + } else { + status = STATUS_INVALID_HANDLE; + } + + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + + return status; + +} /* TdiAssociateAddress */ + + +NTSTATUS +NbfTdiDisassociateAddress( + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine performs the disassociation of the connection and the address + for the user. If the connection has not been stopped, it will be stopped + here. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTP_CONNECTION connection; + NTSTATUS status; +// PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiDisassociateAddress: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // and now we disassociate the address. This only removes + // the appropriate reference for the connection, the + // actually disassociation will be done later. + // + // The DISASSOCIATED flag is used to make sure that + // only one person removes this reference. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Temp use in Associate", connection, CREF_BY_ID); + + return STATUS_SUCCESS; + +} /* TdiDisassociateAddress */ + + + +NTSTATUS +NbfTdiConnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiConnect: Entered.\n"); + } + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // Check that the remote is a Netbios address. + // + + if (!NbfValidateTdiAddress( + parameters->RequestConnectionInformation->RemoteAddress, + parameters->RequestConnectionInformation->RemoteAddressLength)) { + + NbfDereferenceConnection ("Invalid Address", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress((PTRANSPORT_ADDRESS)(parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteAddress == NULL) { + + NbfDereferenceConnection ("Not Netbios", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + + } + + // + // copy the called address someplace we can use it. + // + + connection->CalledAddress.NetbiosNameType = + RemoteAddress->NetbiosNameType; + + RtlCopyMemory( + connection->CalledAddress.NetbiosName, + RemoteAddress->NetbiosName, + 16); + +#ifdef notdef // RASAUTODIAL + if (fAcdLoadedG) { + KIRQL adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbfAttemptAutoDial( + connection, + ACD_NOTIFICATION_PRECONNECT, + NbfRetryPreTdiConnect, + Irp)) + { + ACQUIRE_SPIN_LOCK(&connection->SpinLock, &oldirql); + connection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECT; + connection->Status = STATUS_PENDING; + RELEASE_SPIN_LOCK(&connection->SpinLock, oldirql); + NbfDereferenceConnection ("Automatic connection", connection, CREF_BY_ID); + // + // Set a special cancel routine on the irp + // in case we get cancelled during the + // automatic connection. + // + IoSetCancelRoutine(Irp, NbfCancelPreTdiConnect); + return STATUS_PENDING; + } + } +#endif // RASAUTODIAL + + return NbfTdiConnectCommon(Irp); +} // NbfTdiConnect + + + +NTSTATUS +NbfTdiConnectCommon( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + Note: the caller needs to call NbfVerifyConnectionObject(pConnection) + before calling this routine. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG NameQueryTimeout; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiConnectCommon: Entered.\n"); + } + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no connect request should take more + // than 15 seconds if there is someone out there. We'll assume that's + // what the user wanted if they specify -1 as the timer length. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("TdiConnect: Modifying user timeout to %lx seconds.\n", + TDI_TIMEOUT_CONNECT); + } + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_CONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { // couldn't make the request. + NbfDereferenceConnection ("Throw away", connection, CREF_BY_ID); + return status; // return with failure. + } else { + + // Reference the connection since NbfDestroyRequest derefs it. + + NbfReferenceConnection("For connect request", connection, CREF_REQUEST); + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + tpRequest->Owner = ConnectionType; + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + KeLowerIrql (oldirql); + NbfDereferenceConnection("Temporary Use 1", connection, CREF_BY_ID); + return STATUS_PENDING; + } else { + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + + // + // Initialize this now, we cut these down on an async medium + // that is disconnected. + // + + connection->Retries = (USHORT)connection->Provider->NameQueryRetries; + NameQueryTimeout = connection->Provider->NameQueryTimeout; + + if (connection->Provider->MacInfo.MediumAsync) { + + // + // If we are on an async medium, then we need to send out + // a committed NAME_QUERY frame right from the start, since + // the FIND_NAME frames are not forwarded by the gateway. + // + + connection->Flags2 |= (CONNECTION_FLAGS2_CONNECTOR | // we're the initiator. + CONNECTION_FLAGS2_WAIT_NR); // wait for NAME_RECOGNIZED. + + // + // Because we may call NbfTdiConnect twice + // via an automatic connection, check to see + // if an LSN has already been assigned. + // + if (!(connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN)) { + connection->Flags2 |= CONNECTION_FLAGS2_GROUP_LSN; + + if (NbfAssignGroupLsn(connection) != STATUS_SUCCESS) { + + // + // Could not find an empty LSN; have to fail. + // + RemoveEntryList(&tpRequest->Linkage); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + KeLowerIrql (oldirql); + NbfDereferenceConnection("Temporary Use 1", connection, CREF_BY_ID); + return STATUS_PENDING; + + } + } + + if (!connection->Provider->MediumSpeedAccurate) { + + // + // The link is not up, so we cut our timeouts down. + // We still send one frame so that loopback works. + // + + connection->Retries = 1; + NameQueryTimeout = NAME_QUERY_TIMEOUT / 10; + + } + + } else { + + // + // Normal connection, we send out a FIND_NAME first. + // + + connection->Flags2 |= (CONNECTION_FLAGS2_CONNECTOR | // we're the initiator. + CONNECTION_FLAGS2_WAIT_NR_FN); // wait for NAME_RECOGNIZED. + + } + + connection->Flags2 &= ~(CONNECTION_FLAGS2_STOPPING | + CONNECTION_FLAGS2_INDICATING); + connection->Status = STATUS_PENDING; + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + NbfCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + KeLowerIrql (oldirql); + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + IoSetCancelRoutine(Irp, NbfCancelConnection); + IoReleaseCancelSpinLock(cancelirql); + + } + } + + + // + // On "easily disconnected" networks, quick reregister + // (one ADD_NAME_QUERY) the address if NEED_REREGISTER + // is set (happens when we get a five-second period + // with no multicast activity). If we are currently + // quick reregistering, wait for it to complete. + // + + if (connection->Provider->EasilyDisconnected) { + + PTP_ADDRESS Address; + LARGE_INTEGER Timeout; + + // + // If the address needs (or is) reregistering, then do wait, + // setting a flag so the connect will be resumed when the + // reregistration times out. + // + + Address = connection->AddressFile->Address; + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + if ((Address->Flags & + (ADDRESS_FLAGS_NEED_REREGISTER | ADDRESS_FLAGS_QUICK_REREGISTER)) != 0) { + + connection->Flags2 |= CONNECTION_FLAGS2_W_ADDRESS; + + if (Address->Flags & ADDRESS_FLAGS_NEED_REREGISTER) { + + Address->Flags &= ~ADDRESS_FLAGS_NEED_REREGISTER; + Address->Flags |= ADDRESS_FLAGS_QUICK_REREGISTER; + + NbfReferenceAddress ("start registration", Address, AREF_TIMER); + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now start reregistration process by starting up a retransmission timer + // and begin sending ADD_NAME_QUERY NetBIOS frames. + // + + Address->Retries = 1; + Timeout.LowPart = (ULONG)(-(LONG)Address->Provider->AddNameQueryTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Address->Timer, *(PTIME)&Timeout, &Address->Dpc); + + (VOID)NbfSendAddNameQuery (Address); // send first ADD_NAME_QUERY. + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + } + KeLowerIrql (oldirql); + + NbfDereferenceConnection("Temporary Use 4", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + } + + } + + // + // Send the NAME_QUERY frame as a FIND.NAME to get a NAME_RECOGNIZED + // frame back. The first time we send this frame, we are just looking + // for the data link information to decide whether we already have + // a link with the remote NetBIOS name. If we do, we can reuse it. + // If we don't, then we make one first, and then use it. A consequence + // of this is that we really engage in an extra non-committal NQ/NR + // exchange up front to locate the remote guy, and then commit to an actual + // LSN with a second NQ/NR sequence to establish the transport connection + // + + NbfSendNameQuery ( + connection, + TRUE); + + // + // Start the connection timer (do this at the end, so that + // if we get delayed in this routine the connection won't + // get into an unexpected state). + // + + NbfStartConnectionTimer ( + connection, + ConnectionEstablishmentTimeout, + NameQueryTimeout); + + KeLowerIrql (oldirql); + + NbfDereferenceConnection("Temporary Use 3", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. + +} /* TdiConnect */ + + + +NTSTATUS +NbfTdiDisconnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiDisconnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + LARGE_INTEGER timeout; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + KIRQL oldirql; + NTSTATUS status; + + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiDisconnect: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName; + STRING localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TdiDisconnect entered for connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + + // + // if the connection is currently stopping, there's no reason to blow + // it away... + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { +#if 0 + NbfPrint2 ("TdiDisconnect: ignoring disconnect %lx, connection stopping (%lx)\n", + connection, connection->Status); +#endif + + // + // In case a connect indication is in progress. + // + + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + + // + // If possible, queue the disconnect. This flag is cleared + // when the indication is about to be done. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) && + (connection->Flags2 & CONNECTION_FLAGS2_LDISC) == 0) { +#if DBG + DbgPrint ("NBF: Queueing disconnect irp %lx\n", Irp); +#endif + connection->Flags2 |= CONNECTION_FLAGS2_LDISC; + status = STATUS_SUCCESS; + } else { + status = connection->Status; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + KeLowerIrql (oldirql); + NbfDereferenceConnection ("Ignoring disconnect", connection, CREF_BY_ID); // release our lookup reference. + return status; + + } + + connection->Flags2 &= ~ (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_PRE_ACCEPT | + CONNECTION_FLAGS2_WAITING_SC); + + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + connection->Flags2 |= CONNECTION_FLAGS2_LDISC; + + // + // Set this flag so the disconnect IRP is completed. + // + // BUGBUG: If the connection goes down before we can + // call NbfStopConnection with STATUS_LOCAL_DISCONNECT, + // the disconnect IRP won't get completed. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + +// connection->DisconnectIrp = Irp; + + // + // fix up the timeout if required; no disconnect request should take very + // long. However, the user can cause the timeout to not happen if they + // desire that. + // + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no disconnect request should take more + // than 15 seconds. We'll assume that's what the user wanted if they + // specify -1 as the timer. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("TdiDisconnect: Modifying user timeout to %lx seconds.\n", + TDI_TIMEOUT_CONNECT); + } + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_DISCONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Now the reason for the disconnect + // + + if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_RELEASE) { + connection->Flags2 |= CONNECTION_FLAGS2_DESTROY; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_ABORT) { + connection->Flags2 |= CONNECTION_FLAGS2_ABORT; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_WAIT) { + connection->Flags2 |= CONNECTION_FLAGS2_ORDREL; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("TdiDisconnect calling NbfStopConnection %lx\n", connection); + } + + // + // This will get passed to IoCompleteRequest during TdiDestroyConnection + // + + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); // starts the abort sequence. + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Disconnecting", connection, CREF_BY_ID); // release our lookup reference. + + // + // This request will be completed by TdiDestroyConnection once + // the connection reference count drops to 0. + // + + return STATUS_SUCCESS; +} /* TdiDisconnect */ + + +NTSTATUS +NbfTdiListen( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiListen request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_LISTEN parameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + PVOID RequestBuffer2; + ULONG RequestBuffer2Length; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiListen: Entered.\n"); + } + + // + // validate this connection + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL_LISTEN)&irpSp->Parameters; + + // + // Record the remote address if there is one. + // + + ListenInformation = parameters->RequestConnectionInformation; + + if ((ListenInformation != NULL) && + (ListenInformation->RemoteAddress != NULL)) { + + if ((NbfValidateTdiAddress( + ListenInformation->RemoteAddress, + ListenInformation->RemoteAddressLength)) && + ((ListenAddress = NbfParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) != NULL)) { + + RequestBuffer2 = (PVOID)ListenAddress->NetbiosName, + RequestBuffer2Length = NETBIOS_NAME_LENGTH; + + } else { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiListen: Create Request Failed, bad address.\n"); + } + + NbfDereferenceConnection ("Bad address", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + RequestBuffer2 = NULL; + RequestBuffer2Length = 0; + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + RequestBuffer2, + RequestBuffer2Length, + timeout, // timeout value (can be 0). + &tpRequest); + + + if (!NT_SUCCESS (status)) { // couldn't make the request. + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint1 ("TdiListen: Create Request Failed, reason: %lx.\n", status); + } + + NbfDereferenceConnection ("For create", connection, CREF_BY_ID); + return status; // return with failure. + } + + // Reference the connection since NbfDestroyRequest derefs it. + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql); + tpRequest->Owner = ConnectionType; + + NbfReferenceConnection("For listen request", connection, CREF_REQUEST); + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + NbfDereferenceConnection("Temp create", connection, CREF_BY_ID); + return STATUS_PENDING; + + } else { + + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + connection->Flags2 |= (CONNECTION_FLAGS2_LISTENER | // we're the passive one. + CONNECTION_FLAGS2_WAIT_NQ); // wait for NAME_QUERY. + connection->Flags2 &= ~(CONNECTION_FLAGS2_INDICATING | + CONNECTION_FLAGS2_STOPPING); + connection->Status = STATUS_PENDING; + + // + // If TDI_QUERY_ACCEPT is not set, then we set PRE_ACCEPT to + // indicate that when the listen completes we do not have to + // wait for a TDI_ACCEPT to continue. + // + + if ((parameters->RequestFlags & TDI_QUERY_ACCEPT) == 0) { + connection->Flags2 |= CONNECTION_FLAGS2_PRE_ACCEPT; + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + NbfCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + IoSetCancelRoutine(Irp, NbfCancelConnection); + IoReleaseCancelSpinLock(cancelirql); + + } + + // + // Wait for an incoming NAME_QUERY frame. The remainder of the + // connectionless protocol to set up a connection is processed + // in the NAME_QUERY frame handler. + // + + NbfDereferenceConnection("Temp create", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. +} /* TdiListen */ + + +NTSTATUS +NbfOpenConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_CONNECTION connection; + PFILE_FULL_EA_INFORMATION ea; + + UNREFERENCED_PARAMETER (Irp); + + // + // BUGBUG: check ea to make sure it's correct + // + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfOpenConnection: Entered.\n"); + } + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + status = NbfCreateConnection (DeviceContext, &connection); + if (!NT_SUCCESS (status)) { + return status; // sorry, we couldn't make one. + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + IrpSp->FileObject->FsContext = (PVOID)connection; + IrpSp->FileObject->FsContext2 = (PVOID)TDI_CONNECTION_FILE; + connection->FileObject = IrpSp->FileObject; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint1 ("NBFOpenConnection: Opened Connection %lx.\n", + connection); + } + + return status; + +} /* NbfOpenConnection */ + +#if DBG +VOID +ConnectionCloseTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + Connection = (PTP_CONNECTION)DeferredContext; + + DbgPrint ("NBF: Close of connection %lxpending for 2 minutes\n", Connection); + DbgBreakPoint(); + +} +#endif + + +NTSTATUS +NbfCloseConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. There may be actions in + progress on this connection, so we note the closing IRP, mark the + connection as closing, and complete it somewhere down the road (when all + references have been removed). + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_CONNECTION connection; + + UNREFERENCED_PARAMETER (DeviceObject); + UNREFERENCED_PARAMETER (Irp); + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfCloseConnection: Entered.\n"); + } + + // + // is the file object a connection? + // + + connection = IrpSp->FileObject->FsContext; + + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // We duplicate the code from VerifyConnectionObject, + // although we don't actually call that since it does + // a reference, which we don't want (to avoid bouncing + // the reference count up from 0 if this is a dead + // link). + // + + try { + + if ((connection->Size == sizeof (TP_CONNECTION)) && + (connection->Type == NBF_CONNECTION_SIGNATURE)) { + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + status = STATUS_SUCCESS; + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + KeLowerIrql (oldirql); + return GetExceptionCode(); + } + + if (!NT_SUCCESS (status)) { + KeLowerIrql (oldirql); + return status; + } + + // + // We recognize it; is it closing already? + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) != 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + KeLowerIrql (oldirql); +#if DBG + NbfPrint1("Closing already-closing connection %lx\n", connection); +#endif + return STATUS_INVALID_CONNECTION; + } + + connection->Flags2 |= CONNECTION_FLAGS2_CLOSING; + + // + // if there is activity on the connection, tear it down. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // If the connection is still associated, disassociate it. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // Save this to complete the IRP later. + // + + connection->CloseIrp = Irp; + +#if 0 + // + // make it impossible to use this connection from the file object + // + + IrpSp->FileObject->FsContext = NULL; + IrpSp->FileObject->FsContext2 = NULL; + connection->FileObject = NULL; +#endif + +#if DBG + { + LARGE_INTEGER Timeout; + BOOLEAN AlreadyInserted; + + Timeout.LowPart = (ULONG)(-(120*SECONDS)); + Timeout.HighPart = -1; + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + AlreadyInserted = KeCancelTimer (&connection->Timer); + + KeInitializeDpc ( + &connection->Dpc, + ConnectionCloseTimeout, + (PVOID)connection); + + KeSetTimer ( + &connection->Timer, + Timeout, + &connection->Dpc); + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if (AlreadyInserted) { + DbgPrint ("NBF: Cancelling connection timer for debug %lx\n", connection); + NbfDereferenceConnection ("debug", connection, CREF_TIMER); + } + + } +#endif + + KeLowerIrql (oldirql); + + // + // dereference for the creation. Note that this dereference + // here won't have any effect until the regular reference count + // hits zero. + // + + NbfDereferenceConnectionSpecial (" Closing Connection", connection, CREF_SPECIAL_CREATION); + + return STATUS_PENDING; + +} /* NbfCloseConnection */ + + diff --git a/private/ntos/tdi/nbf/connobj.c b/private/ntos/tdi/nbf/connobj.c new file mode 100644 index 000000000..1e7e9221b --- /dev/null +++ b/private/ntos/tdi/nbf/connobj.c @@ -0,0 +1,2413 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + connobj.c + +Abstract: + + This module contains code which implements the TP_CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + + A word about connection reference counts: + + With TDI version 2, connections live on even after they have been stopped. + This necessitated changing the way NBF handles connection reference counts, + making the stopping of a connection only another way station in the life + of a connection, rather than its demise. Reference counts now work like + this: + + Connection State Reference Count Flags + ------------------ ----------------- -------- + Opened, no activity 1 0 + Open, Associated 2 FLAGS2_ASSOCIATED + Open, Assoc., Connected 3 FLAGS_READY + Above + activity >3 varies + Open, Assoc., Stopping >3 FLAGS_STOPPING + Open, Assoc., Stopped 3 FLAGS_STOPPING + Open, Disassoc. Complete 2 FLAGS_STOPPING + FLAGS2_ASSOCIATED == 0 + Closing 1 FLAGS2_CLOSING + Closed 0 FLAGS2_CLOSING + + Note that keeping the stopping flag set when the connection has fully + stopped avoids using the connection until it is connected again; the + CLOSING flag serves the same purpose. This allows a connection to run + down in its own time. + + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include +#include +#endif // RASAUTODIAL + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported routines +// +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION Connection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ); + +VOID +NbfRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); + +BOOLEAN +NbfCancelTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); +#endif // RASAUTODIAL + + + +VOID +ConnectionEstablishmentTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the NAME_QUERY/NAME_RECOGNIZED protocol expires. The retry + count in the Connection object is decremented, and if it reaches 0, + the connection is aborted. If the retry count has not reached zero, + then the NAME QUERY is retried. The following cases are covered by + this routine: + + 1. Initial NAME_QUERY timeout for find_name portion of connection setup. + + NQ(find_name) -------------------> + [TIMEOUT] + NQ(find_name) -------------------> + <------------------- NR(find_name) + + 2. Secondary NAME_QUERY timeout for connection setup. + + NQ(connection) -------------------> + [TIMEOUT] + NQ(connection) -------------------> + <------------------- NR(connection) + + There is another case where the data link connection does not get + established within a reasonable amount of time. This is handled by + the link layer routines. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_CONNECTION block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Connection = (PTP_CONNECTION)DeferredContext; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("ConnectionEstablishmentTimeout: Entered for connection %lx.\n", + Connection); + } + + // + // If this connection is being run down, then we can't do anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfDereferenceConnection ("Connect timed out", Connection, CREF_TIMER); + LEAVE_NBF; + return; + } + + + if (Connection->Flags2 & (CONNECTION_FLAGS2_WAIT_NR_FN | CONNECTION_FLAGS2_WAIT_NR)) { + + // + // We are waiting for a commital or non-commital NAME_RECOGNIZED frame. + // Decrement the retry count, and possibly resend the NAME_QUERY. + // + + if (--(Connection->Retries) == 0) { // if retry count exhausted. + + NTSTATUS StopStatus; + + // + // See if we got a no listens response, or just + // nothing. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_NO_LISTEN) != 0) { + + Connection->Flags2 &= ~CONNECTION_FLAGS2_NO_LISTEN; + StopStatus = STATUS_REMOTE_NOT_LISTENING; // no listens + + } else { + + StopStatus = STATUS_BAD_NETWORK_PATH; // name not found. + + } + +#ifdef RASAUTODIAL + // + // If this is a connect operation that has + // returned with STATUS_BAD_NETWORK_PATH, then + // attempt to create an automatic connection. + // + if (fAcdLoadedG && + StopStatus == STATUS_BAD_NETWORK_PATH) + { + KIRQL adirql; + BOOLEAN fEnabled; + + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbfAttemptAutoDial( + Connection, + 0, + NbfRetryTdiConnect, + Connection)) + { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + goto done; + } + } +#endif // RASAUTODIAL + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStopConnection (Connection, StopStatus); + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // We make source routing optional on every second + // name query (whenever Retries is even). + // + + NbfSendNameQuery ( + Connection, + (BOOLEAN)((Connection->Retries & 1) ? FALSE : TRUE)); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + Connection->Provider->NameQueryTimeout); + + } + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + + // + // Dereference the connection, to account for the fact that the + // timer went off. Note that if we restarted the timer using + // NbfStartConnectionTimer, the reference count has already been + // incremented to account for the new timer. + // + +done: + NbfDereferenceConnection ("Timer timed out",Connection, CREF_TIMER); + + LEAVE_NBF; + return; + +} /* ConnectionEstablishmentTimeout */ + + +VOID +NbfAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport connection. Some + minimal initialization is done. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. Returns + NULL if the storage cannot be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_CONNECTION Connection; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_CONNECTION)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate connection: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 103, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + *TransportConnection = NULL; + return; + } + + Connection = (PTP_CONNECTION)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_CONNECTION), + 'cFBN'); + if (Connection == NULL) { + PANIC("NBF: Could not allocate connection: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 203, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + *TransportConnection = NULL; + return; + } + RtlZeroMemory (Connection, sizeof(TP_CONNECTION)); + + DeviceContext->MemoryUsage += sizeof(TP_CONNECTION); + ++DeviceContext->ConnectionAllocated; + + Connection->Type = NBF_CONNECTION_SIGNATURE; + Connection->Size = sizeof (TP_CONNECTION); + + Connection->Provider = DeviceContext; + Connection->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Connection->SpinLock); + KeInitializeDpc ( + &Connection->Dpc, + ConnectionEstablishmentTimeout, + (PVOID)Connection); + KeInitializeTimer (&Connection->Timer); + + + InitializeListHead (&Connection->LinkList); + InitializeListHead (&Connection->AddressFileList); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->PacketWaitLinkage); + InitializeListHead (&Connection->PacketizeLinkage); + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + InitializeListHead (&Connection->DeferredQueue); + + NbfAddSendPacket (DeviceContext); + NbfAddSendPacket (DeviceContext); + NbfAddUIFrame (DeviceContext); + + *TransportConnection = Connection; + +} /* NbfAllocateConnection */ + + +VOID +NbfDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport connection. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a transport connection structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportConnection); + --DeviceContext->ConnectionAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_CONNECTION); + + NbfRemoveSendPacket (DeviceContext); + NbfRemoveSendPacket (DeviceContext); + NbfRemoveUIFrame (DeviceContext); + +} /* NbfDeallocateConnection */ + + +NTSTATUS +NbfCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine creates a transport connection. The reference count in the + connection is automatically set to 1, and the reference count in the + DeviceContext is incremented. + +Arguments: + + Address - the address for this connection to be associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION Connection; + KIRQL oldirql; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfCreateConnection: Entered.\n"); + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->ConnectionPool); + if (p == &DeviceContext->ConnectionPool) { + + if ((DeviceContext->ConnectionMaxAllocated == 0) || + (DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) { + + NbfAllocateConnection (DeviceContext, &Connection); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated connection at %lx\n", Connection); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 403, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + Connection = NULL; + + } + + if (Connection == NULL) { + ++DeviceContext->ConnectionExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateConnection: Could not allocate connection object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); +#if DBG + InitializeListHead (p); +#endif + + } + + ++DeviceContext->ConnectionInUse; + if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) { + ++DeviceContext->ConnectionMaxInUse; + } + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfCreateConnection: Connection at %lx.\n", Connection); + } + + // + // We have two references; one is for creation, and the + // other is a temporary one so that the connection won't + // go away before the creator has a chance to access it. + // + + Connection->SpecialRefCount = 1; + Connection->ReferenceCount = -1; // this is -1 based + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_CREFS; Counter++) { + Connection->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfCloseConnection + + Connection->RefTypes[CREF_SPECIAL_CREATION] = 1; + } +#endif + + // + // Initialize the request queues & components of this connection. + // + + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->AddressFileList); + Connection->SpecialReceiveIrp = (PIRP)NULL; + Connection->Flags = 0; + Connection->Flags2 = 0; + Connection->DeferredFlags = 0; + Connection->Lsn = 0; + Connection->Rsn = 0; + Connection->Retries = 0; // no retries yet. + Connection->MessageBytesReceived = 0; // no data yet + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->ReceiveBytesUnaccepted = 0; + Connection->CurrentReceiveAckQueueable = FALSE; + Connection->CurrentReceiveSynchronous = FALSE; + Connection->ConsecutiveSends = 0; + Connection->ConsecutiveReceives = 0; + Connection->Link = NULL; // no datalink connection yet. + Connection->LinkSpinLock = NULL; + Connection->Context = NULL; // no context yet. + Connection->Status = STATUS_PENDING; // default NbfStopConnection status. + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->CurrentReceiveIrp = (PIRP)NULL; + Connection->DisconnectIrp = (PIRP)NULL; + Connection->CloseIrp = (PIRP)NULL; + Connection->AddressFile = NULL; + Connection->IndicationInProgress = FALSE; + Connection->OnDataAckQueue = FALSE; + Connection->OnPacketWaitQueue = FALSE; + Connection->TransferBytesPending = 0; + Connection->TotalTransferBytesPending = 0; + + RtlZeroMemory (&Connection->NetbiosHeader, sizeof(NBF_HDR_CONNECTION)); + +#if DBG + Connection->Destroyed = FALSE; + Connection->TotalReferences = 0; + Connection->TotalDereferences = 0; + Connection->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalConnectionList, &Connection->GlobalLinkage, &NbfGlobalInterlock); + StoreConnectionHistory (Connection, TRUE); +#endif + + // + // Now assign this connection an ID. This is used later to identify the + // connection across multiple processes. + // + // The high bit of the ID is not user, it is off for connection + // initiating NAME.QUERY frames and on for ones that are the result + // of a FIND.NAME request. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + Connection->ConnectionId = DeviceContext->UniqueIdentifier; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + NbfReferenceDeviceContext ("Create Connection", DeviceContext, DCREF_CONNECTION); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + *TransportConnection = Connection; // return the connection. + + return STATUS_SUCCESS; +} /* NbfCreateConnection */ + + +NTSTATUS +NbfVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. + +Arguments: + + Connection - potential pointer to a TP_CONNECTION object. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + + // + // try to verify the connection signature. If the signature is valid, + // get the connection spinlock, check its state, and increment the + // reference count if it's ok to use it. Note that being in the stopping + // state is an OK place to be and reference the connection; we can + // disassociate the address while running down. + // + + try { + + if ((Connection != (PTP_CONNECTION)NULL) && + (Connection->Size == sizeof (TP_CONNECTION)) && + (Connection->Type == NBF_CONNECTION_SIGNATURE)) { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + NbfReferenceConnection ("Verify Temp Use", Connection, CREF_BY_ID); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + return status; + +} + + +NTSTATUS +NbfDestroyAssociation( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys the association between a transport connection and + the address it was formerly associated with. The only action taken is + to disassociate the address and remove the connection from all address + queues. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql2; + PTP_ADDRESS_FILE addressFile; + BOOLEAN NotAssociated = FALSE; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfDestroyAssociation: Entered for connection %lx.\n", + TransportConnection); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { +#if DBG + if (!(IsListEmpty(&TransportConnection->AddressList)) || + !(IsListEmpty(&TransportConnection->AddressFileList))) { + DbgPrint ("NBF: C %lx, AF %lx, freed while still queued\n", + TransportConnection, TransportConnection->AddressFile); + DbgBreakPoint(); + } +#endif + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + NotAssociated = TRUE; + } else { + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 1 in NbfDestroyAssociation\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + if (NotAssociated) { + return STATUS_SUCCESS; + } + + addressFile = TransportConnection->AddressFile; + + // + // Delink this connection from its associated address connection + // database. To do this we must spin lock on the address object as + // well as on the connection, + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + RemoveEntryList (&TransportConnection->AddressFileList); + RemoveEntryList (&TransportConnection->AddressList); + + InitializeListHead (&TransportConnection->AddressList); + InitializeListHead (&TransportConnection->AddressFileList); + + // + // remove the association between the address and the connection. + // + + TransportConnection->AddressFile = NULL; + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 2 in NbfDestroyAssociation\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + // + // and remove a reference to the address + // + + NbfDereferenceAddress ("Destroy association", addressFile->Address, AREF_CONNECTION); + + + return STATUS_SUCCESS; + +} /* NbfDestroyAssociation */ + + +NTSTATUS +NbfIndicateDisconnect( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine indicates a remote disconnection on this connection if it + is necessary to do so. No other action is taken here. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS_FILE addressFile; + PDEVICE_CONTEXT DeviceContext; + ULONG DisconnectReason; + PIRP DisconnectIrp; + KIRQL oldirql; + ULONG Lflags2; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfIndicateDisconnect: Entered for connection %lx.\n", + TransportConnection); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + + if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) { + + ASSERT (TransportConnection->Lsn == 0); + + // + // Turn off all but the still-relevant bits in the flags. + // + + Lflags2 = TransportConnection->Flags2; + TransportConnection->Flags2 &= + (CONNECTION_FLAGS2_ASSOCIATED | + CONNECTION_FLAGS2_DISASSOCIATED | + CONNECTION_FLAGS2_CLOSING); + TransportConnection->Flags2 |= CONNECTION_FLAGS2_STOPPING; + + // + // Clean up other stuff -- basically everything gets + // done here except for the flags and the status, since + // they are used to block other requests. When the connection + // is given back to us (in Accept, Connect, or Listen) + // they are cleared. + // + + TransportConnection->NetbiosHeader.TransmitCorrelator = 0; + TransportConnection->Retries = 0; // no retries yet. + TransportConnection->MessageBytesReceived = 0; // no data yet + TransportConnection->MessageBytesAcked = 0; + TransportConnection->MessageInitAccepted = 0; + TransportConnection->ReceiveBytesUnaccepted = 0; + TransportConnection->ConsecutiveSends = 0; + TransportConnection->ConsecutiveReceives = 0; + TransportConnection->SendState = CONNECTION_SENDSTATE_IDLE; + + TransportConnection->TransmittedTsdus = 0; + TransportConnection->ReceivedTsdus = 0; + + TransportConnection->CurrentReceiveIrp = (PIRP)NULL; + + DisconnectIrp = TransportConnection->DisconnectIrp; + TransportConnection->DisconnectIrp = (PIRP)NULL; + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) != 0) { + addressFile = TransportConnection->AddressFile; + } else { + addressFile = NULL; + } + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + + DeviceContext = TransportConnection->Provider; + + + // + // If this connection was stopped by a call to TdiDisconnect, + // we have to complete that. We save the Irp so we can return + // the connection to the pool before we complete the request. + // + + + if (DisconnectIrp != (PIRP)NULL || + (Lflags2 & CONNECTION_FLAGS2_LDISC) != 0) { + + if (DisconnectIrp != (PIRP)NULL) { + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("IndicateDisconnect %lx, complete IRP\n", TransportConnection); + } + + // + // Now complete the IRP if needed. This will be non-null + // only if TdiDisconnect was called, and we have not + // yet completed it. + // + + DisconnectIrp->IoStatus.Information = 0; + DisconnectIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (DisconnectIrp, IO_NETWORK_INCREMENT); + } + + } else if ((TransportConnection->Status != STATUS_LOCAL_DISCONNECT) && + (addressFile != NULL) && + (addressFile->RegisteredDisconnectHandler == TRUE)) { + + // + // This was a remotely spawned disconnect, so indicate that + // to our client. Note that in the comparison above we + // check the status first, since if it is LOCAL_DISCONNECT + // addressFile may be NULL (BUGBUG: This is sort of a hack + // for PDK2, we should really indicate the disconnect inside + // NbfStopConnection, where we know addressFile is valid). + // + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("IndicateDisconnect %lx, indicate\n", TransportConnection); + } + + // + // if the disconnection was remotely spawned, then indicate + // disconnect. In the case that a disconnect was issued at + // the same time as the connection went down remotely, we + // won't do this because DisconnectIrp will be non-NULL. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfIndicateDisconnect calling DisconnectHandler, refcnt=%ld\n", + TransportConnection->ReferenceCount); + } + + // + // Invoke the user's disconnection event handler, if any. We do this here + // so that any outstanding sends will complete before we tear down the + // connection. + // + + DisconnectReason = 0; + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + (*addressFile->DisconnectHandler)( + addressFile->DisconnectHandlerContext, + TransportConnection->Context, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + +#if MAGIC + if (NbfEnableMagic) { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } +#endif + } + + } else { + + // + // The client does not yet think that this connection + // is up...generally this happens due to request count + // fluctuation during connection setup. + // + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfIndicateDisconnect\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + } + + + return STATUS_SUCCESS; + +} /* NbfIndicateDisconnect */ + + +NTSTATUS +NbfDestroyConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to our lookaside list. It is assumed that the caller + has removed all IRPs from the connections's queues first. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfDestroyConnection: Entered for connection %lx.\n", + TransportConnection); + } + +#if DBG + if (TransportConnection->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed connection 0x%lx\n", TransportConnection); + DbgBreakPoint (); + } + if (!(TransportConnection->Flags2 & CONNECTION_FLAGS2_STOPPING)) { + NbfPrint1 ("attempt to destroy unstopped connection 0x%lx\n", TransportConnection); + DbgBreakPoint (); + } + TransportConnection->Destroyed = TRUE; + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&TransportConnection->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#endif + + DeviceContext = TransportConnection->Provider; + + // + // Destroy any association that this connection has. + // + + NbfDestroyAssociation (TransportConnection); + + // + // Clear out any associated nasties hanging around the connection. Note + // that the current flags are set to STOPPING; this way anyone that may + // maliciously try to use the connection after it's dead and gone will + // just get ignored. + // + + ASSERT (TransportConnection->Lsn == 0); + + TransportConnection->Flags = 0; + TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING; + TransportConnection->NetbiosHeader.TransmitCorrelator = 0; + TransportConnection->Retries = 0; // no retries yet. + TransportConnection->MessageBytesReceived = 0; // no data yet + TransportConnection->MessageBytesAcked = 0; + TransportConnection->MessageInitAccepted = 0; + TransportConnection->ReceiveBytesUnaccepted = 0; + + + // + // Now complete the close IRP. This will be set to non-null + // when CloseConnection was called. + // + + CloseIrp = TransportConnection->CloseIrp; + + if (CloseIrp != (PIRP)NULL) { + + TransportConnection->CloseIrp = (PIRP)NULL; + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + + } else { + +#if DBG + NbfPrint1("Connection %x destroyed, no CloseIrp!!\n", TransportConnection); +#endif + + } + + // + // Return the connection to the provider's pool. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + --DeviceContext->ConnectionInUse; + + if ((DeviceContext->ConnectionAllocated - DeviceContext->ConnectionInUse) > + DeviceContext->ConnectionInitAllocated) { + NbfDeallocateConnection (DeviceContext, TransportConnection); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated connection at %lx\n", TransportConnection); + } + } else { + InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Destroy Connection", DeviceContext, DCREF_CONNECTION); + + return STATUS_SUCCESS; + +} /* NbfDestroyConnection */ + + +#if DBG +VOID +NbfRefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfReferenceConnection: entered for connection %lx, " + "current level=%ld.\n", + TransportConnection, + TransportConnection->ReferenceCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, TRUE ); +#endif + + result = InterlockedIncrement (&TransportConnection->ReferenceCount); + + if (result == 0) { + + // + // The first increment causes us to increment the + // "ref count is not zero" special ref. + // + + ExInterlockedAddUlong( + (PULONG)(&TransportConnection->SpecialRefCount), + 1, + TransportConnection->ProviderInterlock); + +#if DBG + ++TransportConnection->RefTypes[CREF_SPECIAL_TEMP]; +#endif + + } + + ASSERT (result >= 0); + +} /* NbfRefConnection */ +#endif + + +VOID +NbfDerefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyConnection to remove it from the system. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfDereferenceConnection: entered for connection %lx, " + "current level=%ld.\n", + TransportConnection, + TransportConnection->ReferenceCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, FALSE ); +#endif + + result = InterlockedDecrement (&TransportConnection->ReferenceCount); + + // + // If all the normal references to this connection are gone, then + // we can remove the special reference that stood for + // "the regular ref count is non-zero". + // + + if (result < 0) { + + // + // If the refcount is -1, then we need to disconnect from + // the link and indicate disconnect. However, we need to + // do this before we actually do the special deref, since + // otherwise the connection might go away while we + // are doing that. + // + // Note that both these routines are protected in that if they + // are called twice, the second call will have no effect. + // + + + // + // If both the connection and its link are active, then they have + // mutual references to each other. We remove the link's + // reference to the connection in NbfStopConnection, now + // the reference count has fallen enough that we know it + // is okay to remove the connection's reference to the + // link. + // + + if (NbfDisconnectFromLink (TransportConnection, TRUE)) { + + // + // if the reference count goes to one, we can safely indicate the + // user about disconnect states. That reference should + // be for the connection's creation. + // + + NbfIndicateDisconnect (TransportConnection); + + } + + // + // Now it is OK to let the connection go away. + // + + NbfDereferenceConnectionSpecial ("Regular ref gone", TransportConnection, CREF_SPECIAL_TEMP); + + } + +} /* NbfDerefConnection */ + + +VOID +NbfDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routines completes the dereferencing of a connection. + It may be called any time, but it does not do its work until + the regular reference count is also 0. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint3 ("NbfDereferenceConnectionSpecial: entered for connection %lx, " + "current level=%ld (%ld).\n", + TransportConnection, + TransportConnection->ReferenceCount, + TransportConnection->SpecialRefCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, FALSE ); +#endif + + + ACQUIRE_SPIN_LOCK (TransportConnection->ProviderInterlock, &oldirql); + + --TransportConnection->SpecialRefCount; + + if ((TransportConnection->SpecialRefCount == 0) && + (TransportConnection->ReferenceCount == -1)) { + + // + // If we have deleted all references to this connection, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the connection any longer. + // + +#if DBG + { + BOOLEAN TimerCancelled; + TimerCancelled = KeCancelTimer (&TransportConnection->Timer); + ASSERT (TimerCancelled); + } +#endif + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + NbfDestroyConnection (TransportConnection); + + } else { + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + } + +} /* NbfDerefConnectionSpecial */ + + +VOID +NbfClearConnectionLsn( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine clears the LSN field in a connection. To do this is + acquires the device context lock, and modifies the table value + for that LSN depending on the type of the connection. + + NOTE: This routine is called with the connection spinlock held, + or in a state where nobody else will be accessing the + connection. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + KIRQL oldirql; + + DeviceContext = TransportConnection->Provider; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (TransportConnection->Lsn != 0) { + + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) { + + // + // It was to a group address; the count should be + // LSN_TABLE_MAX. + // + + ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] == LSN_TABLE_MAX); + + DeviceContext->LsnTable[TransportConnection->Lsn] = 0; + + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_GROUP_LSN; + + } else { + + ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] > 0); + + --(DeviceContext->LsnTable[TransportConnection->Lsn]); + + } + + TransportConnection->Lsn = 0; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} + + +PTP_CONNECTION +NbfLookupConnectionById( + IN PTP_ADDRESS Address, + IN USHORT ConnectionId + ) + +/*++ + +Routine Description: + + This routine accepts a connection identifier and an address and + returns a pointer to the connection object, TP_CONNECTION. If the + connection identifier is not found on the address, then NULL is returned. + This routine automatically increments the reference count of the + TP_CONNECTION structure if it is found. It is assumed that the + TP_ADDRESS structure is already held with a reference count. + +Arguments: + + Address - Pointer to a transport address object. + + ConnectionId - Identifier of the connection for this address. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION Connection; + BOOLEAN Found = FALSE; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfLookupConnectionById: entered, Address: %lx ID: %lx\n", + Address, ConnectionId); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) && + (Connection->ConnectionId == ConnectionId)) { + + // This reference is removed by the calling function + NbfReferenceConnection ("Lookup up for request", Connection, CREF_BY_ID); + Found = TRUE; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + } + + if (Found) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return Connection; + } + + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* NbfLookupConnectionById */ + + +PTP_CONNECTION +NbfLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine accepts a connection identifier and an address and + returns a pointer to the connection object, TP_CONNECTION. If the + connection identifier is not found on the address, then NULL is returned. + This routine automatically increments the reference count of the + TP_CONNECTION structure if it is found. It is assumed that the + TP_ADDRESS structure is already held with a reference count. + + BUGBUG: Should the ConnectionDatabase go in the address file? + +Arguments: + + Address - Pointer to a transport address object. + + ConnectionContext - Connection Context for this address. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + BOOLEAN Found = FALSE; + PTP_CONNECTION Connection; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfLookupConnectionByContext: entered, Address: %lx Context: %lx\n", + Address, ConnectionContext); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (Connection->Context == ConnectionContext) { + // This reference is removed by the calling function + NbfReferenceConnection ("Lookup up for request", Connection, CREF_LISTENING); + Found = TRUE; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + } + + if (Found) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return Connection; + } + + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* NbfLookupConnectionByContext */ + + +PTP_CONNECTION +NbfLookupListeningConnection( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connection database on an address to find + a TP_CONNECTION object which has LSN=0 and CONNECTION_FLAGS_WAIT_NQ + flag set. It returns a pointer to the found connection object (and + simultaneously resets the flag) or NULL if it could not be found. + The reference count is also incremented atomically on the connection. + + The list is scanned for listens posted to this specific remote + name, or to those with no remote name specified. + +Arguments: + + Address - Pointer to a transport address object. + + RemoteName - The name of the remote. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_CONNECTION Connection; + PLIST_ENTRY p, q; + PTP_REQUEST ListenRequest; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfLookupListeningConnection: Entered.\n"); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + if ((Connection->Lsn == 0) && + (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NQ)) { + + q = Connection->InProgressRequest.Flink; + if (q != &Connection->InProgressRequest) { + ListenRequest = CONTAINING_RECORD (q, TP_REQUEST, Linkage); + if ((ListenRequest->Buffer2 != NULL) && + (!RtlEqualMemory( + ListenRequest->Buffer2, + RemoteName, + NETBIOS_NAME_LENGTH))) { + continue; + } + } else { + continue; + } + // This reference is removed by the calling function + NbfReferenceConnection ("Found Listening", Connection, CREF_LISTENING); + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NQ; + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfLookupListeningConnection: Found Connection %lx\n",Connection); + } + return Connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("NbfLookupListeningConnection: Found No Connection!\n"); + } + + return NULL; + +} /* NbfLookupListeningConnection */ + + +VOID +NbfStopConnection( + IN PTP_CONNECTION Connection, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on a connection and + destroy the object. This is done in a graceful manner; i.e., all + outstanding requests are terminated by cancelling them, etc. It is + assumed that the caller has a reference to this connection object, + but this routine will do the dereference for the one issued at creation + time. + + Orderly release is a function of this routine, but it is not a provided + service of this transport provider, so there is no code to do it here. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Status - The status that caused us to stop the connection. This + will determine what status pending requests are aborted with, + and also how we proceed during the stop (whether to send a + session end, and whether to indicate disconnect). + +Return Value: + + none. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PIRP Irp; + PTP_REQUEST Request; + BOOLEAN TimerWasCleared; + ULONG DisconnectReason; + PULONG StopCounter; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint3 ("NbfStopConnection: Entered for connection %lx LSN %x RSN %x.\n", + Connection, Connection->Lsn, Connection->Rsn); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + DeviceContext = Connection->Provider; + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + if (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING)) { + + // + // We are stopping the connection, record statistics + // about it. + // + + if (Connection->Flags & CONNECTION_FLAGS_READY) { + DECREMENT_COUNTER (DeviceContext, OpenConnections); + } + + Connection->Flags2 |= CONNECTION_FLAGS2_STOPPING; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->Status = Status; + + if (Connection->Link != NULL) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->Flags &= ~(CONNECTION_FLAGS_READY| + CONNECTION_FLAGS_WAIT_SI| + CONNECTION_FLAGS_WAIT_SC); // no longer open for business + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // If this flag was on, turn it off. + // + Connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH; + + // + // Stop the timer if it was running. + // + + TimerWasCleared = KeCancelTimer (&Connection->Timer); + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfStopConnection: Timer for connection %lx " + "%s canceled.\n", Connection, + TimerWasCleared ? "was" : "was NOT" ); + } + + + switch (Status) { + + case STATUS_LOCAL_DISCONNECT: + StopCounter = &DeviceContext->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + StopCounter = &DeviceContext->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + StopCounter = &DeviceContext->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + StopCounter = &DeviceContext->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + StopCounter = &DeviceContext->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + StopCounter = &DeviceContext->Statistics.RemoteResourceFailures; + break; + case STATUS_INSUFFICIENT_RESOURCES: + StopCounter = &DeviceContext->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + StopCounter = &DeviceContext->Statistics.NotFoundFailures; + break; + case STATUS_REMOTE_NOT_LISTENING: + StopCounter = &DeviceContext->Statistics.NoListenFailures; + break; + + default: + StopCounter = NULL; + break; + + } + + if (StopCounter != NULL) { + (*StopCounter)++; + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // Run down all TdiConnect/TdiDisconnect/TdiListen requests. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + while (TRUE) { + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + break; + } + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock(cancelirql); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + LARGE_INTEGER MilliSeconds, Time; + ULONG junk; + KeQuerySystemTime (&Time); + MilliSeconds.LowPart = Time.LowPart; + MilliSeconds.HighPart = Time.HighPart; + MilliSeconds.QuadPart = MilliSeconds.QuadPart - + (Request->Time).QuadPart; + MilliSeconds = RtlExtendedLargeIntegerDivide (MilliSeconds, 10000L, &junk); + NbfPrint3 ("NbfStopConnection: Canceling pending CONNECT, Irp: %lx Time Pending: %ld%ld msec\n", + Request->IoRequestPacket, MilliSeconds.HighPart, MilliSeconds.LowPart); + } +#endif + + NbfCompleteRequest (Request, Connection->Status, 0); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + } + + + if (Connection->Link == NULL) { + + // + // We are stopping early on. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + + if (TimerWasCleared) { + NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference. + } + + + ASSERT (Connection->SendState == CONNECTION_SENDSTATE_IDLE); + ASSERT (!Connection->OnPacketWaitQueue); + ASSERT (!Connection->OnDataAckQueue); + ASSERT (!(Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK)); + ASSERT (IsListEmpty(&Connection->SendQueue)); + ASSERT (IsListEmpty(&Connection->ReceiveQueue)); + + return; + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If this connection is waiting to packetize, + // remove it from the device context queue it is on. + // + // NOTE: If the connection is currently in the + // packetize queue, it will eventually go to get + // packetized and at that point it will get + // removed. + // + + if (Connection->OnPacketWaitQueue) { + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1("Stop waiting connection, flags %lx\n", + Connection->Flags); + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + Connection->OnPacketWaitQueue = FALSE; + ASSERT ((Connection->Flags & CONNECTION_FLAGS_SEND_SE) == 0); + Connection->Flags &= ~(CONNECTION_FLAGS_STARVED|CONNECTION_FLAGS_W_PACKETIZE); + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + + // + // If we are on the data ack queue, then take ourselves off. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if (Connection->OnDataAckQueue) { + RemoveEntryList (&Connection->DataAckLinkage); + Connection->OnDataAckQueue = FALSE; + DeviceContext->DataAckQueueChanged = TRUE; + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If this connection is waiting to send a piggyback ack, + // remove it from the device context queue for that, and + // send a data ack (which will get there before the + // SessionEnd). + // + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + +#if DBG + { + extern ULONG NbfDebugPiggybackAcks; + if (NbfDebugPiggybackAcks) { + NbfPrint1("Stop waiting connection, deferred flags %lx\n", + Connection->DeferredFlags); + } + } +#endif + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfSendDataAck (Connection); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (TimerWasCleared) { + NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference. + } + + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + // + // Run down all TdiSend requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->SendQueue); + if (p == &Connection->SendQueue) { + break; + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelirql); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1("NbfStopConnection: Canceling pending SEND, Irp: %lx\n", + Irp); + } +#endif + NbfCompleteSendIrp (Irp, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ++Connection->TransmissionErrors; + } + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + // + // Run down all TdiReceive requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->ReceiveQueue); + if (p == &Connection->ReceiveQueue) { + break; + } + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NULL); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfStopConnection: Canceling pending RECEIVE, Irp: %lx\n", + Irp); + } +#endif + + // + // It is OK to call this with the locks held. + // + NbfCompleteReceiveIrp (Irp, Connection->Status, 0); + + ++Connection->ReceiveErrors; + } + + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelirql); + + // + // If we aren't DESTROYing the link, then send a SESSION_END frame + // to the remote side. When the SESSION_END frame is acknowleged, + // we will decrement the connection's reference count by one, removing + // its creation reference. This will cause the connection object to + // be disposed of, and will begin running down the link. + // DGB: add logic to avoid blowing away link if one doesn't exist yet. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + DisconnectReason = 0; + if (Connection->Flags2 & CONNECTION_FLAGS2_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (Connection->Flags2 & CONNECTION_FLAGS2_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + if (Connection->Link != NULL) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Status == STATUS_LOCAL_DISCONNECT) || + (Status == STATUS_CANCELLED)) { + + // + // (note that a connection should only get stopped + // with STATUS_INSUFFICIENT_RESOURCES if it is not + // yet connected to the remote). + // + + // + // If this is done, when this packet is destroyed + // it will dereference the connection for CREF_LINK. + // + + NbfSendSessionEnd ( + Connection, + (BOOLEAN)((DisconnectReason & TDI_DISCONNECT_ABORT) != 0)); + + } else { + + // + // Not attached to the link anymore; this dereference + // will allow our reference to fall below 3, which + // will cause NbfDisconnectFromLink to be called. + // + + NbfDereferenceConnection("Stopped", Connection, CREF_LINK); + + } + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + + // + // Note that we've blocked all new requests being queued during the + // time we have been in this teardown code; NbfDestroyConnection also + // sets the connection flags to STOPPING when returning the + // connection to the queue. This avoids lingerers using non-existent + // connections. + // + + } else { + + // + // The connection was already stopping; it may have a + // SESSION_END pending in which case we should kill + // it. + // + + if ((Status != STATUS_LOCAL_DISCONNECT) && + (Status != STATUS_CANCELLED)) { + + if (Connection->Flags & CONNECTION_FLAGS_SEND_SE) { + + ASSERT (Connection->Link != NULL); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags &= ~CONNECTION_FLAGS_SEND_SE; + + if (Connection->OnPacketWaitQueue) { + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); +#if DBG + DbgPrint ("NBF: Removing connection %lx from PacketWait for SESSION_END\n", Connection); +#endif + Connection->OnPacketWaitQueue = FALSE; + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfDereferenceConnection("Stopped again", Connection, CREF_LINK); + return; + + } + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + } +} /* NbfStopConnection */ + + +VOID +NbfCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + or a listen. It is simple since there can only be one of these + active on a connection; we just stop the connection, the IRP + will get completed as part of normal session teardown. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_REQUEST Request; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_CONNECT || IrpSp->MinorFunction == TDI_LISTEN)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + NbfReferenceConnection ("Cancelling Send", Connection, CREF_TEMP); + + p = RemoveHeadList (&Connection->InProgressRequest); + ASSERT (p != &Connection->InProgressRequest); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + ASSERT (Request->IoRequestPacket == Irp); +#ifdef RASAUTODIAL + // + // If there's an automatic connection in + // progress, cancel it. + // + if (Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING) + fCanceled = NbfCancelTdiConnect(NULL, Irp); +#endif // RASAUTODIAL + + if (fCanceled) + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (fCanceled) { + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2("NBF: Cancelled in-progress connect/listen %lx on %lx\n", + Request->IoRequestPacket, Connection); + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + NbfCompleteRequest (Request, STATUS_CANCELLED, 0); + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); // prevent indication to clients + KeLowerIrql (oldirql); + } + + NbfDereferenceConnection ("Cancel done", Connection, CREF_TEMP); + +} + +#if 0 +VOID +NbfWaitConnectionOnLink( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ) + +/*++ + +Routine Description: + + This routine is called to suspend a connection's activities because + the specified session-oriented frame could not be sent due to link + problems. Routines in FRAMESND.C call this. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Flags - Field containing bitflag set to indicate starved frame to be sent. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfWaitConnectionOnLink: Entered.\n"); + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) || + (Flags == CONNECTION_FLAGS_SEND_SE)) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= Flags; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); +} /* NbfWaitConnectionOnLink */ +#endif + + +VOID +NbfStartConnectionTimer( + IN PTP_CONNECTION Connection, + IN PKDEFERRED_ROUTINE TimeoutFunction, + IN ULONG WaitTime + ) + +/*++ + +Routine Description: + + This routine is called to start a timeout on NAME_QUERY/NAME_RECOGNIZED + activities on a connection. + +Arguments: + + TransportConnection - Pointer to a TP_CONNECTION object. + + TimeoutFunction - The function to call when the timer fires. + + WaitTime - a longword containing the low order time to wait. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER Timeout; + BOOLEAN AlreadyInserted; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfStartConnectionTimer: Entered for connection %lx.\n", + Connection ); + } + + // + // Start the timer. Unlike the link timers, this is simply a kernel- + // managed object. + // + + Timeout.LowPart = (ULONG)(-(LONG)WaitTime); + Timeout.HighPart = -1; + + // + // Take the lock so we synchronize the cancelling with + // restarting the timer. This is so two threads won't + // both fail to cancel and then start the timer at the + // same time (it messes up the reference count). + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + AlreadyInserted = KeCancelTimer (&Connection->Timer); + + KeInitializeDpc ( + &Connection->Dpc, + TimeoutFunction, + (PVOID)Connection); + + KeSetTimer ( + &Connection->Timer, + Timeout, + &Connection->Dpc); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // If the timer wasn't already running, reference the connection to + // account for the new timer. If the timer was already started, + // then KeCancelTimer will have returned TRUE. In this + // case, the prior call to NbfStartConnectionTimer referenced the + // connection, so we don't do it again here. + // + + if ( !AlreadyInserted ) { + + // This reference is removed in ConnectionEstablishmentTimeout, + // or when the timer is cancelled. + + NbfReferenceConnection ("starting timer", Connection, CREF_TIMER); + } + +} /* NbfStartConnectionTimer */ + diff --git a/private/ntos/tdi/nbf/devctx.c b/private/ntos/tdi/nbf/devctx.c new file mode 100644 index 000000000..0d6179e60 --- /dev/null +++ b/private/ntos/tdi/nbf/devctx.c @@ -0,0 +1,408 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + devctx.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. Currently, there is no need to create them or destroy + them, as this is handled at configuration time. If it is later required + to dynamically load/unload the transport provider's device object and + associated context, then we can add the create and destroy functions. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + David Beaver (dbeaver) 1 -July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfCreateDeviceContext) +#else +#pragma alloc_text(PAGE,NbfCreateDeviceContext) +#endif + +#endif + + +VOID +NbfRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint0 ("NbfRefDeviceContext: Entered.\n"); + } + + ASSERT (DeviceContext->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&DeviceContext->ReferenceCount); + +} /* NbfRefDeviceContext */ + + +VOID +NbfDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint0 ("NbfDerefDeviceContext: Entered.\n"); + } + + result = InterlockedDecrement (&DeviceContext->ReferenceCount); + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyDeviceContext (DeviceContext); + } + +} /* NbfDerefDeviceContext */ + + + +NTSTATUS +NbfCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + DeviceContext - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE_CONTEXT deviceContext; + USHORT i; + + + // + // Create the device object for NETBEUI. + // + + status = IoCreateDevice( + DriverObject, + sizeof (DEVICE_CONTEXT) - sizeof (DEVICE_OBJECT) + + (DeviceName->Length + sizeof(UNICODE_NULL)), + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + deviceContext = (PDEVICE_CONTEXT)deviceObject; + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)deviceContext) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE_CONTEXT) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + deviceContext->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + deviceContext->DeviceName = (PWCHAR)(deviceContext+1); + RtlCopyMemory( + deviceContext->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + deviceContext->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize device context fields. + // + + deviceContext->NetmanVariables = NULL; // no variables yet. + + // + // Initialize the reference count. + // + + deviceContext->ReferenceCount = 1; + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_DCREFS; Counter++) { + deviceContext->RefTypes[Counter] = 0; + } + + // This reference is removed by the caller. + + deviceContext->RefTypes[DCREF_CREATION] = 1; + } +#endif + + // + // initialize the various fields in the device context + // + + KeInitializeSpinLock (&deviceContext->Interlock); + KeInitializeSpinLock (&deviceContext->SpinLock); + KeInitializeSpinLock (&deviceContext->LinkSpinLock); + KeInitializeSpinLock (&deviceContext->TimerSpinLock); + KeInitializeSpinLock (&deviceContext->LoopbackSpinLock); + KeInitializeSpinLock (&deviceContext->SendPoolListLock); + KeInitializeSpinLock (&deviceContext->RcvPoolListLock); + + deviceContext->LinkTreeRoot = NULL; + deviceContext->LastLink = NULL; + deviceContext->LinkTreeElements = 0; + + deviceContext->LoopbackLinks[0] = NULL; + deviceContext->LoopbackLinks[1] = NULL; + deviceContext->LoopbackInProgress = FALSE; + KeInitializeDpc( + &deviceContext->LoopbackDpc, + NbfProcessLoopbackQueue, + (PVOID)deviceContext + ); + + deviceContext->WanThreadQueued = FALSE; + ExInitializeWorkItem( + &deviceContext->WanDelayedQueueItem, + NbfProcessWanDelayedQueue, + (PVOID)deviceContext); + + + deviceContext->UniqueIdentifier = 1; + deviceContext->ControlChannelIdentifier = 1; + + InitializeListHead (&deviceContext->ConnectionPool); + InitializeListHead (&deviceContext->AddressPool); + InitializeListHead (&deviceContext->AddressFilePool); + InitializeListHead (&deviceContext->AddressDatabase); + InitializeListHead (&deviceContext->LinkPool); + InitializeListHead (&deviceContext->LinkDeferred); + InitializeListHead (&deviceContext->PacketWaitQueue); + InitializeListHead (&deviceContext->PacketizeQueue); + InitializeListHead (&deviceContext->DataAckQueue); + InitializeListHead (&deviceContext->DeferredRrQueue); + InitializeListHead (&deviceContext->RequestPool); + InitializeListHead (&deviceContext->UIFramePool); + deviceContext->PacketPool.Next = NULL; + deviceContext->ReceivePacketPool.Next = NULL; + deviceContext->ReceiveBufferPool.Next = NULL; + InitializeListHead (&deviceContext->ReceiveInProgress); + InitializeListHead (&deviceContext->ShortList); + InitializeListHead (&deviceContext->LongList); + InitializeListHead (&deviceContext->PurgeList); + InitializeListHead (&deviceContext->LoopbackQueue); + InitializeListHead (&deviceContext->FindNameQueue); + InitializeListHead (&deviceContext->StatusQueryQueue); + InitializeListHead (&deviceContext->IrpCompletionQueue); + + InitializeListHead (&deviceContext->QueryIndicationQueue); + InitializeListHead (&deviceContext->DatagramIndicationQueue); + deviceContext->IndicationQueuesInUse = FALSE; + + // + // Equivalent to setting ShortListActive, DataAckQueueActive, + // and LinkDeferredActive to FALSE. + // + + deviceContext->a.AnyActive = 0; + + deviceContext->ProcessingShortTimer = FALSE; + deviceContext->DataAckQueueChanged = FALSE; + deviceContext->StalledConnectionCount = (USHORT)0; + + deviceContext->EasilyDisconnected = FALSE; + + // + // Initialize provider statistics. + // + + deviceContext->Statistics.Version = 0x0100; + +#if 0 + deviceContext->Information.Version = 2; // BUGBUG: define TDI_VERSION in TDI.H. + deviceContext->Information.MaxTsduSize = NBF_MAX_TSDU_SIZE; + deviceContext->Information.MaxDatagramSize = NBF_MAX_DATAGRAM_SIZE; + deviceContext->Information.MaxConnectionUserData = NBF_MAX_CONNECTION_USER_DATA; + deviceContext->Information.ServiceFlags = NBF_SERVICE_FLAGS; + deviceContext->Information.TransmittedTsdus = 0; + deviceContext->Information.ReceivedTsdus = 0; + deviceContext->Information.TransmissionErrors = 0; + deviceContext->Information.ReceiveErrors = 0; + deviceContext->Information.MinimumLookaheadData = NBF_MIN_LOOKAHEAD_DATA; + deviceContext->Information.MaximumLookaheadData = NBF_MAX_LOOKAHEAD_DATA; + deviceContext->Information.DiscardedFrames = 0; + deviceContext->Information.OversizeTsdusReceived = 0; + deviceContext->Information.UndersizeTsdusReceived = 0; + deviceContext->Information.MulticastTsdusReceived = 0; + deviceContext->Information.BroadcastTsdusReceived = 0; + deviceContext->Information.MulticastTsdusTransmitted = 0; + deviceContext->Information.BroadcastTsdusTransmitted = 0; + deviceContext->Information.SendTimeouts = 0; + deviceContext->Information.ReceiveTimeouts = 0; + deviceContext->Information.ConnectionIndicationsReceived = 0; + deviceContext->Information.ConnectionIndicationsAccepted = 0; + deviceContext->Information.ConnectionsInitiated = 0; + deviceContext->Information.ConnectionsAccepted = 0; +#endif + + deviceContext->State = DEVICECONTEXT_STATE_OPENING; + + // + // Loopback buffer is not allocated. + // + + deviceContext->LookaheadContiguous = NULL; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&deviceContext->AddressResource); + + // + // No LSNs are in use. + // + + for (i=0; i<(NETBIOS_SESSION_LIMIT+1); i++) { + deviceContext->LsnTable[i] = 0; + } + deviceContext->NextLsnStart = 1; + + // + // No addresses are in use. + // + + for (i=0; i<256; i++) { + deviceContext->AddressCounts[i] = 0; + } + + // + // set the netbios multicast address for this network type + // + + for (i=0; iLocalAddress.Address [i] = 0; // set later + deviceContext->NetBIOSAddress.Address [i] = 0; + } + + deviceContext->Type = NBF_DEVICE_CONTEXT_SIGNATURE; + deviceContext->Size = sizeof (DEVICE_CONTEXT); + + *DeviceContext = deviceContext; + return STATUS_SUCCESS; +} + + +VOID +NbfDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + DeviceContext - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&DeviceContext->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)DeviceContext); + return; +} diff --git a/private/ntos/tdi/nbf/dlc.c b/private/ntos/tdi/nbf/dlc.c new file mode 100644 index 000000000..893bffd49 --- /dev/null +++ b/private/ntos/tdi/nbf/dlc.c @@ -0,0 +1,3270 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + dlc.c + +Abstract: + + This module contains code which implements the data link layer for the + transport provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Macros + + +// +// These two functions are used by the loopback indicator. +// + +STATIC +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ); + + +VOID +NbfProcessSabme( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header, + IN PVOID MacHeader, + IN PHARDWARE_ADDRESS SourceAddress, + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine processes a received SABME frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + + MacHeader - Pointer to the MAC header of the packet. + + DeviceContext - The device context of this adapter. + +Return Value: + + none. + +--*/ + +{ + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessSabme: Entered.\n"); + } + + // + // Format must be: SABME-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + // + // Build response routing information. We do this on the SABME, even + // though we already did in on the Name Query, because the client may + // choose a different route (if there were multiple routes) than we + // did. + // + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + MacHeader, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + MacConstructHeader ( + &DeviceContext->MacInfo, + Link->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + 0, // PacketLength, filled in later + ResponseSR, + SourceRoutingLength, + (PUINT)&(Link->HeaderLength)); + + // + // We optimize for fourteen-byte headers by putting + // the correct Dsap/Ssap at the end, so we can fill + // in new packets as one 16-byte move. + // + + if (Link->HeaderLength <= 14) { + Link->Header[Link->HeaderLength] = DSAP_NETBIOS_OVER_LLC; + Link->Header[Link->HeaderLength+1] = DSAP_NETBIOS_OVER_LLC; + } + + // + // Process the SABME. + // + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Remote station is initiating this link. Send UA and wait for + // his checkpoint before setting READY state. + // + + // Moving out of ADM, add special reference + NbfReferenceLinkSpecial("Waiting for Poll", Link, LREF_NOT_ADM); + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); + NbfSendUa (Link, PollFinal); // releases lock + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("ADM SABME on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // SABME at the same time, so we tried to do it at the same time. + // The only balanced thing we can do at this time is to respond + // with UA and duplicate the effort. To not send anything would + // be bad because the link would never complete. + // + + // + // BUGBUG: What about timers here? + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + NbfSendUa (Link, PollFinal); // releases lock + StartTi (Link); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a SABME, so this is really a link reset. + // + // Unfortunately, if we happen to get two SABMEs + // and respond to the second one with another UA + // (when he has sent the RR-c/p and is expecting + // an RR-r/f), he will send a FRMR. So, we ignore + // this frame. + // + + // Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + StartTi(Link); +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's resetting the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this SABME. We have to assume he wants to reset the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this SABME. He wants to initiate the link + // again because a transport connection has been initiated while + // we were taking the link down. + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); // reset this connection. + NbfSendUa (Link, PollFinal); // releases lock. + StartTi(Link); +#if DBG + s = "READY/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessSabme: Processed, State was %s.\n", s); + } +#endif + +} /* NbfProcessSabme */ + + +VOID +NbfProcessUa( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received UA frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessUa: Entered.\n"); + } + + // + // Format must be: UA-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received an unnumbered acknowlegement while in ADM. Somehow + // the remote station is confused, so tell him we're disconnected. + // + + NbfSendDm (Link, FALSE); // indicate we're disconnected, release lock +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received the UA. + // + + UpdateBaseT1Timeout (Link); // got response to poll. + + Link->State = LINK_STATE_W_FINAL; // wait for RR-r/f. + Link->SendRetries = (UCHAR)Link->LlcRetries; + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's confused; throw it away. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a UA, so he is confused. Throw it away. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this UA. He is confused. Throw it away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "READY/W_POLL/W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received the correct response. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // completed disconnection. + + // + // This is the normal "clean" disconnect path, so we stop + // all the timers here since we won't call NbfStopLink. + // + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // Moving to ADM, dereference link + NbfDereferenceLinkSpecial ("Got UA for DISC", Link, LREF_NOT_ADM); // decrement link's last ref. + +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessUa */ + + +VOID +NbfProcessDisc( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DISC frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDisc: Entered.\n"); + } + + // + // Format must be: DISC-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DISC while in ADM. Simply report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // indicate we're disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Kill the link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDisc calling NbfStopLink\n" ); + } +#endif + NbfStopLink (Link); // say goodnight, gracie + + // Moving to ADM, remove reference + NbfDereferenceLinkSpecial("Stopping link", Link, LREF_NOT_ADM); + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DISC. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DISC, so he wants to drop the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DISC, so he wants to drop the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DISC from him as well. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock. + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DISC on W_DIS_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDisc */ + + +VOID +NbfProcessDm( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DM frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDm: Entered.\n"); + } + + // + // Format must be: DM-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DM while in ADM. Do nothing. + // + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DM. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect mode indicator, we will reconnect in time. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "ADM/CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he is in ADM, so we have to shut down. + // + +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDm calling NbfStopLink\n" ); + } +#endif + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DM, so he has dropped the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DM, so he has already dropped the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DM from him, indicating + // that he is now in ADM. While technically not what we expected, + // this protocol is commonly used in place of UA-r/f, so just treat + // as though we got a UA-r/f. Disconnect the link normally. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DM in W_DISC_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "READY/W_FINAL/W_POLL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDm */ + + +VOID +NbfProcessFrmr( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received FRMR response frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + ULONG DumpData[6]; + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessFrmr: Entered.\n"); + } + + // + // Log an error, this shouldn't happen. + // + + DumpData[0] = Link->State; + DumpData[1] = Link->Flags; + DumpData[2] = (Header->Information.FrmrInfo.Command << 8) + + (Header->Information.FrmrInfo.Ctrl); + DumpData[3] = (Header->Information.FrmrInfo.Vs << 16) + + (Header->Information.FrmrInfo.Vr << 8) + + (Header->Information.FrmrInfo.Reason); + DumpData[4] = (Link->SendState << 24) + + (Link->NextSend << 16) + + (Link->LastAckReceived << 8) + + (Link->SendWindowSize); + DumpData[5] = (Link->ReceiveState << 24) + + (Link->NextReceive << 16) + + (Link->LastAckSent << 8) + + (Link->ReceiveWindowSize); + + NbfWriteGeneralErrorLog( + Link->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 1, + STATUS_LINK_FAILED, + L"FRMR", + 6, + DumpData); + + + ++Link->Provider->FrmrReceived; + + // + // Format must be: FRMR-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a FRMR while in ADM. Report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he reported a protocol error. + // +#if 0 + // We want to reset the link, but not quite as fully + // as NbfResetLink. This code is the same as what + // is in there, except for: + // + // - resetting the send/receive numbers + // - removing packets from the WackQ + // + + StopT1 (Link); + StopT2 (Link); + Link->Flags &= LINK_FLAGS_DEFERRED_MASK; // keep deferred operations + + Link->SendWindowSize = 1; + Link->LinkBusy = FALSE; + + Link->ReceiveWindowSize = 1; + Link->WindowErrors = 0; + Link->BestWindowSize = 1; + Link->WorstWindowSize = Link->MaxWindowSize; + Link->Flags |= LINK_FLAGS_JUMP_START; + + Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout; + Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY; + Link->CurrentPollRetransmits = 0; + Link->CurrentPollOutstanding = FALSE; + Link->T2Timeout = Link->Provider->DefaultT2Timeout; + Link->TiTimeout = Link->Provider->DefaultTiTimeout; + Link->LlcRetries = Link->Provider->LlcRetries; + Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize; + + // + // The rest is similar to NbfActivateLink + // + + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock +#else + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); +#endif + +#if DBG + NbfPrint1("Received FRMR on link %lx\n", Link); +#endif + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a FRMR. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a FRMR. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent FRMR. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a FRMR. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); + +#if DBG + s = "CONN/W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessFrmr */ + + +VOID +NbfProcessTest( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received TEST frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header, PollFinal; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessTest: Entered.\n"); + } + + // + // Process only: TEST-c/x. + // + + // BUGBUG: respond to TEST on a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + +#if DBG + PANIC ("NbfSendTest: Received Test Packet, not processing....\n"); +#endif + //NbfSendTest (Link, FALSE, PollFinal, Psdu); + +} /* NbfProcessTest */ + + +VOID +NbfProcessXid( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received XID frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessXid: Entered.\n"); + } + + // + // Process only: XID-c/x. + // + + // BUGBUG: respond to XID with a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + NbfSendXid (Link, FALSE, PollFinal); // releases lock + +} /* NbfProcessXid */ + + +VOID +NbfProcessSFrame( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_S_FRAME Header, + IN UCHAR CommandByte + ) + +/*++ + +Routine Description: + + This routine processes a received RR, RNR, or REJ frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC S-type frame. + + CommandByte - The command byte of the frame (RR, RNR, or REJ). + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + BOOLEAN Resend; + BOOLEAN AckedPackets; + UCHAR AckSequenceNumber; + UCHAR OldLinkSendRetries; + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfProcessSFrame %s: Entered, Link: %lx\n", Link, + Command == DLC_CMD_RR ? "RR" : (Command == DLC_CMD_RNR ? "RNR" : "REJ")); + } + + // + // Process any of: RR-x/x, RNR-x/x, REJ-x/x + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + if (CommandByte == DLC_CMD_RNR) { + Link->LinkBusy = TRUE; // he's set a busy condition. + } else { + Link->LinkBusy = FALSE; // busy condition cleared. + } + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // We're disconnected. Tell him. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // releases lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Note that the sections below surrounded by + // if (Command && PollFinal) variants are all disjoint sets. + // That's the only reason the Spinlock stuff works right. DO NOT + // attempt to fiddle this unless you figure out the locking first! + // + + // + // If the AckSequenceNumber is not valid, ignore it. The + // number should be between the first packet on the WackQ + // and one more than the last packet. These correspond to + // Link->LastAckReceived and Link->NextSend. + // + + AckSequenceNumber = (UCHAR)(Header->RcvSeq >> 1); + + if (Link->NextSend >= Link->LastAckReceived) { + + // + // There is no 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) || + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } else { + + // + // There is a 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) && + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } + + + // + // We always resend on a REJ, and never on an RNR; + // for an RR we may change Resend to TRUE below. + // If we get a REJ on a WAN line (T1 is more than + // five seconds) then we pretend this was a final + // so we will resend even if a poll was outstanding. + // + + if (CommandByte == DLC_CMD_REJ) { + Resend = TRUE; + if (Link->CurrentT1Timeout >= ((5 * SECONDS) / SHORT_TIMER_DELTA)) { + PollFinal = TRUE; + } + OldLinkSendRetries = (UCHAR)Link->SendRetries; + } else { + Resend = FALSE; + } + + +#if 0 + // + // If we've got a request with no poll, must have fired T2 on + // the other side (or, if the other side is OS/2, he lost a + // packet and knows it or is telling me to lower the window size). + // In the T2 case, we've Acked current stuff, mark the window as + // needing adjustment at some future time. In the OS/2 cases, this + // is also the right thing to do. + // + + if ((!Command) && (!PollFinal)) { + ; + } +#endif + + if (PollFinal) { + + if (Command) { + + // + // If he is checkpointing, then we must respond with RR-r/f to + // update him on the status of our reception of his I-frames. + // + + StopT2 (Link); // we acked some I-frames. + NbfSendRr (Link, FALSE, PollFinal); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + // + // If we were checkpointing, and he has sent an RR-r/f, we + // can clear the checkpoint condition. Any packets which + // are still waiting for acknowlegement at this point must + // now be resent. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: he's responded to our checkpoint.\n"); + } + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: BUGBUG: not ckpting, but final received.\n"); + } + } else if (CommandByte == DLC_CMD_RR) { + OldLinkSendRetries = (UCHAR)Link->SendRetries; + Resend = TRUE; + UpdateBaseT1Timeout (Link); // gor response to poll + } + StopT1 (Link); // checkpointing finished. + Link->SendRetries = (UCHAR)Link->LlcRetries; + Link->SendState = SEND_STATE_READY; + StartTi (Link); + } + } + + // + // NOTE: The link spinlock is held here. + // + + // + // The N(R) in this frame acknowleges some (or all) of our packets. + // We'll ack packets on our send queue if this is a final when we + // call Resend. This call must come after the checkpoint + // acknowlegement check so that an RR-r/f is always sent BEFORE + // any new I-frames. This allows us to always send I-frames as + // commands. + // + + if (Link->WackQ.Flink != &Link->WackQ) { + + // + // NOTE: ResendLlcPackets may release and reacquire + // the link spinlock. + // + + AckedPackets = ResendLlcPackets( + Link, + AckSequenceNumber, + Resend); + + if (Resend && (!AckedPackets) && (Link->State == LINK_STATE_READY)) { + + // + // To prevent stalling, pretend this RR wasn't + // received. + // + + if (OldLinkSendRetries == 1) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + +#if DBG + DbgPrint ("NBF: No ack teardown of %lx\n", Link); +#endif + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfStopLink (Link); + + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + + } else { + + StopTi (Link); + Link->SendRetries = OldLinkSendRetries-1; + + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + Link->SendState = SEND_STATE_CHECKPOINTING; + NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + } +#if DBG + s = "READY"; +#endif + break; // No need to RestartLinkTraffic + + } else if (AckedPackets) { + + Link->SendRetries = (UCHAR)Link->LlcRetries; + + } + + } + + + // + // If the link send state is READY, get the link going + // again. + // + // NOTE: RestartLinkTraffic releases the link spinlock. + // + + if (Link->SendState == SEND_STATE_READY) { + RestartLinkTraffic (Link); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // RR too early, so just let the SABME timeout. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. If he just + // sends something without a poll, we'll let that get by. + // + + if (!Command) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_POLL"; +#endif + break; // don't allow this protocol. + } + Link->State = LINK_STATE_READY; // we're up! + + FakeUpdateBaseT1Timeout (Link); + NbfSendRr (Link, FALSE, PollFinal); // send RR-r/x, release lock + NbfCompleteLink (Link); // fire up the connections. + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("W_POLL RR on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); + +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy. + // + + if (Command || !PollFinal) { // wait for final. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_FINAL"; +#endif + break; // we sent RR-c/p. + } + Link->State = LINK_STATE_READY; // we're up. + StopT1 (Link); // poll was acknowleged. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); +#if DBG + s = "W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this RR. Throw the packet away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + if (CommandByte == DLC_CMD_REJ) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessRej: (%s) REJ received.\n", s); + } + } +#endif + +} /* NbfProcessSFrame */ + + +VOID +NbfInsertInLoopbackQueue ( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET NdisPacket, + IN UCHAR LinkIndex + ) + +/*++ + +Routine Description: + + This routine places a packet on the loopback queue, and + queues a DPC to do the indication if needed. + +Arguments: + + DeviceContext - The device context in question. + + NdisPacket - The packet to place on the loopback queue. + + LinkIndex - The index of the loopback link to indicate to. + +Return Value: + + None: + +--*/ + +{ + PSEND_PACKET_TAG SendPacketTag; + KIRQL oldirql; + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = TRUE; + + SendPacketTag->LoopbackLinkIndex = LinkIndex; + + ACQUIRE_SPIN_LOCK(&DeviceContext->LoopbackSpinLock, &oldirql); + + InsertTailList(&DeviceContext->LoopbackQueue, &SendPacketTag->Linkage); + + if (!DeviceContext->LoopbackInProgress) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + DeviceContext->LoopbackInProgress = TRUE; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->LoopbackSpinLock, oldirql); + +} + + +VOID +NbfProcessLoopbackQueue ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This is the DPC routine which processes items on the + loopback queue. It processes a single request off the + queue (if there is one), then if there is another request + it requeues itself. + +Arguments: + + Dpc - The system DPC object. + + DeferredContext - A pointer to the device context. + + SystemArgument1, SystemArgument2 - Not used. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER FirstBuffer; + PVOID LookaheadBuffer; + UINT LookaheadBufferSize; + UINT BytesCopied; + UINT PacketSize; + ULONG HeaderLength; + PTP_LINK Link; + PSEND_PACKET_TAG SendPacketTag; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + DeviceContext = (PDEVICE_CONTEXT)DeferredContext; + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + p = RemoveHeadList(&DeviceContext->LoopbackQueue); + + // + // BUGBUG: This depends on the fact that the Linkage field is + // the first one in ProtocolReserved. + // + + NdisPacket = CONTAINING_RECORD(p, NDIS_PACKET, ProtocolReserved[0]); + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + + // + // Determine the data needed to indicate. We don't guarantee + // that we will have the correct lookahead length, but since + // we know that any header we prepend is a single piece, + // and that's all we'll have to look at in an indicated packet, + // that's not a concern. + // + // Unfortunately that last paragraph is bogus since for + // indications to our client we need more data... + // + + NdisQueryPacket(NdisPacket, NULL, NULL, &FirstBuffer, &PacketSize); + + NdisQueryBuffer(FirstBuffer, &LookaheadBuffer, &LookaheadBufferSize); + + if ((LookaheadBufferSize != PacketSize) && + (LookaheadBufferSize < NBF_MAX_LOOPBACK_LOOKAHEAD)) { + + // + // There is not enough contiguous data in the + // packet's first buffer, so we merge it into + // DeviceContext->LookaheadContiguous. + // + + if (PacketSize > NBF_MAX_LOOPBACK_LOOKAHEAD) { + LookaheadBufferSize = NBF_MAX_LOOPBACK_LOOKAHEAD; + } else { + LookaheadBufferSize = PacketSize; + } + + NbfCopyFromPacketToBuffer( + NdisPacket, + 0, + LookaheadBufferSize, + DeviceContext->LookaheadContiguous, + &BytesCopied); + + ASSERT (BytesCopied == LookaheadBufferSize); + + LookaheadBuffer = DeviceContext->LookaheadContiguous; + + } + + + // + // Now determine which link to loop it back to; + // UI frames are not indicated on any link. + // + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + + // + // Hold DeviceContext->LinkSpinLock until we get a + // reference. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + switch (SendPacketTag->LoopbackLinkIndex) { + + case LOOPBACK_TO_CONNECTOR: + + Link = DeviceContext->LoopbackLinks[CONNECTOR_LINK]; + break; + + case LOOPBACK_TO_LISTENER: + + Link = DeviceContext->LoopbackLinks[LISTENER_LINK]; + break; + + case LOOPBACK_UI_FRAME: + default: + + Link = (PTP_LINK)NULL; + break; + + } + + // + // For non-null links, we have to reference them. + // We use LREF_TREE since that is what + // NbfGeneralReceiveHandler expects. + // + + if (Link != (PTP_LINK)NULL) { + NbfReferenceLink("loopback indication", Link, LREF_TREE); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + MacReturnHeaderLength( + &DeviceContext->MacInfo, + LookaheadBuffer, + &HeaderLength); + + DeviceContext->LoopbackHeaderLength = HeaderLength; + + // + // Process the receive like any other. We don't have to + // worry about frame padding since we construct the + // frame ourselves. + // + + NbfGeneralReceiveHandler( + DeviceContext, + (NDIS_HANDLE)NdisPacket, + &DeviceContext->LocalAddress, // since it is loopback + Link, + LookaheadBuffer, // header + PacketSize - HeaderLength, // total packet size + (PDLC_FRAME)((PUCHAR)LookaheadBuffer + HeaderLength), // l/a data + LookaheadBufferSize - HeaderLength, // lookahead data length + TRUE + ); + + + // + // Now complete the send. + // + + NbfSendCompletionHandler( + DeviceContext->NdisBindingHandle, + NdisPacket, + NDIS_STATUS_SUCCESS + ); + + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + + // + // Remove these two lines if it is decided thet + // NbfReceiveComplete should be called after every + // loopback indication. + // + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + return; + + } else { + + DeviceContext->LoopbackInProgress = FALSE; + + } + + } else { + + // + // This shouldn't happen! + // + + DeviceContext->LoopbackInProgress = FALSE; + +#if DBG + NbfPrint1("Loopback queue empty for device context %x\n", DeviceContext); +#endif + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + NbfReceiveComplete( + (NDIS_HANDLE)DeviceContext + ); + +} /* NbfProcessLoopbackQueue */ + + +NDIS_STATUS +NbfReceiveIndication ( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + PDEVICE_CONTEXT DeviceContext; + KIRQL oldirql; + PTP_LINK Link; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + UINT RealPacketSize; + PDLC_FRAME DlcHeader; + BOOLEAN Multicast; + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_NDIS) { + PUCHAR p; + SHORT i; + NbfPrint2 ("NbfReceiveIndication: Packet, Size: 0x0%lx LookaheadSize: 0x0%lx\n 00:", + PacketSize, LookaheadBufferSize); + p = (PUCHAR)LookaheadBuffer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",p[i]); + } + NbfPrint0 ("\n"); + } + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + RealPacketSize = 0; + + // + // Obtain the packet length; this may optionally adjust + // the lookahead buffer forward if the header we wish + // to remove spills over into what the MAC considers + // data. If it determines that the header is not + // valid, it keeps RealPacketSize at 0. + // + + MacReturnPacketLength( + &DeviceContext->MacInfo, + HeaderBuffer, + HeaderBufferSize, + PacketSize, + &RealPacketSize, + &LookaheadBuffer, + &LookaheadBufferSize + ); + + if (RealPacketSize < 2) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding packet, bad length %lx\n", + HeaderBuffer); + } + return NDIS_STATUS_NOT_RECOGNIZED; + } + + // + // We've negotiated at least a contiguous DLC header passed back in the + // lookahead buffer. Check it to see if we want this packet. + // + + DlcHeader = (PDLC_FRAME)LookaheadBuffer; + + if (((*(USHORT UNALIGNED *)(&DlcHeader->Dsap)) & + (USHORT)((DLC_SSAP_MASK << 8) | DLC_DSAP_MASK)) != + (USHORT)((DSAP_NETBIOS_OVER_LLC << 8) | DSAP_NETBIOS_OVER_LLC)) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding lookahead data, not NetBIOS: %lx\n", + LookaheadBuffer); + } + LEAVE_NBF; + return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed. + } + + + // + // Check that the packet is not too long. + // + + if (PacketSize > DeviceContext->MaxReceivePacketSize) { +#if DBG + NbfPrint2("NbfReceiveIndication: Ignoring packet length %d, max %d\n", + PacketSize, DeviceContext->MaxReceivePacketSize); +#endif + return NDIS_STATUS_NOT_RECOGNIZED; + } + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceAddressBuffer, + &SourceAddress, + &Multicast + ); + + // + // Record how many multicast packets we get, to monitor + // general network activity. + // + + if (Multicast) { + ++DeviceContext->MulticastPacketCount; + } + + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // Unless this is a UI frame find the Link this packet belongs to. + // If there is not a recognized link, pass the frame on to be handled + // by the receive complete code. + // + + if ((((PDLC_U_FRAME)LookaheadBuffer)->Command) != DLC_CMD_UI) { + + // This adds a link reference if it is found + + Link = NbfFindLink (DeviceContext, SourceAddress->Address); + + if (Link != NULL) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndication: Found link, Link: %lx\n", Link); + } + + } + + } else { + + Link = NULL; + + } + + + NbfGeneralReceiveHandler( + DeviceContext, + ReceiveContext, + SourceAddress, + Link, + HeaderBuffer, // header + RealPacketSize, // total data length in packet + (PDLC_FRAME)LookaheadBuffer, // lookahead data + LookaheadBufferSize, // lookahead data length + FALSE // not loopback + ); + + KeLowerIrql (oldirql); + + return STATUS_SUCCESS; + +} /* NbfReceiveIndication */ + + +VOID +NbfGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PTP_LINK Link, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PDLC_FRAME DlcHeader, + IN UINT DlcSize, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine receives control from either NbfReceiveIndication + or NbfProcessLoopbackQueue. It continues the processing of + indicated data once the link has been determined. + + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + DeviceContext - The device context of this adapter. + + ReceiveContext - A magic cookie for the MAC. + + SourceAddress - The source address of the packet. + + Link - The link that this packet was received on, may be NULL + if the link was not found. If not NULL, Link will have + a reference of type LREF_TREE. + + HeaderBuffer - pointer to the packet header. + + PacketSize - Overall size of the packet (not including header). + + DlcHeader - Points to the DLC header of the packet. + + DlcSize - The length of the packet indicated, starting from DlcHeader. + + Loopback - TRUE if this was called by NbfProcessLoopbackQueue; + used to determine whether to call NdisTransferData or + NbfTransferLoopbackData. + +Return Value: + + None. + +--*/ +{ + + PNDIS_PACKET NdisPacket; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + PSINGLE_LIST_ENTRY linkage; + UINT BytesTransferred; + BOOLEAN Command; + BOOLEAN PollFinal; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PDLC_I_FRAME IHeader; + PDLC_U_FRAME UHeader; + PDLC_S_FRAME SHeader; + PTP_ADDRESS DatagramAddress; + UINT NdisBufferLength; + PVOID BufferPointer; + + ENTER_NBF; + + + INCREMENT_COUNTER (DeviceContext, PacketsReceived); + + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + + if (Link == (PTP_LINK)NULL) { + UHeader = (PDLC_U_FRAME)DlcHeader; + if (((UHeader->Command & ~DLC_U_PF) == DLC_CMD_UI) && Command) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfGeneralReceiveHandler: Processing packet as UI frame.\n"); + } + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRoutingLength > MAX_SOURCE_ROUTING) { + Status = STATUS_ABANDONED; + } + else { + Status = NbfProcessUi ( + DeviceContext, + SourceAddress, + HeaderBuffer, + (PUCHAR)UHeader, + DlcSize, + SourceRouting, + SourceRoutingLength, + &DatagramAddress); + } + } else { + + // + // or drop on the floor. (BUGBUG: Note that state tables say that + // we'll always handle a DM with a DM response. This should change.) + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: it's not a UI frame!\n"); + } + Status = STATUS_SUCCESS; + } + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + LEAVE_NBF; + return; + + } else if (((PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3))->Command == + NBF_CMD_STATUS_RESPONSE) { + + (VOID)NbfProcessStatusResponse( + DeviceContext, + ReceiveContext, + (PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3), + SourceAddress, + SourceRouting, + SourceRoutingLength); + return; + + } else { + goto HandleAtComplete; // only datagrams will get through this + } + } + + + // + // At this point we have a link reference count of type LREF_TREE + // + + ++Link->PacketsReceived; + + // + // deal with I-frames first; they are what we expect the most of... + // + + if (!(DlcHeader->Byte1 & DLC_I_INDICATOR)) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: I-frame encountered.\n"); + } + if (DlcSize >= 4 + sizeof (NBF_HDR_CONNECTION)) { + IHeader = (PDLC_I_FRAME)DlcHeader; + NbfProcessIIndicate ( + Command, + (BOOLEAN)(IHeader->RcvSeq & DLC_I_PF), + Link, + (PUCHAR)DlcHeader, + DlcSize, + PacketSize, + ReceiveContext, + Loopback); + } else { +#if DBG +// IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: Runt I-frame, discarded!\n"); +// } +#endif + ; + } + + } else if (((DlcHeader->Byte1 & DLC_U_INDICATOR) == DLC_U_INDICATOR)) { + + // + // different case switches for S and U frames, because structures + // are different. + // + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NbfReceiveIndication: U-frame encountered.\n"); + } + + UHeader = (PDLC_U_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(UHeader->Command & DLC_U_PF); + switch (UHeader->Command & ~DLC_U_PF) { + + case DLC_CMD_SABME: + NbfProcessSabme (Command, PollFinal, Link, UHeader, + HeaderBuffer, SourceAddress, DeviceContext); + break; + + case DLC_CMD_DISC: + NbfProcessDisc (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UA: + NbfProcessUa (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_DM: + NbfProcessDm (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_FRMR: + NbfProcessFrmr (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UI: + + ASSERT (FALSE); + break; + + case DLC_CMD_XID: + PANIC ("ReceiveIndication: XID!\n"); + NbfProcessXid (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_TEST: + PANIC ("NbfReceiveIndication: TEST!\n"); + NbfProcessTest (Command, PollFinal, Link, UHeader); + break; + + default: + PANIC ("NbfReceiveIndication: bad U-frame, packet dropped.\n"); + + } /* switch */ + + } else { + + // + // We have an S-frame. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: S-frame encountered.\n"); + } + SHeader = (PDLC_S_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(SHeader->RcvSeq & DLC_S_PF); + switch (SHeader->Command) { + + case DLC_CMD_RR: + case DLC_CMD_RNR: + case DLC_CMD_REJ: + NbfProcessSFrame (Command, PollFinal, Link, SHeader, SHeader->Command); + break; + + default: + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: bad S-frame.\n"); + } + + } /* switch */ + + } // if U-frame or S-frame + + // + // If we reach here, the packet has been processed. If it needs + // to be copied, we will jump to HandleAtComplete. + // + + NbfDereferenceLinkMacro ("Done with Indicate frame", Link, LREF_TREE); + LEAVE_NBF; + return; + +HandleAtComplete:; + + // + // At this point we DO NOT have any link references added in + // this function. + // + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive packets.\n"); + DeviceContext->ReceivePacketExhausted++; + LEAVE_NBF; + return; + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceiveBufferPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + } else { + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive buffers.\n"); + DeviceContext->ReceiveBufferExhausted++; + LEAVE_NBF; + return; + } + + NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize); + NdisChainBufferAtFront (NdisPacket, BufferTag->NdisBuffer); + + // + // DatagramAddress has a reference of type AREF_PROCESS_DATAGRAM, + // unless this is a datagram intended for RAS only, in which case + // it is NULL. + // + + BufferTag->Address = DatagramAddress; + + // + // set up async return status so we can tell when it has happened; + // can never get return of NDIS_STATUS_PENDING in synch completion routine + // for NdisTransferData, so we know it has completed when this status + // changes + // + + BufferTag->NdisStatus = NDIS_STATUS_PENDING; + ReceiveTag->PacketType = TYPE_AT_COMPLETE; + + ExInterlockedInsertTailList( + &DeviceContext->ReceiveInProgress, + &BufferTag->Linkage, + &DeviceContext->SpinLock); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Packet on Queue: %lx\n",NdisPacket); + } + + // + // receive packet is mapped at initalize + // + + // + // Determine how to handle the data transfer. + // + + if (Loopback) { + + NbfTransferLoopbackData( + &NdisStatus, + DeviceContext, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred + ); + + } else { + + NdisTransferData ( + &NdisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred); + + } + + // + // handle the various error codes + // + + switch (NdisStatus) { + case NDIS_STATUS_SUCCESS: // received packet + BufferTag->NdisStatus = NDIS_STATUS_SUCCESS; + + if (BytesTransferred == PacketSize) { // Did we get the entire packet? + ReceiveTag->PacketType = TYPE_AT_INDICATE; + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + LEAVE_NBF; + return; + } + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 ("NbfReceiveIndicate: Discarding Packet, Partial transfer: 0x0%lx of 0x0%lx transferred\n", + BytesTransferred, PacketSize); + } + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + LEAVE_NBF; + return; + break; + + default: // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status... + break; + } + + // + // receive failed, for some reason; cleanup and fail return + // BUGBUG: Statistics go here + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Discarding Packet, transfer failed: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + RemoveEntryList (&BufferTag->Linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD ( + BufferPointer, + BUFFER_TAG, + Buffer[0] + ); + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value + + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + if (DatagramAddress) { + NbfDereferenceAddress ("DG TransferData failed", DatagramAddress, AREF_PROCESS_DATAGRAM); + } + + LEAVE_NBF; + return; + +} /* NbfGeneralReceiveHandler */ + + + +VOID +NbfTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to start stripping buffers from the receive queue. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + PRECEIVE_PACKET_TAG ReceiveTag; + PTP_CONNECTION Connection; + KIRQL oldirql1; + PTP_REQUEST Request; + PNDIS_BUFFER NdisBuffer; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + + // + // Put the NDIS status into a place we can use in packet processing. + // Note that this complete indication may be occuring during the call + // to NdisTransferData in the receive indication. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfTransferDataComplete: Entered, Packet: %lx bytes transferred: 0x0%x\n", + NdisPacket, BytesTransferred); + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + // + // note that the processing below depends on having only one packet + // transfer outstanding at a time. NDIS is supposed to guarentee this. + // + + switch (ReceiveTag->PacketType) { + + case TYPE_AT_COMPLETE: // datagrams + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]); + BufferTag->NdisStatus = NdisStatus; + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + case TYPE_AT_INDICATE: // I-frames + + // + // The transfer for this packet is complete. Was it successful?? + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + Connection = ReceiveTag->Connection; + + // + // rip all of the NDIS_BUFFERs we've used off the chain and return them. + // + + if (ReceiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + } + } else { + NdisReinitializePacket (NdisPacket); + } + + + if ((NdisStatus != NDIS_STATUS_SUCCESS) || + (!DeviceContext->MacInfo.SingleReceive)) { + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ULONG DumpData[2]; + DumpData[0] = BytesTransferred; + DumpData[1] = ReceiveTag->BytesToTransfer; + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 603, + NdisStatus, + NULL, + 2, + DumpData); + + // Drop the packet. +#if DBG + NbfPrint1 ("NbfTransferDataComplete: status %s\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_TRANSFER_FAIL; + + } else { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + Connection->TransferBytesPending -= ReceiveTag->BytesToTransfer; + + if ((Connection->TransferBytesPending == 0) && + (Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) { + + Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl; + Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset; + Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending; + Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + ReceiveTag->CompleteReceive = FALSE; + + } else { + + // + // BUGBUG: If we have more data outstanding, this can't + // be the last piece; i.e. we can't handle having + // the last piece complete asynchronously before + // an earlier piece. + // +#if DBG + if (Connection->TransferBytesPending > 0) { + ASSERT (!ReceiveTag->CompleteReceive); + } +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + + // + // dereference the connection to say we've done the I frame processing. + // This reference was done before calling NdisTransferData. + // + + if (ReceiveTag->TransferDataPended) { + NbfDereferenceConnection("TransferData done", Connection, CREF_TRANSFER_DATA); + } + + + } else { + + ASSERT (NdisStatus == STATUS_SUCCESS); + ASSERT (!ReceiveTag->TransferDataPended); + ASSERT (Connection->CurrentReceiveSynchronous); + + if (!Connection->SpecialReceiveIrp) { + Connection->CurrentReceiveIrp->IoStatus.Information += BytesTransferred; + } + + } + + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (ReceiveTag->CompleteReceive) { + CompleteReceive (Connection, ReceiveTag->EndOfMessage, (ULONG)BytesTransferred); + } + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + KeLowerIrql (oldirql1); + + break; + + case TYPE_STATUS_RESPONSE: // response to remote adapter status + + // + // BUGBUG: Handle failure. + // + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DbgPrint ("NBF: STATUS_RESPONSE TransferData failed\n"); + } +#endif + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ASSERT (NdisBuffer); + NdisFreeBuffer (NdisBuffer); + + Request = (PTP_REQUEST)ReceiveTag->Connection; + + if (ReceiveTag->CompleteReceive) { + NbfCompleteRequest( + Request, + ReceiveTag->EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + Request->BytesWritten); + } + + NbfDereferenceRequest("Status xfer done", Request, RREF_STATUS); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + default: +#if DBG + NbfPrint1 ("NbfTransferDataComplete: Bang! Packet Transfer failed, unknown packet type: %ld\n", + ReceiveTag->PacketType); + DbgBreakPoint (); +#endif + break; + } + + // + // BUGBUG: Statistics need to be kept here. + // + + return; + +} // NbfTransferDataComplete + + + +VOID +NbfReceiveComplete ( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + Nbf uses the DeviceContext for this parameter. + +Return Value: + + None + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + UINT i; + NTSTATUS Status; + KIRQL oldirql2; + BOOLEAN Command; + PDLC_U_FRAME UHeader; + PDLC_FRAME DlcHeader; + PLIST_ENTRY linkage; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + PTP_ADDRESS Address; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_LINK Link; + + ENTER_NBF; + + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Entered.\n"); + } + + DeviceContext = (PDEVICE_CONTEXT) BindingContext; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql2); + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) { + + linkage = ExInterlockedRemoveHeadList( + &DeviceContext->IrpCompletionQueue, + &DeviceContext->Interlock); + + if (linkage != NULL) { + + Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + Connection = IRP_RECEIVE_CONNECTION(IrpSp); + + if (Connection == NULL) { +#if DBG + DbgPrint ("Connection of Irp %lx is NULL\n", Irp); + DbgBreakPoint(); +#endif + } + + IRP_RECEIVE_REFCOUNT(IrpSp) = 0; + IRP_RECEIVE_IRP (IrpSp) = NULL; + + LEAVE_NBF; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (Connection->Flags & CONNECTION_FLAGS_RC_PENDING) { + + Connection->Flags &= ~CONNECTION_FLAGS_RC_PENDING; + + if (Connection->Flags & CONNECTION_FLAGS_PEND_INDICATE) { + + Connection->Flags &= ~CONNECTION_FLAGS_PEND_INDICATE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // We got an indicate (and sent a NO_RECEIVE) while + // this was in progress, so send the receive outstanding + // now. + // + + NbfSendReceiveOutstanding (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + NbfDereferenceConnectionMacro ("receive completed", Connection, CREF_RECEIVE_IRP); + + } else { + + // + // ExInterlockedRemoveHeadList returned NULL, so don't + // bother looping back. + // + + break; + + } + + } + + + // + // Packetize all waiting connections + // + + if (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + PacketizeConnections (DeviceContext); + + } + + if (!IsListEmpty (&DeviceContext->DeferredRrQueue)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + while (!IsListEmpty(&DeviceContext->DeferredRrQueue)) { + + linkage = RemoveHeadList (&DeviceContext->DeferredRrQueue); + + Link = CONTAINING_RECORD (linkage, TP_LINK, DeferredRrLinkage); + + Link->OnDeferredRrQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, FALSE); // releases lock + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + + // + // Get every waiting packet, in order... + // + + + if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress); + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: Processing Buffer %lx\n", BufferTag); + } + + // + // NdisTransferData may have failed at async completion; check and + // see. If it did, then we discard this packet. If we're still waiting + // for transfer to complete, go back to sleep and hope (no guarantee!) + // we get waken up later. + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: NdisStatus: %s.\n", + NbfGetNdisStatus(BufferTag->NdisStatus)); + } +#endif + if (BufferTag->NdisStatus == NDIS_STATUS_PENDING) { + InsertHeadList (&DeviceContext->ReceiveInProgress, linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Status pending, returning packet to queue.\n"); + } + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (BufferTag->NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 (" NbfReceiveComplete: Failed return from Transfer data, reason: %s\n", + NbfGetNdisStatus (BufferTag->NdisStatus)); +#endif + // BUGBUG: some statistics go here for lost packet + + goto FreeBuffer; // skip the buffer, continue with while loop + } + + // + // Have a buffer. Since I allocated the storage for it, I know it's + // virtually contiguous and can treat it that way, which I will + // henceforth. + // + + NdisQueryBuffer (BufferTag->NdisBuffer, &BufferPointer, &NdisBufferLength); + + IF_NBFDBG (NBF_DEBUG_DLC) { + PUCHAR pc; + NbfPrint0 (" NbfRC Packet: \n "); + pc = (PUCHAR)BufferPointer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",pc[i]); + } + NbfPrint0 ("\n"); + } + + // + // Determine what address this is for, which is stored + // in the buffer tag header. + // + + Address = BufferTag->Address; + + // + // Process the frame as a UI frame; only datagrams should + // be processed here. If Address is NULL then this datagram + // is not needed for any bound address and should be given + // to RAS only. + // + + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Delivering this frame manually.\n"); + } + DlcHeader = (PDLC_FRAME)BufferPointer; + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + UHeader = (PDLC_U_FRAME)DlcHeader; + + BufferPointer = (PUCHAR)BufferPointer + 3; + NdisBufferLength -= 3; // 3 less bytes. + + if (Address != NULL) { + + // + // Indicate it or complete posted datagrams. + // + + Status = NbfIndicateDatagram ( + DeviceContext, + Address, + BufferPointer, // the Dsdu, with some tweaking + NdisBufferLength); + + // + // Dereference the address. + // + + NbfDereferenceAddress ("Datagram done", Address, AREF_PROCESS_DATAGRAM); + + } + + // + // Let the RAS clients have a crack at this if they want + // (they only want directed datagrams, not broadcast). + // + + if ((((PNBF_HDR_CONNECTIONLESS)BufferPointer)->Command == NBF_CMD_DATAGRAM) && + (DeviceContext->IndicationQueuesInUse)) { + + NbfActionDatagramIndication( + DeviceContext, + (PNBF_HDR_CONNECTIONLESS)BufferPointer, + NdisBufferLength); + + } + + BufferPointer = (PUCHAR)BufferPointer - 3; + NdisBufferLength += 3; // 3 more bytes. + + // + // Finished with buffer; return to pool. + // + +FreeBuffer:; + + NdisAdjustBufferLength (BufferTag->NdisBuffer, BufferTag->Length); + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } // if queue not empty + + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + +} /* NbfReceiveComplete */ + + +VOID +NbfTransferLoopbackData ( + OUT PNDIS_STATUS NdisStatus, + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called instead of NdisTransferData for + loopback indications. It copies the data from the + source packet to the receive packet. + +Arguments: + + NdisStatus - Returns the status of the operation. + + DeviceContext - The device context. + + ReceiveContext - A pointer to the source packet. + + ByteOffset - The offset from the start of the source packet + that the transfer should begin at. + + BytesToTransfer - The number of bytes to transfer. + + Packet - A pointer to the receive packet. + + BytesTransferred - Returns the number of bytes copied. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET SourcePacket = (PNDIS_PACKET)ReceiveContext; + + NdisCopyFromPacketToPacket( + Packet, + 0, + BytesToTransfer, + SourcePacket, + DeviceContext->LoopbackHeaderLength + ByteOffset, + BytesTransferred + ); + + *NdisStatus = NDIS_STATUS_SUCCESS; // BUGBUG: what if BytesTransferred is too small + +} + + +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ) + +/*++ + +Routine Description: + + Copy from an ndis packet into a buffer. + +Arguments: + + Packet - The packet to copy from. + + Offset - The offset from which to start the copy. + + BytesToCopy - The number of bytes to copy from the packet. + + Buffer - The destination of the copy. + + BytesCopied - The number of bytes actually copied. Can be less then + BytesToCopy if the packet is shorter than BytesToCopy. + +Return Value: + + None + +--*/ + +{ + + // + // Holds the number of ndis buffers comprising the packet. + // + UINT NdisBufferCount; + + // + // Points to the buffer from which we are extracting data. + // + PNDIS_BUFFER CurrentBuffer; + + // + // Holds the virtual address of the current buffer. + // + PVOID VirtualAddress; + + // + // Holds the length of the current buffer of the packet. + // + UINT CurrentLength; + + // + // Keep a local variable of BytesCopied so we aren't referencing + // through a pointer. + // + UINT LocalBytesCopied = 0; + + // + // Take care of boundary condition of zero length copy. + // + + *BytesCopied = 0; + if (!BytesToCopy) return; + + // + // Get the first buffer. + // + + NdisQueryPacket( + Packet, + NULL, + &NdisBufferCount, + &CurrentBuffer, + NULL + ); + + // + // Could have a null packet. + // + + if (!NdisBufferCount) return; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + + while (LocalBytesCopied < BytesToCopy) { + + if (!CurrentLength) { + + NdisGetNextBuffer( + CurrentBuffer, + &CurrentBuffer + ); + + // + // We've reached the end of the packet. We return + // with what we've done so far. (Which must be shorter + // than requested. + // + + if (!CurrentBuffer) break; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + continue; + + } + + // + // Try to get us up to the point to start the copy. + // + + if (Offset) { + + if (Offset > CurrentLength) { + + // + // What we want isn't in this buffer. + // + + Offset -= CurrentLength; + CurrentLength = 0; + continue; + + } else { + + VirtualAddress = (PCHAR)VirtualAddress + Offset; + CurrentLength -= Offset; + Offset = 0; + + } + + } + + // + // Copy the data. + // + + + { + + // + // Holds the amount of data to move. + // + UINT AmountToMove; + + AmountToMove = + ((CurrentLength <= (BytesToCopy - LocalBytesCopied))? + (CurrentLength):(BytesToCopy - LocalBytesCopied)); + + RtlCopyMemory( + Buffer, + VirtualAddress, + AmountToMove + ); + + Buffer = (PCHAR)Buffer + AmountToMove; + VirtualAddress = (PCHAR)VirtualAddress + AmountToMove; + + LocalBytesCopied += AmountToMove; + CurrentLength -= AmountToMove; + + } + + } + + *BytesCopied = LocalBytesCopied; + +} + diff --git a/private/ntos/tdi/nbf/event.c b/private/ntos/tdi/nbf/event.c new file mode 100644 index 000000000..6cb072d8a --- /dev/null +++ b/private/ntos/tdi/nbf/event.c @@ -0,0 +1,190 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiSetEventHandler( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Irp - Pointer to the IRP for this request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS rc=STATUS_SUCCESS; + KIRQL oldirql; + PTDI_REQUEST_KERNEL_SET_EVENT parameters; + PIO_STACK_LOCATION irpSp; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + NTSTATUS status; + + // + // Get the Address this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + addressFile = irpSp->FileObject->FsContext; + status = NbfVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)&irpSp->Parameters; + + switch (parameters->EventType) { + + case TDI_EVENT_RECEIVE: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + } else { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)parameters->EventHandler; + addressFile->ReceiveHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_EXPEDITED: + + if (parameters->EventHandler == NULL) { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + } else { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)parameters->EventHandler; + addressFile->ExpeditedDataHandlerContext = parameters->EventContext; + addressFile->RegisteredExpeditedDataHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)parameters->EventHandler; + addressFile->ReceiveDatagramHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (parameters->EventHandler == NULL) { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + } else { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)parameters->EventHandler; + addressFile->ErrorHandlerContext = parameters->EventContext; + addressFile->RegisteredErrorHandler = TRUE; + } + + break; + + case TDI_EVENT_DISCONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + } else { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)parameters->EventHandler; + addressFile->DisconnectHandlerContext = parameters->EventContext; + addressFile->RegisteredDisconnectHandler = TRUE; + } + + break; + + case TDI_EVENT_CONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredConnectionHandler = FALSE; + } else { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)parameters->EventHandler; + addressFile->ConnectionHandlerContext = parameters->EventContext; + addressFile->RegisteredConnectionHandler = TRUE; + } + break; + + default: + + rc = STATUS_INVALID_PARAMETER; + + } /* switch */ + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + NbfDereferenceAddress ("Set event handler", address, AREF_VERIFY); + + return rc; +} /* TdiSetEventHandler */ diff --git a/private/ntos/tdi/nbf/framecon.c b/private/ntos/tdi/nbf/framecon.c new file mode 100644 index 000000000..6905d9668 --- /dev/null +++ b/private/ntos/tdi/nbf/framecon.c @@ -0,0 +1,1087 @@ + +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + framecon.c + +Abstract: + + This module contains routines which build NetBIOS Frames Protocol frames, + both connection-oriented and connectionless. The following frames are + constructed by routines in this module: + + o NBF_CMD_ADD_GROUP_NAME_QUERY + o NBF_CMD_ADD_NAME_QUERY + o NBF_CMD_NAME_IN_CONFLICT + o NBF_CMD_STATUS_QUERY + o NBF_CMD_TERMINATE_TRACE + o NBF_CMD_DATAGRAM + o NBF_CMD_DATAGRAM_BROADCAST + o NBF_CMD_NAME_QUERY + o NBF_CMD_ADD_NAME_RESPONSE + o NBF_CMD_NAME_RECOGNIZED + o NBF_CMD_STATUS_RESPONSE + o NBF_CMD_TERMINATE_TRACE2 + o NBF_CMD_DATA_ACK + o NBF_CMD_DATA_FIRST_MIDDLE + o NBF_CMD_DATA_ONLY_LAST + o NBF_CMD_SESSION_CONFIRM + o NBF_CMD_SESSION_END + o NBF_CMD_SESSION_INITIALIZE + o NBF_CMD_NO_RECEIVE + o NBF_CMD_RECEIVE_OUTSTANDING + o NBF_CMD_RECEIVE_CONTINUE + o NBF_CMD_SESSION_ALIVE + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +VOID +ConstructAddGroupNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME GroupName // NetBIOS group name to be added. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_GROUP_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Correlator - Correlator for ADD_NAME_RESPONSE frame. + + GroupName - Pointer to NetBIOS group name to be added. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddGroupNameQuery: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_ADD_GROUP_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = 0; + RawFrame->SourceName [i] = GroupName [i]; + } +} /* ConstructAddGroupNameQuery */ + + +VOID +ConstructAddNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME Name // NetBIOS name to be added. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Correlator - Correlator for ADD_NAME_RESPONSE frame. + + Name - Pointer to NetBIOS name to be added. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddNameQuery: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_ADD_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = 0; + RawFrame->SourceName [i] = Name [i]; + } +} /* ConstructAddNameQuery */ + + +VOID +ConstructNameInConflict( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ConflictingName, // NetBIOS name that is conflicting. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_IN_CONFLICT connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Conflictingname - Pointer to NetBIOS name that is conflicting. + + SendingPermanentName - Pointer to NetBIOS permanent node name of sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNameInConflict: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_NAME_IN_CONFLICT; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = ConflictingName[i]; + RawFrame->SourceName [i] = SendingPermanentName[i]; + } + +} /* ConstructNameInConflict */ + + +VOID +ConstructStatusQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request. + IN USHORT BufferLength, // length of user's status buffer. + IN USHORT Correlator, // correlator for STATUS_RESPONSE. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_STATUS_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + RequestType - Type of request. One of: + 0 - request is 1.x or 2.0. + 1 - first request, 2.1 or above. + >1 - subsequent request, 2.1 or above. + + BufferLength - Length of user's status buffer. + + Correlator - Correlator for STATUS_RESPONSE frame. + + ReceiverName - Pointer to NetBIOS name of receiver. + + SendingPermanentName - Pointer to NetBIOS permanent node name of sender. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint1 ("ConstructStatusQuery: Entered, frame: %lx\n", RawFrame); + } + + RawFrame->Command = NBF_CMD_STATUS_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = RequestType; + RawFrame->Data2Low = (UCHAR)(BufferLength & 0xff); + RawFrame->Data2High = (UCHAR)(BufferLength >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; iDestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SendingPermanentName [i]; + } + +} /* ConstructStatusQuery */ + + +VOID +ConstructDatagram( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATAGRAM connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDatagram: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATAGRAM; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructDatagram */ + + +VOID +ConstructDatagramBroadcast( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATAGRAM_BROADCAST connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDatagramBroadcast: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATAGRAM_BROADCAST; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = 0; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructDatagramBroadcast */ + + +VOID +ConstructNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session (0=FIND_NAME). + IN USHORT Correlator, // correlator in NAME_RECOGNIZED. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, one of the following: + NAME_QUERY_LSN_FIND_NAME + + LocalSessionNumber - LSN assigned to session (0=FIND.NAME). + + Correlator - Correlator in NAME_RECOGNIZED. + + SenderName - Pointer to a NetBIOS name of the sender. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint1 ("ConstructNameQuery: Entered, frame: %lx\n", RawFrame); + } + + RawFrame->Command = NBF_CMD_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = LocalSessionNumber; + RawFrame->Data2High = NameType; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; iDestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructNameQuery */ + + +VOID +ConstructAddNameResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN USHORT Correlator, // correlator from ADD_[GROUP_]NAME_QUERY. + IN PNAME Name // NetBIOS name being responded to. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_NAME_RESPONSE connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, either group or unique. + + Correlator - Correlator from ADD_[GROUP]NAME_QUERY. + + Name - Pointer to NetBIOS name being responded to. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddNameResponse: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_ADD_NAME_RESPONSE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = NameType; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; iDestinationName [i] = Name [i]; + RawFrame->SourceName [i] = Name [i]; + } +} /* ConstructAddNameResponse */ + + +VOID +ConstructNameRecognized( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN USHORT NameQueryCorrelator, // correlator from NAME_QUERY. + IN USHORT Correlator, // correlator expected from next response. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_RECOGNIZED connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, either group or unique. + + LocalSessionNumber - LSN assigned to session. Special values are: + NAME_RECOGNIZED_LSN_NO_LISTENS // no listens available. + NAME_RECOGNIZED_LSN_FIND_NAME // this is a find name response. + NAME_RECOGNIZED_LSN_NO_RESOURCE // listen available, but no resources. + + NameQueryCorrelator - Correlator from NAME_QUERY. + + Correlator - Correlator expected from next response. + + SenderName - Pointer to a NetBIOS name of the sender. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNameRecognized: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_NAME_RECOGNIZED; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = LocalSessionNumber; + RawFrame->Data2High = NameType; + TRANSMIT_CORR(RawFrame) = NameQueryCorrelator; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; iDestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructNameRecognized */ + + +VOID +ConstructStatusResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN BOOLEAN Truncated, // data is truncated. + IN BOOLEAN DataOverflow, // too much data for user's buffer. + IN USHORT DataLength, // length of data sent. + IN USHORT Correlator, // correlator from STATUS_QUERY. + IN PNAME ReceivingPermanentName, // NetBIOS permanent node name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_STATUS_RESPONSE connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + RequestType - type of request, one of the below: + 0 - request is 1.x or 2.0. + >0 - number of names, 2.1 or above. + + Truncated - TRUE if there are more names. + + DataOverflow - TRUE if the total data is larger than the user's buffer. + + DataLength - The length of the data in Buffer. + + Correlator - Correlator from STATUS_QUERY. + + ReceivingPermanentName - Pointer to the NetBIOS permanent node name of the receiver, + as passed in the STATUS_QUERY frame. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructStatusResponse: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_STATUS_RESPONSE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = RequestType; + RawFrame->Data2Low = (UCHAR)(DataLength & 0xff); + RawFrame->Data2High = (UCHAR)((DataLength >> 8) + + (Truncated << 7) + + (DataOverflow << 6)); + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = 0; + for (i=0; iDestinationName [i] = ReceivingPermanentName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } + +} /* ConstructStatusResponse */ + + +VOID +ConstructDataAck( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_ONLY_LAST. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATA_ACK connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Correlator - Correlator from DATA_ONLY_LAST being acked. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDataAck: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATA_ACK; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructDataAck */ + + +VOID +ConstructDataOnlyLast( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN BOOLEAN Resynched, // TRUE if we are resynching. + IN USHORT Correlator, // correlator for RECEIVE_CONTINUE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATA_ONLY_LAST connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Resynched - TRUE if we are resynching and should set the + correct bit in the frame. + + Correlator - Correlator for RECEIVE_CONTINUE, if any. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDataOnlyLast: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATA_ONLY_LAST; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + ASSERT (TRUE == (UCHAR)1); + RawFrame->Data2Low = Resynched; + RawFrame->Data2High = (UCHAR)0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructDataOnlyLast */ + + +VOID +ConstructSessionConfirm( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT Correlator, // correlator from SESSION_INITIALIZE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_CONFIRM connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + SESSION_CONFIRM_OPTIONS_20 // set if NETBIOS 2.0 or above. + SESSION_CONFIRM_NO_ACK // set if NO.ACK protocol supported. + + MaximumUserBufferSize - Maximum size of user data per frame on this + session, in bytes. This is limited by the following constant: + SESSION_CONFIRM_MAXIMUM_FRAME_SIZE // defined limit of this field. + + Correlator - Correlator from SESSION_INITIALIZE. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionConfirm: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_CONFIRM; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = Options; + RawFrame->Data2Low = (UCHAR)(MaximumUserBufferSize & 0xff); + RawFrame->Data2High = (UCHAR)(MaximumUserBufferSize >> 8); + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionConfirm */ + + +VOID +ConstructSessionEnd( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Reason, // reason for termination, defined below. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_END connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Reason - Reason code for termination, any of the following: + SESSION_END_REASON_HANGUP // normal termination via HANGUP. + SESSION_END_REASON_ABEND // abnormal session termination. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionEnd: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_END; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = (UCHAR)(Reason & 0xff); + RawFrame->Data2High = (UCHAR)(Reason >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionEnd */ + + +VOID +ConstructSessionInitialize( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT NameRecognizedCorrelator, // correlator from NAME_RECOGNIZED. + IN USHORT Correlator, // correlator for SESSION_CONFIRM. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_INITIALIZE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + SESSION_INITIALIZE_OPTIONS_20 // set if NETBIOS 2.0 or above. + SESSION_INITIALIZE_NO_ACK // set if NO.ACK protocol supported. + + MaximumUserBufferSize - Maximum size of user data per frame on this + session, in bytes. This is limited by the following constant: + SESSION_INITIALIZE_MAXIMUM_FRAME_SIZE // defined limit of this field. + + NameRecognizedCorrelator - Correlator from NAME_RECOGNIZED. + + Correlator - Correlator for SESSION_CONFIRM. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionInitialize: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_INITIALIZE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = Options; + RawFrame->Data2Low = (UCHAR)(MaximumUserBufferSize & 0xff); + RawFrame->Data2High = (UCHAR)(MaximumUserBufferSize >> 8); + TRANSMIT_CORR(RawFrame) = NameRecognizedCorrelator; + RESPONSE_CORR(RawFrame) = Correlator; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionInitialize */ + + +VOID +ConstructNoReceive( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Options, // option bitflags, defined below. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NO_RECEIVE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + NO_RECEIVE_OPTIONS_PARTIAL_NO_ACK // NO.ACK data partially received. + + BytesAccepted - Number of bytes accepted, current outstanding message. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ +// Options; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNoReceive: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_NO_RECEIVE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + if (Options == NO_RECEIVE_PARTIAL_NO_ACK) { + RawFrame->Data1 = NO_RECEIVE_PARTIAL_NO_ACK; + } else { + RawFrame->Data1 = 0; + } + RawFrame->Data2Low = (UCHAR)(BytesAccepted & 0xff); + RawFrame->Data2High = (UCHAR)(BytesAccepted >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructNoReceive */ + + +VOID +ConstructReceiveOutstanding( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_RECEIVE_OUTSTANDING connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + BytesAccepted - Number of bytes accepted, current outstanding message. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructReceiveOutstanding: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_RECEIVE_OUTSTANDING; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = (UCHAR)(BytesAccepted & 0xff); + RawFrame->Data2High = (UCHAR)(BytesAccepted >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructReceiveOutstanding */ + + +VOID +ConstructReceiveContinue( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_FIRST_MIDDLE + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_RECEIVE_CONTINUE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Correlator - The correlator from the DATA_FIRST_MIDDLE frame. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructReceiveContinue: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_RECEIVE_CONTINUE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructReceiveContinue */ + +#if 0 + +VOID +ConstructSessionAlive( + IN PNBF_HDR_CONNECTION RawFrame // frame buffer to format. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_ALIVE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionAlive: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_ALIVE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = 0; + RawFrame->DestinationSessionNumber = 0; +} /* ConstructSessionAlive */ + +#endif diff --git a/private/ntos/tdi/nbf/framesnd.c b/private/ntos/tdi/nbf/framesnd.c new file mode 100644 index 000000000..e4c356d99 --- /dev/null +++ b/private/ntos/tdi/nbf/framesnd.c @@ -0,0 +1,2504 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + framesnd.c + +Abstract: + + This module contains routines which build and send NetBIOS Frames Protocol + frames and data link frames for other modules. These routines call on the + ones in FRAMECON.C to do the construction work. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +ULONG NbfSendsIssued = 0; +ULONG NbfSendsCompletedInline = 0; +ULONG NbfSendsCompletedOk = 0; +ULONG NbfSendsCompletedFail = 0; +ULONG NbfSendsPended = 0; +ULONG NbfSendsCompletedAfterPendOk = 0; +ULONG NbfSendsCompletedAfterPendFail = 0; + +ULONG NbfPacketPanic = 0; +#endif + + +NTSTATUS +NbfSendAddNameQuery( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine sends a ADD_NAME_QUERY frame to register the specified + address. + +Arguments: + + Address - Pointer to a transport address object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint3 ("NbfSendAddNameQuery: Sending Frame: %lx, NdisPacket: %lx MacHeader: %lx\n", + RawFrame, RawFrame->NdisPacket, RawFrame->Header); + } + + + // + // Build the MAC header. ADD_NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the appropriate Netbios header based on the type + // of the address. + // + + if ((Address->Flags & ADDRESS_FLAGS_GROUP) != 0) { + + ConstructAddGroupNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + 0, // correlator we don't use. + Address->NetworkName->NetbiosName); + + } else { + + ConstructAddNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + 0, // correlator we don't use. + Address->NetworkName->NetbiosName); + + } + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame). + + return STATUS_SUCCESS; +} /* NbfSendAddNameQuery */ + + +VOID +NbfSendNameQuery( + IN PTP_CONNECTION Connection, + IN BOOLEAN SourceRoutingOptional + ) + +/*++ + +Routine Description: + + This routine sends a NAME_QUERY frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + SourceRoutingOptional - TRUE if source routing should be removed if + we are configured that way. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_ADDRESS Address; + PTP_UI_FRAME RawFrame; + PUCHAR NameQuerySR; + UINT NameQuerySRLength; + PUCHAR NameQueryAddress; + UINT HeaderLength; + UCHAR Lsn; + UCHAR NameType; + + Address = Connection->AddressFile->Address; + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame(DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameQuery: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. + // + + if (((Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) != 0) && + ((Connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) == 0)) { + + // + // This is the second find name to a unique name; this + // means that we already have a link and we can send this + // frame directed to it. + // + + ASSERT (Connection->Link != NULL); + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + Connection->Link->Header, + &NameQuerySR, + &NameQuerySRLength); + + NameQueryAddress = Connection->Link->HardwareAddress.Address; + + } else { + + // + // Standard NAME_QUERY frames go out as + // single-route source routing, except if + // it is optional and we are configured + // that way. + // + + if (SourceRoutingOptional && + Connection->Provider->MacInfo.QueryWithoutSourceRouting) { + + NameQuerySR = NULL; + NameQuerySRLength = 0; + + } else { + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &NameQuerySR, + &NameQuerySRLength); + + } + + NameQueryAddress = DeviceContext->NetBIOSAddress.Address; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + NameQueryAddress, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + NameQuerySR, + NameQuerySRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + Lsn = (UCHAR)((Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN) ? + NAME_QUERY_LSN_FIND_NAME : Connection->Lsn); + + NameType = (UCHAR)((Connection->AddressFile->Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + ConstructNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of our name. + Lsn, // calculated, above. + (USHORT)Connection->ConnectionId, // corr. in 1st NAME_RECOGNIZED. + Address->NetworkName->NetbiosName, // NetBIOS name of sender. + Connection->CalledAddress.NetbiosName); // NetBIOS name of receiver. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameQuery */ + + +VOID +NbfSendNameRecognized( + IN PTP_ADDRESS Address, + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine sends a NAME_RECOGNIZED frame of the appropriate type + in response to the NAME_QUERY pointed to by Header. + +Arguments: + + Address - Pointer to a transport address object. + + LocalSessionNumber - The LSN to use in the frame. + + Header - Pointer to the connectionless NetBIOS header of the + NAME_QUERY frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRoutingInformation - Pointer to source routing information, if any. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + UINT HeaderLength; + PUCHAR ReplySR; + UINT ReplySRLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + UCHAR NameType; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameRecognized: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. NAME_RECOGNIZED frames go out as + // directed source routing unless configured for general-route. + // + + if (DeviceContext->MacInfo.AllRoutesNameRecognized) { + + MacReturnGeneralRouteSR( + &DeviceContext->MacInfo, + &ReplySR, + &ReplySRLength); + + } else { + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ReplySR); + + ReplySRLength = SourceRoutingLength; + + } else { + + ReplySR = NULL; + } + } + + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ReplySR, + ReplySRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + NameType = (UCHAR)((Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + ConstructNameRecognized ( // build a good response. + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of local name. + LocalSessionNumber, // return our LSN. + RESPONSE_CORR(Header), // new xmit corr. + 0, // our response correlator (unused). + Header->DestinationName, // our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + // + // BUGBUG: Use Address->NetworkName->Address[0].Address[0].NetbiosName + // instead of Header->DestinationName? + // + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameRecognized */ + + +VOID +NbfSendNameInConflict( + IN PTP_ADDRESS Address, + IN PUCHAR ConflictingName + ) + +/*++ + +Routine Description: + + This routine sends a NAME_IN_CONFLICT frame. + +Arguments: + + Address - Pointer to a transport address object. + + ConflictingName - The NetBIOS name which is in conflict. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + UINT HeaderLength; + PUCHAR SingleSR; + UINT SingleSRLength; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameRecognized: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. ADD_NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructNameInConflict ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + ConflictingName, // his NetBIOS name. + DeviceContext->ReservedNetBIOSAddress); // our reserved NetBIOS name. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameInConflict */ + + +VOID +NbfSendSessionInitialize( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_INITIALIZE frame on the specified connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session Initialize", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionInitialize: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SI); + NbfDereferenceConnection("Couldn't get SI packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructSessionInitialize ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + SESSION_INIT_OPTIONS_20 | SESSION_INIT_NO_ACK | + SESSION_INIT_OPTIONS_LF, // supported options BUGBUG: Set LF correctly. + (USHORT)(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)), + // maximum frame size/this session. + Connection->NetbiosHeader.TransmitCorrelator, // correlator from NAME_RECOGNIZED. + 0, // correlator for expected SESSION_CONFIRM. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionInitialize */ + + +VOID +NbfSendSessionConfirm( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_CONFIRM frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session Confirm", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionConfirm: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SC); + NbfDereferenceConnection("Couldn't get SC packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructSessionConfirm ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + SESSION_CONFIRM_OPTIONS_20 | SESSION_CONFIRM_NO_ACK, // supported options. + (USHORT)(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)), + // maximum frame size/this session. + Connection->NetbiosHeader.TransmitCorrelator, // correlator from NAME_RECOGNIZED. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionConfirm */ + + +VOID +NbfSendSessionEnd( + IN PTP_CONNECTION Connection, + IN BOOLEAN Abort + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_END frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + Abort - Boolean set to TRUE if the connection is abnormally terminating. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session End", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionEnd: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SE); + NbfDereferenceConnection("Couldn't get SE packet", Connection, CREF_FRAME_SEND); + return; + } + + // + // The following statements instruct the packet destructor to run + // down this connection when the packet is acknowleged. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_END; + + + // + // Initialize the Netbios header. + // + + ConstructSessionEnd ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + (USHORT)(Abort ? // reason for termination. + SESSION_END_REASON_ABEND : + SESSION_END_REASON_HANGUP), + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + // Note that we force an ack for this packet, as we want to make sure we + // run down the connection and link correctly. + // + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, TRUE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionEnd */ + + +VOID +NbfSendNoReceive( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a NO_RECEIVE frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send No Receive", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendNoReceive: NbfCreatePacket failed.\n"); + } +#endif + NbfDereferenceConnection("Couldn't get NR packet", Connection, CREF_FRAME_SEND); + return; + } + + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + // + // Initialize the Netbios header. + // + + ConstructNoReceive ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + (USHORT)0, // options + MessageBytesToAck, // number of bytes accepted. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendNoReceive */ + + +VOID +NbfSendReceiveContinue( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a RECEIVE_CONTINUE frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send Receive Continue", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendReceiveContinue: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_RC); + NbfDereferenceConnection("Couldn't get RC packet", Connection, CREF_FRAME_SEND); + return; + } + + // + // Save this variable now since it is what we are implicitly ack'ing. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + + // + // Initialize the Netbios header. + // + + ConstructReceiveContinue ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + Connection->NetbiosHeader.TransmitCorrelator, // correlator from DFM + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendReceiveContinue */ + + +VOID +NbfSendReceiveOutstanding( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a RECEIVE_OUTSTANDING frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send Receive Outstanding", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendReceiveOutstanding: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_RO); + NbfDereferenceConnection("Couldn't get RO packet", Connection, CREF_FRAME_SEND); + return; + } + + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + // + // Initialize the Netbios header. + // + + ConstructReceiveOutstanding ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + MessageBytesToAck, // number of bytes accepted. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendReceiveOutstanding */ + + +VOID +NbfSendDataAck( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a DATA_ACK frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Data Ack", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDataAck: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_DA); + NbfDereferenceConnection("Couldn't get DA packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructDataAck ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + Connection->NetbiosHeader.TransmitCorrelator, // correlator from DATA_ONLY_LAST. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. Note that Data Ack will be + // seeing this condition frequently when send windows close after large + // sends. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendDataAck */ + + +VOID +NbfSendDm( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a DM-r/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC + LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC DM-r/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC | DLC_SSAP_RESPONSE; + DlcHeader->Command = (UCHAR)(DLC_CMD_DM | (PollFinal ? DLC_U_PF : 0)); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDm: packet not sent.\n"); + } +#endif + } +} /* NbfSendDm */ + + +VOID +NbfSendUa( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a UA-r/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC + LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + + OldIrql - The IRQL at which Link->SpinLock was acquired. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // U-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // Format LLC UA-r/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC | DLC_SSAP_RESPONSE; + DlcHeader->Command = (UCHAR)(DLC_CMD_UA | (PollFinal ? DLC_U_PF : 0)); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendUa: packet not sent.\n"); + } +#endif + } +} /* NbfSendUa */ + + +VOID +NbfSendSabme( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a SABME-c/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PLIST_ENTRY p; + PTP_PACKET RawFrame, packet; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC SABME-c/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Command = (UCHAR)(DLC_CMD_SABME | (PollFinal ? DLC_U_PF : 0)); + + // + // Set up so that T1 will be started when the send + // completes. + // + + if (PollFinal) { + if (Link->Provider->MacInfo.MediumAsync) { + RawFrame->NdisIFrameLength = Link->HeaderLength + sizeof(DLC_S_FRAME); + RawFrame->Link = Link; + NbfReferenceLink ("Sabme/p", Link, LREF_START_T1); + } else { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + } + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + // + // Reset the link state based on having sent this packet.. + // Note that a SABME can be sent under some conditions on an existing + // link. If it is, it means we want to reset this link to a known state. + // We'll do that; note that that involves ditching any packets outstanding + // on the link. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->NextSend = 0; + Link->LastAckReceived = 0; + Link->NextReceive = 0; // expect next packet to be sequence 0 + Link->NextReceive = 0; + Link->LastAckSent = 0; + Link->NextReceive = 0; + Link->LastAckSent = 0; + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } else { + if (PollFinal) { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSabme: packet not sent.\n"); + } +#endif + } +} /* NbfSendSabme */ + + +VOID +NbfSendDisc( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a DISC-c/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + KIRQL oldirql; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC DISC-c/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Command = (UCHAR)(DLC_CMD_DISC | (PollFinal ? DLC_U_PF : 0)); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDisc: packet not sent.\n"); + } +#endif + } + + KeLowerIrql (oldirql); + +} /* NbfSendDisc */ + + +VOID +NbfSendRr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a RR-x/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. THIS ROUTINE MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreateRrPacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + + // + // RawFrame->Action will be set to PACKET_ACTION_RR if + // NbfCreateRrPacket got a packet from the RrPacketPool + // and PACKET_ACTION_NULL if it got one from the regular + // pool. + // + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC RR-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_RR; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + // + // If this is a command frame (which will always be a + // poll with the current code) set up so that T1 will + // be started when the send completes. + // + + if (Command) { + if (Link->Provider->MacInfo.MediumAsync) { + RawFrame->NdisIFrameLength = Link->HeaderLength + sizeof(DLC_S_FRAME); + RawFrame->Link = Link; + NbfReferenceLink ("Rr/p", Link, LREF_START_T1); + } else { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + } + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spinlock. + // + + SendControlPacket (Link, RawFrame); + + } else { + if (Command) { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRr: packet not sent.\n"); + } +#endif + } +} /* NbfSendRr */ + +#if 0 + +// +// These functions are not currently called, so they are commented +// out. +// + + +VOID +NbfSendRnr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a RNR-x/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + KIRQL oldirql; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC RR-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_RNR; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRnr: packet not sent.\n"); + } +#endif + } + KeLowerIrql (oldirql); +} /* NbfSendRnr */ + + +VOID +NbfSendTest( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PMDL Psdu + ) + +/*++ + +Routine Description: + + This routine sends a TEST-x/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + + Psdu - Pointer to an MDL chain describing received TEST-c frame's storage. + +Return Value: + + none. + +--*/ + +{ + Link, Command, PollFinal, Psdu; // prevent compiler warnings + + PANIC ("NbfSendTest: Entered (BUGBUG).\n"); +} /* NbfSendTest */ + + +VOID +NbfSendFrmr( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a FRMR-r/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + Link, PollFinal; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendFrmr: Entered (BUGBUG).\n"); + } +} /* NbfSendFrmr */ + +#endif + + +VOID +NbfSendXid( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends an XID-x/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + Link, Command, PollFinal; // prevent compiler warnings + + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); + PANIC ("NbfSendXid: Entered (BUGBUG).\n"); +} /* NbfSendXid */ + + +VOID +NbfSendRej( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a REJ-x/x DLC frame on the specified link. + + NOTE: This function is called with Link->SpinLock held and + returns with it released. THIS MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendRej: Entered.\n"); + } + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC REJ-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_REJ; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRej: packet not sent.\n"); + } +#endif + } +} /* NbfSendRej */ + + +NTSTATUS +NbfCreateConnectionlessFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME *RawFrame + ) + +/*++ + +Routine Description: + + This routine allocates a connectionless frame (either from the local + device context pool or out of non-paged pool). + +Arguments: + + DeviceContext - Pointer to our device context to charge the frame to. + + RawFrame - Pointer to a place where we will return a pointer to the + allocated frame. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTP_UI_FRAME UIFrame; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfCreateConnectionlessFrame: Entered.\n"); + } + + // + // Make sure that structure padding hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTIONLESS) == 44); + + p = ExInterlockedRemoveHeadList ( + &DeviceContext->UIFramePool, + &DeviceContext->Interlock); + + if (p == NULL) { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfCreateConnectionlessFrame: PANIC! no more UI frames in pool!\n"); + } +#endif + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->UIFrameExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + UIFrame = (PTP_UI_FRAME) CONTAINING_RECORD (p, TP_UI_FRAME, Linkage); + + *RawFrame = UIFrame; + + return STATUS_SUCCESS; +} /* NbfCreateConnectionlessFrame */ + + +VOID +NbfDestroyConnectionlessFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME RawFrame + ) + +/*++ + +Routine Description: + + This routine destroys a connectionless frame by either returning it + to the device context's pool or to the system's non-paged pool. + +Arguments: + + DeviceContext - Pointer to our device context to return the frame to. + + RawFrame - Pointer to a frame to be returned. + +Return Value: + + none. + +--*/ + +{ + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfDestroyConnectionlessFrame: Entered.\n"); + } + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &HeaderBuffer); + + // data buffers get thrown away + + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &NdisBuffer); + } + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + + // + // If this UI frame has some transport-created data, + // free the buffer now. + // + + if (RawFrame->DataBuffer) { + ExFreePool (RawFrame->DataBuffer); + RawFrame->DataBuffer = NULL; + } + + NdisChainBufferAtFront (RawFrame->NdisPacket, HeaderBuffer); + + ExInterlockedInsertTailList ( + &DeviceContext->UIFramePool, + &RawFrame->Linkage, + &DeviceContext->Interlock); + +} /* NbfDestroyConnectionlessFrame */ + + +VOID +NbfSendUIFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME RawFrame, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine sends a connectionless frame by calling the physical + provider's Send service. When the request completes, or if the service + does not return successfully, then the frame is deallocated. + +Arguments: + + DeviceContext - Pointer to our device context. + + RawFrame - Pointer to a connectionless frame to be sent. + + Loopback - A boolean flag set to TRUE if the source hardware address + of the packet should be set to zeros. + + SourceRoutingInformation - Pointer to optional source routing information. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PUCHAR DestinationAddress; + + UNREFERENCED_PARAMETER(Loopback); + +#if DBG + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendUIFrame: Entered, RawFrame: %lx NdisPacket %lx\n", + RawFrame, RawFrame->NdisPacket); + DbgPrint ("NbfSendUIFrame: MacHeader: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x \n", + RawFrame->Header[0], + RawFrame->Header[1], + RawFrame->Header[2], + RawFrame->Header[3], + RawFrame->Header[4], + RawFrame->Header[5], + RawFrame->Header[6], + RawFrame->Header[7], + RawFrame->Header[8], + RawFrame->Header[9], + RawFrame->Header[10], + RawFrame->Header[11], + RawFrame->Header[12], + RawFrame->Header[13]); + } +#endif + + // + // Send the packet. + // + +#if DBG + NbfSendsIssued++; +#endif + + // + // Loopback will be FALSE for multicast frames or other + // frames that we know are not directly addressed to + // our hardware address. + // + + if (Loopback) { + + // + // See if this frame should be looped back. + // + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + RawFrame->Header, + &DestinationAddress); + + if (RtlEqualMemory( + DestinationAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + NbfInsertInLoopbackQueue( + DeviceContext, + RawFrame->NdisPacket, + LOOPBACK_UI_FRAME + ); + + NdisStatus = NDIS_STATUS_PENDING; + + goto NoNdisSend; + + } + + } + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + NdisSend ( + &NdisStatus, + (NDIS_HANDLE)DeviceContext->NdisBindingHandle, + RawFrame->NdisPacket); + +NoNdisSend: + + if (NdisStatus != NDIS_STATUS_PENDING) { + +#if DBG + if (NdisStatus == NDIS_STATUS_SUCCESS) { + NbfSendsCompletedOk++; + } else { + NbfSendsCompletedFail++; + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint1 ("NbfSendUIFrame: NdisSend failed, status other Pending or Complete: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + } +#endif + + NbfDestroyConnectionlessFrame (DeviceContext, RawFrame); + + } else { + +#if DBG + NbfSendsPended++; +#endif + } + +} /* NbfSendUIFrame */ + + +VOID +NbfSendUIMdlFrame( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine sends a connectionless frame by calling the NbfSendUIFrame. + It is intended that this routine be used for sending datagrams and + braodcast datagrams. + + The datagram to be sent is described in the NDIS packet contained + in the Address. When the send completes, the send completion handler + returns the NDIS buffer describing the datagram to the buffer pool and + marks the address ndis packet as usable again. Thus, all datagram and + UI frames are sequenced through the address they are sent on. + +Arguments: + + Address - pointer to the address from which to send this datagram. + + SourceRoutingInformation - Pointer to optional source routing information. + +Return Value: + + None. + +--*/ + +{ +// NTSTATUS Status; + NDIS_STATUS NdisStatus; + PDEVICE_CONTEXT DeviceContext; + PUCHAR DestinationAddress; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendUIMdlFrame: Entered.\n"); + } + + + // + // Send the packet. + // + + DeviceContext = Address->Provider; + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + Address->UIFrame->Header, + &DestinationAddress); + + if (RtlEqualMemory( + DestinationAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + DeviceContext, + Address->UIFrame->NdisPacket, + LOOPBACK_UI_FRAME + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + (NDIS_HANDLE)Address->Provider->NdisBindingHandle, + Address->UIFrame->NdisPacket); + + } + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbfSendDatagramCompletion (Address, Address->UIFrame->NdisPacket, NdisStatus); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { // This is an error, trickle it up + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint1 ("NbfSendUIMdlFrame: NdisSend failed, status other Pending or Complete: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + } +#endif + } + +} /* NbfSendUIMdlFrame */ + + +VOID +NbfSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called as an I/O completion handler at the time a + NbfSendUIMdlFrame send request is completed. Because this handler is only + associated with NbfSendUIMdlFrame, and because NbfSendUIMdlFrame is only + used with datagrams and broadcast datagrams, we know that the I/O being + completed is a datagram. Here we complete the in-progress datagram, and + start-up the next one if there is one. + +Arguments: + + Address - Pointer to a transport address on which the datagram + is queued. + + NdisPacket - pointer to the NDIS packet describing this request. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PLIST_ENTRY p; + KIRQL oldirql; + PNDIS_BUFFER HeaderBuffer; + + NdisPacket; // prevent compiler warnings. + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendDatagramCompletion: Entered.\n"); + } + + + // + // Dequeue the current request and return it to the client. Release + // our hold on the send datagram queue. + // + // *** There may be no current request, if the one that was queued + // was aborted or timed out. If this is the case, we added a + // special reference to the address, so we still want to deref + // when we are done (I don't think this is true - adb 3/22/93). + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + p = RemoveHeadList (&Address->SendDatagramQueue); + + if (p != &Address->SendDatagramQueue) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfDestroyConnectionlessFrame: Entered.\n"); + } + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Address->UIFrame->NdisPacket, &HeaderBuffer); + + // drop the rest of the packet + + NdisReinitializePacket (Address->UIFrame->NdisPacket); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Address->UIFrame->NdisPacket, HeaderBuffer); + + // + // Ignore NdisStatus; datagrams always "succeed". The Information + // field was filled in when we queued the datagram. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Send more datagrams on the Address if possible. + // + + NbfSendDatagramsOnAddress (Address); // do more datagrams. + + } else { + + ASSERT (FALSE); + + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + } + + NbfDereferenceAddress ("Complete datagram", Address, AREF_REQUEST); + +} /* NbfSendDatagramCompletion */ diff --git a/private/ntos/tdi/nbf/iframes.c b/private/ntos/tdi/nbf/iframes.c new file mode 100644 index 000000000..d14eeb505 --- /dev/null +++ b/private/ntos/tdi/nbf/iframes.c @@ -0,0 +1,3339 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + iframes.c + +Abstract: + + This module contains routines called to handle i-frames received + from the data link provider. Most of these routines are called at receive + indication time. + + Also included here are routines that process data at receive completion + time. These are limited to handling DFM/DOL frames. + + The following frame types are cracked by routines in this module: + + o NBF_CMD_DATA_ACK + o NBF_CMD_DATA_FIRST_MIDDLE + o NBF_CMD_DATA_ONLY_LAST + o NBF_CMD_SESSION_CONFIRM + o NBF_CMD_SESSION_END + o NBF_CMD_SESSION_INITIALIZE + o NBF_CMD_NO_RECEIVE + o NBF_CMD_RECEIVE_OUTSTANDING + o NBF_CMD_RECEIVE_CONTINUE + o NBF_CMD_SESSION_ALIVE + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +extern ULONG StartTimerDelayedAck; + +#define NbfUsePiggybackAcks 1 +#if DBG +extern ULONG NbfDebugPiggybackAcks; +#endif + + +VOID +NbfAcknowledgeDataOnlyLast( + IN PTP_CONNECTION Connection, + IN ULONG MessageLength + ) + +/*++ + +Routine Description: + + This routine takes care of acknowledging a DOL which has + been received. It either sends a DATA_ACK right away, or + queues a request for a piggyback ack. + + NOTE: This routine is called with the connection spinlock + held, and it returns with it released. IT MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + MessageLength - the total length (including all DFMs and this + DOL) of the message. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + + // + // Determine if we need to ack at all. + // + + if (Connection->CurrentReceiveNoAck) { + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return; + } + + + // + // Determine if a piggyback ack is feasible. + // + if (NbfUsePiggybackAcks && + Connection->CurrentReceiveAckQueueable) { + + // + // The sender allows it, see if we want to. + // + +#if 0 + // + // First reset this variable, to be safe. + // + + Connection->CurrentReceiveAckQueueable = FALSE; +#endif + + // + // For long sends, don't bother since these + // often happen without back traffic. + // + + if (MessageLength >= 8192L) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("M"); + } +#endif + goto NormalDataAck; + } + + // + // If there have been two receives in a row with + // no sends in between, don't wait for back traffic. + // + + if (Connection->ConsecutiveReceives >= 2) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("R"); + } +#endif + goto NormalDataAck; + } + + // + // Do not put a stopping connection on the DataAckQueue + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("S"); + } +#endif + goto NormalDataAck; + } + + // + // Queue the piggyback ack request. If the timer expires + // before a DFM or DOL is sent, a normal DATA ACK will + // be sent. + // + // Connection->Header.TransmitCorrelator has already been filled in. + // + + // + // BAD! We shouldn't already have an ack queued. + // + + ASSERT ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0); + + KeQueryTickCount (&Connection->ConnectStartTime); + Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_ACK; + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("Q"); + } +#endif + + DeviceContext = Connection->Link->Provider; + + if (!Connection->OnDataAckQueue) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = TRUE; + InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage); + + if (!(DeviceContext->a.i.DataAckQueueActive)) { + + StartTimerDelayedAck++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.DataAckQueueActive = TRUE; + + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (DeviceContext, PiggybackAckQueued); + + return; + + } + +NormalDataAck:; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendDataAck (Connection); + +} /* NbfAcknowledgeDataOnlyLast */ + + +NTSTATUS +ProcessSessionConfirm( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_CONFIRM NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PTDI_CONNECTION_INFORMATION remoteInformation; + USHORT HisMaxDataSize; + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + ULONG returnLength; + TA_NETBIOS_ADDRESS TempAddress; +// BOOLEAN TimerWasSet; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint1 ("ProcessSessionConfirm: Entered, Flags: %lx\n", Connection->Flags); + } + + Connection->IndicationInProgress = FALSE; + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SC) != 0) { + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SC; + + // + // Get his capability bits and maximum frame size. + // + + if (IFrame->Data1 & SESSION_CONFIRM_OPTIONS_20) { + Connection->Flags |= CONNECTION_FLAGS_VERSION2; + } + + if (Connection->Link->Loopback) { + Connection->MaximumDataSize = 0x8000; + } else { + Connection->MaximumDataSize = (USHORT) + (Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)); + + HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + if (HisMaxDataSize < Connection->MaximumDataSize) { + Connection->MaximumDataSize = HisMaxDataSize; + } + } + + // + // Build a standard Netbios header for speed when sending + // data frames. + // + + ConstructDataOnlyLast( + &Connection->NetbiosHeader, + FALSE, + (USHORT)0, + Connection->Lsn, + Connection->Rsn); + + // + // Turn off the connection request timer if there is one, and set + // this connection's state to READY. + // + + Connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + // + // Record that the connect request has been successfully + // completed by TpCompleteRequest. + // + + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Connection->IndicationInProgress = FALSE; + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + } + + // + // Complete the TdiConnect request. + // + + p = RemoveHeadList (&Connection->InProgressRequest); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // Now complete the request and get out. + // + + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + + // + // We have a completed connection with a queued connect. Complete + // the connect. + // + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock(cancelirql); + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + if (status == STATUS_SUCCESS) { + + if ((ULONG)Connection->Retries == Connection->Provider->NameQueryRetries) { + + INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterNoRetry); + + } else { + + INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterRetry); + + } + + } + + // + // Don't clear this until now, so that the connection is all + // set up before we allow more indications. + // + + Connection->IndicationInProgress = FALSE; + + NbfCompleteRequest (request, status, returnLength); + + } else { + + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelirql); + + } + + return STATUS_SUCCESS; +} /* ProcessSessionConfirm */ + + +NTSTATUS +ProcessSessionEnd( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_END NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT data2; + NTSTATUS StopStatus; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessSessionEnd: Entered.\n"); + } + + // + // Handle the error code in the Data2 field. Current protocol says + // if the field is 0, then this is a normal HANGUP.NCB operation. + // If the field is 1, then this is an abnormal session end, caused + // by something like a SEND.NCB timing out. Of course, new protocol + // may be added in the future, so we handle only these specific cases. + // + + data2 = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + switch (data2) { + case 0: + case 1: + StopStatus = STATUS_REMOTE_DISCONNECT; + break; + + default: + PANIC ("ProcessSessionEnd: frame not expected.\n"); + StopStatus = STATUS_INVALID_NETWORK_RESPONSE; + } +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint3( "SessionEnd received for connection to %S from %S; reason %s\n", + &remoteName, &localName, + data2 == 0 ? "NORMAL" : data2 == 1 ? "ABORT" : "UNKNOWN" ); + } +#endif + + // + // Run-down this connection. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ProcessSessionEnd calling NbfStopConnection\n"); + } + NbfStopConnection (Connection, StopStatus); // disconnected by the other end + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; +} /* ProcessSessionEnd */ + + +NTSTATUS +ProcessSessionInitialize( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_INITIALIZE NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PIO_STACK_LOCATION irpSp; + USHORT HisMaxDataSize; + ULONG returnLength; + PTDI_CONNECTION_INFORMATION remoteInformation; + NTSTATUS status; + TA_NETBIOS_ADDRESS TempAddress; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint1 ("ProcessSessionInitialize: Entered, Flags: %lx\n", Connection->Flags); + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) { + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SI; + + // + // Get his capability bits and maximum frame size. + // + + if (IFrame->Data1 & SESSION_INIT_OPTIONS_20) { + Connection->Flags |= CONNECTION_FLAGS_VERSION2; + } + + if (Connection->Link->Loopback) { + Connection->MaximumDataSize = 0x8000; + } else { + Connection->MaximumDataSize = (USHORT) + (Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)); + + HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + if (HisMaxDataSize < Connection->MaximumDataSize) { + Connection->MaximumDataSize = HisMaxDataSize; + } + } + + // + // Build a standard Netbios header for speed when sending + // data frames. + // + + ConstructDataOnlyLast( + &Connection->NetbiosHeader, + FALSE, + (USHORT)0, + Connection->Lsn, + Connection->Rsn); + + // + // Save his session initialize correlator so we can send it + // in the session confirm frame. + // + + Connection->NetbiosHeader.TransmitCorrelator = RESPONSE_CORR(IFrame); + + // + // Turn off the connection request timer if there is one (we're done). + // Do this with the lock held in case the connection is about to + // be closed, to not interfere with the timer started when the + // connection started then. + // + + if (KeCancelTimer (&Connection->Timer)) { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("Timer canceled", Connection, CREF_TIMER); // remove timer reference. + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // Now, complete the listen request on the connection (if there was + // one) and continue with as much of the protocol request as possible. + // if the user has "pre-accepted" the connection, we'll just continue + // onward here and complete the entire connection setup. If the user + // was indicated and has not yet accepted, we'll just put the + // connection into the proper state and fall out the bottom without + // completing anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_ACCEPTED) != 0) || + ((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) != 0)) { + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("SessionInitialize: Accepted connection %lx\n", Connection); + } + // + // we've already accepted the connection; allow it to proceed. + // this is the normal path for kernel mode indication clients, + // or for those who don't specify TDI_QUERY_ACCEPT on the listen. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_READY; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + // + // Record that the listen request has been successfully + // completed by NbfCompleteRequest. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + status = STATUS_SUCCESS; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfSendSessionConfirm (Connection); + + } else { + + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + + // + // we disconnected, destroy the connection + // + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("SessionInitialize: Disconnected connection %lx\n", Connection); + } + + status = STATUS_LOCAL_DISCONNECT; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + + } else { + + // + // we've done nothing, wait for the user to accept on this + // connection. This is the "normal" path for non-indication + // clients. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_WAITING_SC; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + status = STATUS_SUCCESS; + } + } + + // + // Now, if there was no queued listen, we have done everything we can + // for this connection, so we simply exit and leave everything up to + // the user. If we've gotten an indication response that allows the + // connection to proceed, we will come out of here with a connection + // that's up and running. + // + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + // + // We have a completed connection with a queued listen. Complete + // the listen and let the user do an accept at some time down the + // road. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock (cancelirql); + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + // + // Don't clear this until now, so that the connection is all + // set up before we allow more indications. + // + + Connection->IndicationInProgress = FALSE; + + NbfCompleteRequest (request, status, 0); + + } else { + + Connection->IndicationInProgress = FALSE; +#if DBG + NbfPrint3 ("ProcessSessionInitialize: C %lx, Flags %lx, Flags2 %lx\n", + Connection, Connection->Flags, Connection->Flags2); +#endif + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + return STATUS_SUCCESS; +} /* ProcessSessionInitialize */ + + +NTSTATUS +ProcessNoReceive( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming NO_RECEIVE NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessNoReceive: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. +// Connection->SendState = CONNECTION_SENDSTATE_W_RCVCONT; +// +// this used to be here, and is right for the other side of the connection. It's +// wrong here. +// Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + break; + + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessNoReceive: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessNoReceive: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return STATUS_SUCCESS; +} /* ProcessNoReceive */ + + +NTSTATUS +ProcessReceiveOutstanding( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming RECEIVE_OUTSTANDING NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessReceiveOutstanding: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if ((Connection->Flags & CONNECTION_FLAGS_READY) != 0) { + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + } + break; + + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessReceiveOutstanding: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessReceiveOutstanding: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + // + // Now start packetizing the connection again since we've reframed + // the current send. If we were idle or in a bad state, then the + // packetizing routine will detect that. + // + // *** StartPacketizingConnection releases the Connection spin lock. + // + + StartPacketizingConnection (Connection, FALSE); + return STATUS_SUCCESS; +} /* ProcessReceiveOutstanding */ + + +NTSTATUS +ProcessReceiveContinue( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming RECEIVE_CONTINUE NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessReceiveContinue: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, Connection->sp.MessageBytesSent); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + break; + + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessReceiveContinue: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessReceiveContinue: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + // + // Now start packetizing the connection again since we've reframed + // the current send. If we were idle or in a bad state, then the + // packetizing routine will detect that. + // + // *** StartPacketizingConnection releases the Connection spin lock. + // + + StartPacketizingConnection (Connection, FALSE); + return STATUS_SUCCESS; +} /* ProcessReceiveContinue */ + + +NTSTATUS +ProcessSessionAlive( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_ALIVE NetBIOS frame. This + routine is by far the simplest in the transport because it does nothing. + The SESSION_ALIVE frame is simply a dummy frame that is sent on the + reliable data link layer to determine if the data link is still active; + no NetBIOS level protocol processing is performed. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Connection); // prevent compiler warnings + UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessSessionAlive: Entered.\n"); + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; +} /* ProcessSessionAlive */ + + +VOID +NbfProcessIIndicate( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN UINT DlcTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine processes a received I frame at indication time. It will do + all necessary verification processing of the frame and pass those frames + that are valid on to the proper handling routines. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC I-type frame. + + DlcHeader - A pointer to the start of the DLC header in the packet. + + DlcIndicatedLength - The length of the packet indicated, starting at + DlcHeader. + + DlcTotalLength - The total length of the packet, starting at DlcHeader. + + ReceiveContext - A magic value for NDIS that indicates which packet we're + talking about. + + Loopback - Is this a loopback indication; used to determine whether + to call NdisTransferData or NbfTransferLoopbackData. + +Return Value: + + None. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + PNBF_HDR_CONNECTION nbfHeader; + PDLC_I_FRAME header; + NTSTATUS Status; + UCHAR lsn, rsn; + PTP_CONNECTION connection; + PUCHAR DataHeader; + ULONG DataTotalLength; + PLIST_ENTRY p; + BOOLEAN ConnectionFound; + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessIIndicate: Entered.\n"); + } + + // + // Process any of: I-x/x. + // + + header = (PDLC_I_FRAME)DlcHeader; + nbfHeader = (PNBF_HDR_CONNECTION)((PUCHAR)header + 4); // skip DLC hdr. + + // + // Verify signatures. We test the signature as a 16-bit + // word as specified in the NetBIOS Formats and Protocols manual, + // with the assert guarding us against big-endian systems. + // + + ASSERT ((((PUCHAR)(&nbfHeader->Length))[0] + ((PUCHAR)(&nbfHeader->Length))[1]*256) == + HEADER_LENGTH(nbfHeader)); + + if (HEADER_LENGTH(nbfHeader) != sizeof(NBF_HDR_CONNECTION)) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Too short or long.\n"); + } + return; // frame too small or too large. + } + + if (HEADER_SIGNATURE(nbfHeader) != NETBIOS_SIGNATURE) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Signature bad.\n"); + } + return; // invalid signature in frame. + } + + DataHeader = (PUCHAR)DlcHeader + (4 + sizeof(NBF_HDR_CONNECTION)); + DataTotalLength = DlcTotalLength - (4 + sizeof(NBF_HDR_CONNECTION)); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + switch (Link->State) { + + case LINK_STATE_READY: + + // + // Link is balanced. This code is extremely critical since + // it is the most-covered path in the system for small and + // large I/O. Be very careful in adding code here as it will + // seriously affect the overall performance of the LAN. It is + // first in the list of possible states for that reason. + // + +#if DBG + s = "READY"; +#endif + Link->LinkBusy = FALSE; + + // + // The I-frame's N(S) should match our V(R). If it + // doesn't, issue a reject. Otherwise, increment our V(R). + // + + if ((UCHAR)((header->SendSeq >> 1) & 0x7F) != Link->NextReceive) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessIIndicate: N(S) != V(R).\n"); + } + + if (Link->ReceiveState == RECEIVE_STATE_REJECTING) { + + + // + // We already sent a reject, only respond if + // he is polling. + // + + if (Command & PollFinal) { + NbfSendRr(Link, FALSE, TRUE); // releases lock + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + } else { + + Link->ReceiveState = RECEIVE_STATE_REJECTING; + + // + // NbfSendRej releases the spinlock. + // + + if (Command) { + NbfSendRej (Link, FALSE, PollFinal); + } else { + NbfSendRej (Link, FALSE, FALSE); + } + } + + // + // Update our "bytes rejected" counters. + // + + ADD_TO_LARGE_INTEGER( + &Link->Provider->Statistics.DataFrameBytesRejected, + DataTotalLength); + ++Link->Provider->Statistics.DataFramesRejected; + + // + // Discard this packet. + // + + break; + + } + + + // + // Find the transport connection object associated with this frame. + // Because there may be several NetBIOS (transport) connections + // over the same data link connection, and the ConnectionContext + // value represents a data link connection to a specific address, + // we simply use the RSN field in the frame to index into the + // connection database for this link object. + // + // We do this before processing the rest of the LLC header, + // in case the connection is busy and we have to ignore + // the frame. + // + + ConnectionFound = FALSE; + + lsn = nbfHeader->DestinationSessionNumber; + rsn = nbfHeader->SourceSessionNumber; + + if ((lsn == 0) || (lsn > NETBIOS_SESSION_LIMIT)) { + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Invalid LSN.\n"); + } + + } else { + + p = Link->ConnectionDatabase.Flink; + while (p != &Link->ConnectionDatabase) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (connection->Lsn >= lsn) { // assumes ordered list + break; + } + p = p->Flink; + } + + // Don't use compound if 'cause Connection may be garbage + + if (p == &Link->ConnectionDatabase) { +#if DBG + NbfPrint2 ("NbfProcessIIndicate: Connection not found in database: \n Lsn %x Link %lx", + lsn, Link); + NbfPrint6 ("Remote: %x-%x-%x-%x-%x-%x\n", + Link->HardwareAddress.Address[0], Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], Link->HardwareAddress.Address[5]); +#endif + } else if (connection->Lsn != lsn) { +#if DBG + NbfPrint0 ("NbfProcessIIndicate: Connection in database doesn't match.\n"); +#endif + } else if (connection->Rsn != rsn) { +#if DBG + NbfPrint3 ("NbfProcessIIndicate: Connection lsn %d had rsn %d, got frame for %d\n", + connection->Lsn, connection->Rsn, rsn); +#endif + } else { + + // + // The connection is good, proceed. + // + + ConnectionFound = TRUE; + + if (connection->IndicationInProgress) { + NbfPrint1("ProcessIIndicate: Indication in progress on %lx\n", connection); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + } + + // + // Set this, it prevents other I-frames from being received + // on this connection. The various ProcessXXX routines + // that are called from the switch below will clear + // this flag when they determine it is OK to be reentered. + // + + connection->IndicationInProgress = TRUE; + + + // This reference is removed before this function returns or + // we are done with the LINK_STATE_READY part of the outer switch. + + NbfReferenceConnection ("Processing IFrame", connection, CREF_PROCESS_DATA); + + + } + + } + + + // + // As long as we don't have to drop this frame, adjust the link + // state correctly. If ConnectionFound is FALSE, then we exit + // right after doing this. + // + + + // + // The I-frame we expected arrived, clear rejecting state. + // + + if (Link->ReceiveState == RECEIVE_STATE_REJECTING) { + Link->ReceiveState = RECEIVE_STATE_READY; + } + + Link->NextReceive = (UCHAR)((Link->NextReceive+1) & 0x7f); + + // + // If he is checkpointing, we need to respond with RR-c/f. If + // we respond, then stop the delayed ack timer. Otherwise, we + // need to start it because this is an I-frame that will not be + // acked immediately. + // + + if (Command && PollFinal) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessI: he's checkpointing.\n"); + } + Link->RemoteNoPoll = FALSE; + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, TRUE); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + if (Link->RemoteNoPoll) { + + if ((++Link->ConsecutiveIFrames) == Link->Provider->MaxConsecutiveIFrames) { + + // + // This appears to be one of those remotes which + // never polls, so we send an RR if there are two + // frames outstanding (StopT2 sets ConsecutiveIFrames + // to 0). + // + + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, FALSE); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + StartT2 (Link); + + ACQUIRE_DPC_SPIN_LOCK (&Link->Provider->Interlock); + if (!Link->OnDeferredRrQueue) { + InsertTailList( + &Link->Provider->DeferredRrQueue, + &Link->DeferredRrLinkage); + Link->OnDeferredRrQueue = TRUE; + } + RELEASE_DPC_SPIN_LOCK (&Link->Provider->Interlock); + + } + + } else { + + StartT2 (Link); // start delayed ack sequence. + } + + // + // If he is responding to a checkpoint, we need to clear our + // send state. Any packets which are still waiting for acknowlegement + // at this point must now be resent. + // + + if ((!Command) && PollFinal) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessI: he's responding to our checkpoint.\n"); + } + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessI: BUGBUG: Ckpt but SendState=%ld.\n", + Link->SendState); + } + } + StopT1 (Link); // checkpoint completed. + Link->SendState = SEND_STATE_READY; + StartTi (Link); + } + + } + + // + // Now, if we could not find the connection or the sequence + // numbers did not match, return. We don't call ResendLlcPackets + // in this case, but that is OK (eventually we will poll). + // + + if (!ConnectionFound) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + } + + ASSERT (connection->LinkSpinLock == &Link->SpinLock); + + // + // The N(R) in this frame may acknowlege some WackQ packets. + // We delay checking this until after processing the I-frame, + // so that we can get IndicationInProgress set to FALSE + // before we start resending the WackQ. + // + + switch (nbfHeader->Command) { + + case NBF_CMD_DATA_FIRST_MIDDLE: + case NBF_CMD_DATA_ONLY_LAST: + + // + // First see if this packet has a piggyback ack -- we process + // this even if we throw the packet away below. + // + // This is a bit ugly since theoretically the piggyback + // ack bits in a DFM and a DOL could be different, but + // they aren't. + // + if (NbfUsePiggybackAcks) { + ASSERT (DFM_OPTIONS_ACK_INCLUDED == DOL_OPTIONS_ACK_INCLUDED); + + if ((nbfHeader->Data1 & DFM_OPTIONS_ACK_INCLUDED) != 0) { + + // + // This returns with the connection spinlock held + // but may release it and reacquire it. + // + + CompleteSend( + connection, + TRANSMIT_CORR(nbfHeader)); + + } + } + + // + // NOTE: The connection spinlock is held here. + // + + // + // If the connection is not ready, drop the frame. + // + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + + // + // A quick check for the three flags that are + // rarely set. + // + + if ((connection->Flags & (CONNECTION_FLAGS_W_RESYNCH | + CONNECTION_FLAGS_RC_PENDING | + CONNECTION_FLAGS_RECEIVE_WAKEUP)) == 0) { + goto NoFlagsSet; + } + + // + // If we are waiting for a resynch bit to be set in an + // incoming frame, toss the frame if it isn't set. + // Otherwise, clear the wait condition. + // + + if (connection->Flags & CONNECTION_FLAGS_W_RESYNCH) { + if ((nbfHeader->Data2Low == 1) && (nbfHeader->Data2High == 0)) { + connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH; + } else { + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, waiting for resynch.\n"); + } + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + } + + // + // If we have a previous receive that is pending + // completion, then we need to ignore this frame. + // This may be common on MP, so rather than drop + // it and wait for a poll, we send a NO_RECEIVE, + // then a RCV_OUTSTANDING when we have some + // resources. + // + + if (connection->Flags & CONNECTION_FLAGS_RC_PENDING) { + + // + // Hack the connection object so the NO_RECEIVE + // looks right. + // + + connection->MessageBytesReceived = 0; + connection->MessageBytesAcked = 0; + connection->MessageInitAccepted = 0; + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + NbfSendNoReceive (connection); + + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + // + // We now turn on the PEND_INDICATE flag to show + // that we need to send RCV_OUTSTANDING when the + // receive completes. If RC_PENDING is now off, + // it means the receive was just completed, so + // we ourselves need to send the RCV_OUTSTANDING. + // + + if ((connection->Flags & CONNECTION_FLAGS_RC_PENDING) == 0) { + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + NbfSendReceiveOutstanding (connection); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } else { + + connection->Flags |= CONNECTION_FLAGS_PEND_INDICATE; + + } + + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, receive complete pending.\n"); + } + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + + // + // If we are discarding data received on this connection + // because we've sent a no receive, ditch it. + // + + if (connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) { + connection->IndicationInProgress = FALSE; + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 ("NbfProcessIIndicate: In wakeup state, discarding frame.\n"); + } + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + +NoFlagsSet:; + + // + // The connection spinlock is held here. + // + + if (nbfHeader->Command == NBF_CMD_DATA_FIRST_MIDDLE) { + + // + // NOTE: This release connection->LinkSpinLock. + // + + Status = ProcessIndicateData ( + connection, + DlcHeader, + DlcIndicatedLength, + DataHeader, + DataTotalLength, + ReceiveContext, + FALSE, + Loopback); + + // + // If the receive-continue bit is set in this frame, then we must + // reply with a RECEIVE_CONTINUE frame saying that he can continue + // sending. This old protocol option allowed a sender to send a + // single frame over to see if there was a receive posted before + // sending the entire message and potentially dropping the entire + // message. Because the TDI is indication-based, we cannot know + // if there is NO receive available until we actually try perform + // the indication, so we simply say that there is one posted. + // (This will only happen on DFMs.) + // + + if (nbfHeader->Data1 & 0x01) { + + // + // Save this to use in RECEIVE_CONTINUE. + // + + connection->NetbiosHeader.TransmitCorrelator = + RESPONSE_CORR(nbfHeader); + + NbfSendReceiveContinue (connection); + } + } else { + + // + // Keep track of how many consecutive receives we have had. + // + + connection->ConsecutiveReceives++; + connection->ConsecutiveSends = 0; + + // + // Save this information now, it will be needed + // when the ACK for this DOL is sent. + // + + connection->CurrentReceiveAckQueueable = + (nbfHeader->Data1 & DOL_OPTIONS_ACK_W_DATA_ALLOWED); + + connection->CurrentReceiveNoAck = + (nbfHeader->Data1 & DOL_OPTIONS_NO_ACK); + + connection->NetbiosHeader.TransmitCorrelator = + RESPONSE_CORR(nbfHeader); + + // + // NOTE: This release connection->LinkSpinLock. + // + + Status = ProcessIndicateData ( + connection, + DlcHeader, + DlcIndicatedLength, + DataHeader, + DataTotalLength, + ReceiveContext, + TRUE, + Loopback); + } + + // + // Update our "bytes received" counters. + // + + Link->Provider->TempIFrameBytesReceived += DataTotalLength; + ++Link->Provider->TempIFramesReceived; + +SkipProcessIndicateData: + + break; + + case NBF_CMD_DATA_ACK: + + connection->IndicationInProgress = FALSE; + + // + // This returns with the lock held. + // + + CompleteSend( + connection, + TRANSMIT_CORR(nbfHeader)); + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + break; + + case NBF_CMD_SESSION_CONFIRM: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionConfirm ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_END: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionEnd ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_INITIALIZE: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionInitialize ( + connection, + nbfHeader); + break; + + case NBF_CMD_NO_RECEIVE: + + // + // This releases the connection spinlock. + // + + Status = ProcessNoReceive ( + connection, + nbfHeader); + break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + + // + // This releases the connection spinlock. + // + + Status = ProcessReceiveOutstanding ( + connection, + nbfHeader); + break; + + case NBF_CMD_RECEIVE_CONTINUE: + + // + // This releases the connection spinlock. + // + + Status = ProcessReceiveContinue ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_ALIVE: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionAlive ( + connection, + nbfHeader); + break; + + // + // An unrecognized command was found in a NetBIOS frame. Because + // this is a connection-oriented frame, we should probably shoot + // the sender, but for now we will simply discard the packet. + // + // BUGBUG: trash the session here-- protocol violation. + // + + default: + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + PANIC ("NbfProcessIIndicate: Unknown NBF command byte.\n"); + connection->IndicationInProgress = FALSE; + Status = STATUS_SUCCESS; + } /* switch */ + + // + // A status of STATUS_MORE_PROCESSING_REQUIRED means + // that the connection reference count was inherited + // by the routine we called, so we don't do the dereference + // here. + // + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + NbfDereferenceConnectionMacro("ProcessIIndicate done", connection, CREF_PROCESS_DATA); + } + + + // + // The N(R) in this frame acknowleges some (or all) of our packets. + // This call must come after the checkpoint acknowlegement check + // so that an RR-r/f is always sent BEFORE any new I-frames. This + // allows us to always send I-frames as commands. + // If he responded to a checkpoint, then resend all left-over + // packets. + // + + // Link->NextSend = (UCHAR)(header->RcvSeq >> 1) < Link->NextSend ? + // Link->NextSend : (UCHAR)(header->RcvSeq >> 1); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->WackQ.Flink != &Link->WackQ) { + + UCHAR AckSequenceNumber = (UCHAR)(header->RcvSeq >> 1); + + // + // Verify that the sequence number is reasonable. + // + + if (Link->NextSend >= Link->LastAckReceived) { + + // + // There is no 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) || + (AckSequenceNumber > Link->NextSend)) { + goto NoResend; + } + + } else { + + // + // There is a 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) && + (AckSequenceNumber > Link->NextSend)) { + goto NoResend; + } + + } + + // + // NOTE: ResendLlcPackets may release and + // reacquire the link spinlock. + // + + (VOID)ResendLlcPackets( + Link, + AckSequenceNumber, + (BOOLEAN)((!Command) && PollFinal)); + +NoResend:; + + } + + + // + // Get link going again. + // + // NOTE: RestartLinkTraffic releases the link spinlock + // + + RestartLinkTraffic (Link); + break; + + case LINK_STATE_ADM: + + // + // used to be, we'd just blow off the other guy with a DM and go home. + // it seems that OS/2 likes to believe (under some conditions) that + // it has a link up and it is still potentially active (probably + // because we return the same connection number to him that he used + // to be using). This would all be ok, except for the fact that we + // may have a connection hanging on this link waiting for a listen + // to finish. If we're in that state, go ahead and accept the + // connect. + // Set our values for link packet serial numbers to what he wants. + // + + if (!IsListEmpty (&Link->ConnectionDatabase)) { + if (nbfHeader->Command == NBF_CMD_SESSION_INITIALIZE) { + + // + // OK, we're at the only legal case. We've gotten an SI + // and we have a connection on this link. If the connection + // is waiting SI, we will go ahead and make believe we did + // all the correct stuff before we got it. + // + + for ( + p = Link->ConnectionDatabase.Flink, connection = NULL; + p != &Link->ConnectionDatabase ; + p = p->Flink, connection = NULL + ) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) { + // This reference is removed below + NbfReferenceConnection ("Found Listener at session init", connection, CREF_ADM_SESS); + break; + } + } + + // + // Well, we've looked through the connections, if we have one, + // make it the connection of the day. Note that it will + // complete when we call ProcessSessionInitialize. + // + + if (connection != NULL) { + + Link->NextReceive = (UCHAR)(header->SendSeq >> 1) & (UCHAR)0x7f; + Link->NextSend = (UCHAR)(header->RcvSeq >> 1) & (UCHAR)0x7F; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfCompleteLink (Link); // completes the listening connection + + Status = ProcessSessionInitialize ( + connection, + nbfHeader); + NbfDereferenceConnection ("Processed SessInit", connection, CREF_ADM_SESS); + +#if DBG + s = "ADM"; +#endif + + // Link is ready for use. + + break; + } + } + + // + // we've got a connection on a link that's in state admin. + // really bad, kill it and the link. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessIIndicate calling NbfStopLink\n" ); + } +#endif + NbfStopLink (Link); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + // + // We're disconnected. Tell him. + // + + NbfSendDm (Link, PollFinal); // releases lock +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent an + // I-frame too early, so just let the SABME time out. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. If he starts + // with an I-frame, then we'll let him squeak by. + // + + if (!Command) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_POLL"; +#endif + break; + } + + Link->State = LINK_STATE_READY; // we're up! + StopT1 (Link); // no longer waiting. + FakeUpdateBaseT1Timeout (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); + NbfProcessIIndicate ( // recursive, but safe + Command, + PollFinal, + Link, + DlcHeader, + DlcIndicatedLength, + DlcTotalLength, + ReceiveContext, + Loopback); +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy. I-r/f will do. + // + + if (Command || !PollFinal) { // don't allow this protocol. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_FINAL"; +#endif + break; // we sent RR-c/p. + } + + Link->State = LINK_STATE_READY; // we're up. + StopT1 (Link); // no longer waiting. + StartT2 (Link); // we have an unacked I-frame. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); + NbfProcessIIndicate ( // recursive, but safe + Command, + PollFinal, + Link, + DlcHeader, + DlcIndicatedLength, + DlcTotalLength, + ReceiveContext, + Loopback); +#if DBG + s = "W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this I-frame. Throw the packet away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessIIndicate: (%s) I-Frame processed.\n", s); + } +#endif + + return; +} /* NbfProcessIIndicate */ + + +NTSTATUS +ProcessIndicateData( + IN PTP_CONNECTION Connection, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN PUCHAR DataHeader, + IN UINT DataTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine is called to process data received in a DATA_FIRST_MIDDLE + or DATA_ONLY_LAST frame. We attempt to satisfy as many TdiReceive + requests as possible with this data. + + If a receive is already active on this Connection, then we copy as much + data into the active receive's buffer as possible. If all the data is + copied and the receive request's buffer has not been filled, then the + Last flag is checked, and if it is TRUE, we go ahead and complete the + current receive with the TDI_END_OF_RECORD receive indicator. If Last + is FALSE, we simply return. + + If more (uncopied) data remains in the frame, or if there is no active + receive outstanding, then an indication is issued to the owning address's + receive event handler. The event handler can take one of three actions: + + 1. Return STATUS_SUCCESS, in which case the transport will assume that + all of the indicated data has been accepted by the client. + + 3. Return STATUS_DATA_NOT_ACCEPTED, in which case the transport will + discard the data and set the CONNECTION_FLAGS_RECEIVE_WAKEUP bitflag + in the Connection, indicating that remaining data is to be discarded + until a receive becomes available. + + NOTE: This routine is called with Connection->LinkSpinLock held, + and returns with it released. THIS ROUTINE MUST BE CALLED AT + DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + DlcHeader - The pointer handed to us as the start of the NBF header by NDIS; + use this to compute the offset into the packet to start the transfer + of data to user buffers. + + DlcIndicatedLength - The amount of NBF data available at indicate. + + DataHeader - A pointer to the start of the data in the packet. + + DataTotalLength - The total length of the packet, starting at DataHeader. + + ReceiveContext - An NDIS handle that identifies the packet we are currently + processing. + + Last - Boolean value that indicates whether this is the last piece of data + in a message. The DATA_ONLY_LAST processor sets this flag to TRUE when + calling this routine, and the DATA_FIRST_MIDDLE processor resets this + flag to FALSE when calling this routine. + + Loopback - Is this a loopback indication; used to determine whether + to call NdisTransferData or NbfTransferLoopbackData. + + +Return Value: + + STATUS_SUCCESS if we've consumed the packet, + +--*/ + +{ + NTSTATUS status, tmpstatus; + PDEVICE_CONTEXT deviceContext; + NDIS_STATUS ndisStatus; + PNDIS_PACKET ndisPacket; + PSINGLE_LIST_ENTRY linkage; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PNDIS_BUFFER ndisBuffer; + ULONG destBytes; + ULONG bufferChainLength; + ULONG indicateBytesTransferred; + ULONG ReceiveFlags; + ULONG ndisBytesTransferred; + UINT BytesToTransfer; + ULONG bytesIndicated; + ULONG DataOffset = (PUCHAR)DataHeader - (PUCHAR)DlcHeader; + PRECEIVE_PACKET_TAG receiveTag; + PTP_ADDRESS_FILE addressFile; + PMDL SavedCurrentMdl; + ULONG SavedCurrentByteOffset; + BOOLEAN ActivatedLongReceive = FALSE; + BOOLEAN CompleteReceiveBool, EndOfMessage; + ULONG DumpData[2]; + + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint4 (" ProcessIndicateData: Entered, PacketStart: %lx Offset: %lx \n TotalLength %ld DlcIndicatedLength: %ld\n", + DlcHeader, DataOffset, DataTotalLength, DlcIndicatedLength); + } + + + // + // copy this packet into our receive buffer. + // + + deviceContext = Connection->Provider; + + if ((Connection->Flags & CONNECTION_FLAGS_RCV_CANCELLED) != 0) { + + // + // A receive in progress was cancelled; we toss the data, + // but do send the DOL if it was the last piece of the + // send. + // + + if (Last) { + + Connection->Flags &= ~CONNECTION_FLAGS_RCV_CANCELLED; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfSendDataAck (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + } + + // + // Initialize this to zero, in case we do not indicate or + // the client does not fill it in. + // + + indicateBytesTransferred = 0; + + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + + // + // check first to see if there is a receive available. If there is, + // use it before doing an indication. + // + + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one and + // cycle around again. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation (Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + status = STATUS_SUCCESS; + goto NormalReceive; + } + + // + // A receive is not active. Post a receive event. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + return STATUS_SUCCESS; + } + + addressFile = Connection->AddressFile; + + if ((!addressFile->RegisteredReceiveHandler) || + (Connection->ReceiveBytesUnaccepted != 0)) { + + // + // There is no receive posted to the Connection, and + // no event handler. Set the RECEIVE_WAKEUP bit, so that when a + // receive does become available, it will restart the + // current send. Also send a NoReceive to tell the other + // guy he needs to resynch. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->IndicationInProgress = FALSE; + + // NbfSendNoReceive (Connection); + return STATUS_SUCCESS; + } + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Receive not active. Posting event.\n"); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + LEAVE_NBF; + + // + // Indicate to the user. For BytesAvailable we + // always use DataTotalLength; for BytesIndicated we use + // MIN (DlcIndicatedLength - DataOffset, DataTotalLength). + // + // To clarify BytesIndicated, on an Ethernet packet + // which is padded DataTotalLength will be shorter; on an + // Ethernet packet which is not padded and which is + // completely indicated, the two will be equal; and + // on a long Ethernet packet DlcIndicatedLength - DataOffset + // will be shorter. + // + + bytesIndicated = DlcIndicatedLength - DataOffset; + if (DataTotalLength <= bytesIndicated) { + bytesIndicated = DataTotalLength; + } + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (deviceContext->MacInfo.CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + status = (*addressFile->ReceiveHandler)( + addressFile->ReceiveHandlerContext, + Connection->Context, + ReceiveFlags, + bytesIndicated, + DataTotalLength, // BytesAvailable + &indicateBytesTransferred, + DataHeader, + &irp); + ENTER_NBF; + + if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + ULONG SpecialIrpLength; + PTDI_REQUEST_KERNEL_RECEIVE Parameters; + + // + // The client's event handler has returned an IRP in the + // form of a TdiReceive that is to be associated with this + // data. The request will be installed at the front of the + // ReceiveQueue, and then made the active receive request. + // This request will be used to accept the incoming data, which + // will happen below. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_MORE_PROCESSING_REQUIRED.\n"); + NbfPrint4 (" ProcessIndicateData: Irp=%lx, Mdl=%lx, UserBuffer=%lx, Count=%ld.\n", + irp, irp->MdlAddress, irp->UserBuffer, + MmGetMdlByteCount (irp->MdlAddress)); + } + + // + // Queueing a receive of any kind causes a Connection reference; + // that's what we've just done here, so make the Connection stick + // around. We create a request to keep a packets outstanding ref + // count for the current IRP; we queue this on the connection's + // receive queue so we can treat it like a normal receive. If + // we can't get a request to describe this irp, we can't keep it + // around hoping for better later; we simple fail it with + // insufficient resources. Note this is only likely to happen if + // we've completely run out of transport memory. + // + + irp->IoStatus.Information = 0; // byte transfer count. + irp->IoStatus.Status = STATUS_PENDING; + irpSp = IoGetCurrentIrpStackLocation (irp); + + ASSERT (irpSp->FileObject->FsContext == Connection); + + Parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters; + SpecialIrpLength = Parameters->ReceiveLength; + + // + // If the packet is a DOL, and it will fit entirely + // inside this posted IRP, then we don't bother + // creating a request, because we don't need any of + // that overhead. We also don't set ReceiveBytes + // Unaccepted, since this receive would clear it + // anyway. + // + + if (Last && + (SpecialIrpLength >= (DataTotalLength - indicateBytesTransferred))) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->SpecialReceiveIrp = irp; + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = SpecialIrpLength; + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveIrp = NULL; + Connection->CurrentReceiveSynchronous = TRUE; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + if ((Parameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0) { + Connection->CurrentReceiveAckQueueable = FALSE; + } + +#if DBG + // + // switch our reference from PROCESS_DATA to + // RECEIVE_IRP, this is OK because the RECEIVE_IRP + // reference won't be removed until Transfer + // DataComplete, which is the last thing + // we call. + // + + NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP); + NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA); +#endif + + } else { + KIRQL cancelIrql; + + // + // The normal path, for longer receives. + // + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + IRP_RECEIVE_IRP(irpSp) = irp; + if (deviceContext->MacInfo.SingleReceive) { + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + } else { +#if DBG + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + NbfReferenceReceiveIrpLocked ("Transfer Data", irpSp, RREF_RECEIVE); +#else + IRP_RECEIVE_REFCOUNT(irpSp) = 2; // include one for first xfer +#endif + } + + // + // If the Connection is stopping, abort this request. + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + Connection->IndicationInProgress = FALSE; + + NbfReferenceConnection("Special IRP stopping", Connection, CREF_RECEIVE_IRP); + NbfCompleteReceiveIrp ( + irp, + Connection->Status, + 0); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + if (!deviceContext->MacInfo.SingleReceive) { + NbfDereferenceReceiveIrp ("Not ready", irpSp, RREF_RECEIVE); + } + return STATUS_SUCCESS; // we have consumed the packet + + } + + // + // If this IRP has been cancelled, complete it now. + // + + if (irp->Cancel) { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + + Connection->IndicationInProgress = FALSE; + + NbfReferenceConnection("Special IRP cancelled", Connection, CREF_RECEIVE_IRP); + + // + // It is safe to call this with locks held. + // + NbfCompleteReceiveIrp (irp, STATUS_CANCELLED, 0); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + if (!deviceContext->MacInfo.SingleReceive) { + NbfDereferenceReceiveIrp ("Cancelled", irpSp, RREF_RECEIVE); + } + + return STATUS_SUCCESS; + } + + // + // Insert the request on the head of the connection's + // receive queue, so it can be handled like a normal + // receive. + // + + InsertHeadList (&Connection->ReceiveQueue, &irp->Tail.Overlay.ListEntry); + + IoSetCancelRoutine(irp, NbfCancelReceive); + + // + // Release the cancel spinlock out of order. Since we were + // at DPC level when we acquired it, we don't have to fiddle + // with swapping irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = Parameters->ReceiveLength; + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveIrp = irp; + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + +#if DBG + // + // switch our reference from PROCESS_DATA to + // REQUEST, this is OK because the REQUEST + // reference won't be removed until Transfer + // DataComplete, which is the last thing + // we call. + // + + NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP); + NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA); +#endif + // + // Make a note so we know what to do below. + // + + ActivatedLongReceive = TRUE; + +#if DBG + NbfReceives[NbfReceivesNext].Irp = irp; + NbfReceivesNext = (NbfReceivesNext++) % TRACK_TDI_LIMIT; +#endif + } + + } else if (status == STATUS_SUCCESS) { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_SUCCESS.\n"); + } + + // + // The client has accepted some or all of the indicated data in + // the event handler. Update MessageBytesReceived variable in + // the Connection so that if we are called upon to ACK him + // at the byte level, then we can correctly report the + // number of bytes received thus far. If this is a DOL, + // then reset the number of bytes received, since this value + // always at zero for new messages. If the data indicated wasn't + // all the data in this packet, flow control to the sender that + // didn't get all of the data. + // + + if (Last && (indicateBytesTransferred >= DataTotalLength)) { + + ASSERT (indicateBytesTransferred == DataTotalLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // This will send a DATA ACK or queue a request for + // a piggyback ack. + // + // NOTE: It will also release the connection spinlock. + // + + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } else { + + // + // This gets gory. + // If this packet wasn't a DOL, we have no way of knowing how + // much the client will take of the data in this send that is + // now arriving. Pathological clients will break this protocol + // if they do things like taking part of the receive at indicate + // immediate and then return an irp (this would make the byte + // count wrong for the irp). + // + // Since the client did not take all the data that we + // told him about, he will eventually post a receive. + // If this has not already happened then we set the + // RECEIVE_WAKEUP bit and send a NO_RECEIVE. + // + +#if DBG + NbfPrint0("NBF: Indicate returned SUCCESS but did not take all data\n"); +#endif + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + + if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) { + + // + // There is no receive posted to the Connection. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + + if (indicateBytesTransferred == DataTotalLength) { + + // + // This means he took everything, but it was not + // a DOL; there is no need to do anything since + // the rest of the data will be right behind. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } else { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendNoReceive (Connection); + + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + + } else { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one. This will cause + // an NdisTransferData below, so we don't dereference the + // Connection here. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + } + + } + + } else { // STATUS_DATA_NOT_ACCEPTED or other + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_DATA_NOT_ACCEPTED.\n"); + } + + // + // Either there is no event handler installed (the default + // handler returns this code) or the event handler is not + // able to process the received data at this time. If there + // is a TdiReceive request outstanding on this Connection's + // ReceiveQueue, then we may use it to receive this data. + // If there is no request outstanding, then we must initiate + // flow control at the transport level. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->ReceiveBytesUnaccepted = DataTotalLength; + + if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) { + + // + // There is no receive posted to the Connection, and + // the event handler didn't want to accept the incoming + // data. Set the RECEIVE_WAKEUP bit, so that when a + // receive does become available, it will restart the + // current send. Also send a NoReceive to tell the other + // guy he needs to resynch. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + Connection->IndicationInProgress = FALSE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return STATUS_SUCCESS; + + } else { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one. This will cause + // an NdisTransferData below, so we don't dereference the + // Connection here. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + } + + } + + } else { + + // + // A receive is active, set the status to show + // that so far. + // + + status = STATUS_SUCCESS; + + } + + +NormalReceive:; + + // + // NOTE: The connection spinlock is held here. + // + // We should only get through here if a receive is active + // and we have not released the lock since checking or + // making one active. + // + + ASSERT(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint2 (" ProcessIndicateData: Receive is active. ReceiveLengthLength: %ld Offset: %ld.\n", + Connection->ReceiveLength, Connection->MessageBytesReceived); + } + + destBytes = Connection->ReceiveLength - Connection->MessageBytesReceived; + + // + // If we just activated a non-special receive IRP, we already + // added a refcount for this transfer. + // + + if (!Connection->CurrentReceiveSynchronous && !ActivatedLongReceive) { + NbfReferenceReceiveIrpLocked ("Transfer Data", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + // + // Determine how much data remains to be transferred. + // + + ASSERT (indicateBytesTransferred <= DataTotalLength); + BytesToTransfer = DataTotalLength - indicateBytesTransferred; + + if (destBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceiveBool = TRUE; + BytesToTransfer = destBytes; + + } else if (destBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceiveBool = TRUE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceiveBool = Last; + + } + + + // + // If we can copy the data directly, then do so. + // + + if ((BytesToTransfer > 0) && + (DataOffset + indicateBytesTransferred + BytesToTransfer <= DlcIndicatedLength)) { + + // + // All the data that we need to transfer is available in + // the indication, so copy it directly. + // + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PMDL CurMdl; + ULONG CurByteOffset; + + // + // First we advance the connection pointers by the appropriate + // number of bytes, so that we can reallow indications (only + // do this if needed). + // + + CurMdl = Connection->CurrentReceiveMdl; + CurByteOffset = Connection->ReceiveByteOffset; + + if (!deviceContext->MacInfo.ReceiveSerialized) { + + SavedCurrentMdl = CurMdl; + SavedCurrentByteOffset = CurByteOffset; + + BytesLeft = BytesToTransfer; + CurTargetLen = MmGetMdlByteCount (CurMdl) - CurByteOffset; + while (TRUE) { + if (BytesLeft >= CurTargetLen) { + BytesLeft -= CurTargetLen; + CurMdl = CurMdl->Next; + CurByteOffset = 0; + if (BytesLeft == 0) { + break; + } + CurTargetLen = MmGetMdlByteCount (CurMdl); + } else { + CurByteOffset += BytesLeft; + break; + } + } + + Connection->CurrentReceiveMdl = CurMdl; + Connection->ReceiveByteOffset = CurByteOffset; + Connection->MessageBytesReceived += BytesToTransfer; + + // + // Set this up, we know the transfer won't + // "fail" but another one at the same time + // might. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->TransferBytesPending == 0) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + } else { + Connection->TransferBytesPending += BytesToTransfer; + Connection->TotalTransferBytesPending += BytesToTransfer; + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->IndicationInProgress = FALSE; + + // + // Restore these for the next section of code. + // + + CurMdl = SavedCurrentMdl; + CurByteOffset = SavedCurrentByteOffset; + + } + + CurTarget = (PUCHAR)(MmGetSystemAddressForMdl(CurMdl)) + CurByteOffset; + CurTargetLen = MmGetMdlByteCount(CurMdl) - CurByteOffset; + CurSource = DataHeader + indicateBytesTransferred; + + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + deviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurMdl = CurMdl->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + CurTarget = MmGetSystemAddressForMdl(CurMdl); + CurTargetLen = MmGetMdlByteCount(CurMdl); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + ASSERT (BytesLeft == BytesNow); + break; + } + + } + + if (deviceContext->MacInfo.ReceiveSerialized) { + + // + // If we delayed updating these, do it now. + // + + Connection->CurrentReceiveMdl = CurMdl; + Connection->ReceiveByteOffset = CurByteOffset; + Connection->MessageBytesReceived += BytesToTransfer; + Connection->IndicationInProgress = FALSE; + + } else { + + // + // Check if something else failed and we are the + // last to complete, if so then back up our + // receive pointers and send a receive + // outstanding to make him resend. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->TransferBytesPending -= BytesToTransfer; + + if ((Connection->TransferBytesPending == 0) && + (Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) { + + Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl; + Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset; + Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending; + Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendReceiveOutstanding (Connection); + + if (!Connection->SpecialReceiveIrp && + !Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + return status; + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + } + + // + // Now that the transfer is complete, simulate a call to + // TransferDataComplete. + // + + + if (!Connection->SpecialReceiveIrp) { + + Connection->CurrentReceiveIrp->IoStatus.Information += BytesToTransfer; + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + } + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (CompleteReceiveBool) { + CompleteReceive (Connection, EndOfMessage, BytesToTransfer); + } + + return status; + + } + + + // + // Get a packet for the coming transfer + // + + linkage = ExInterlockedPopEntryList( + &deviceContext->ReceivePacketPool, + &deviceContext->Interlock); + + if (linkage != NULL) { + ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + deviceContext->ReceivePacketExhausted++; + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("No receive packet", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + // + // We could not get a receive packet. We do have an active + // receive, so we just send a receive outstanding to + // get him to resend. Hopefully we will have a receive + // packet available when the data is resent. + // + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + +#if DBG + NbfPrint0 (" ProcessIndicateData: Discarding Packet, no receive packets\n"); +#endif + Connection->IndicationInProgress = FALSE; + + return status; + } + + // + // Initialize the receive packet. + // + + receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved); + // receiveTag->PacketType = TYPE_AT_INDICATE; + receiveTag->Connection = Connection; + receiveTag->TransferDataPended = TRUE; + + receiveTag->EndOfMessage = EndOfMessage; + receiveTag->CompleteReceive = CompleteReceiveBool; + + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + Connection->IndicationInProgress = FALSE; + receiveTag->TransferDataPended = FALSE; + receiveTag->AllocatedNdisBuffer = FALSE; + receiveTag->BytesToTransfer = 0; + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + NDIS_STATUS_SUCCESS, + 0); + + return status; + } + + // + // describe the right part of the user buffer to NDIS. If we can't get + // the mdl for the packet, drop dead. Bump the request reference count + // so that we know we need to hold open receives until the NDIS transfer + // data requests complete. + // + + SavedCurrentMdl = Connection->CurrentReceiveMdl; + SavedCurrentByteOffset = Connection->ReceiveByteOffset; + + if ((Connection->ReceiveByteOffset == 0) && + (CompleteReceiveBool)) { + + // + // If we are transferring into the beginning of + // the current MDL, and we will be completing the + // receive after the transfer, then we don't need to + // copy it. + // + + ndisBuffer = (PNDIS_BUFFER)Connection->CurrentReceiveMdl; + bufferChainLength = BytesToTransfer; + Connection->CurrentReceiveMdl = NULL; + // Connection->ReceiveByteOffset = 0; + receiveTag->AllocatedNdisBuffer = FALSE; + tmpstatus = STATUS_SUCCESS; + + } else { + + tmpstatus = BuildBufferChainFromMdlChain ( + deviceContext, + Connection->CurrentReceiveMdl, + Connection->ReceiveByteOffset, + BytesToTransfer, + &ndisBuffer, + &Connection->CurrentReceiveMdl, + &Connection->ReceiveByteOffset, + &bufferChainLength); + + receiveTag->AllocatedNdisBuffer = TRUE; + + } + + + if ((!NT_SUCCESS (tmpstatus)) || (bufferChainLength != BytesToTransfer)) { + + DumpData[0] = bufferChainLength; + DumpData[1] = BytesToTransfer; + + NbfWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + tmpstatus, + NULL, + 2, + DumpData); + + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("No MDL chain", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + // + // Restore our old state and make him resend. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + Connection->IndicationInProgress = FALSE; + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + &receiveTag->Linkage, + &deviceContext->Interlock); + + return status; + } + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint3 (" ProcessIndicateData: Mdl: %lx user buffer: %lx user offset: %lx \n", + ndisBuffer, Connection->CurrentReceiveMdl, Connection->ReceiveByteOffset); + } + + NdisChainBufferAtFront (ndisPacket, ndisBuffer); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" ProcessIndicateData: Transferring Complete Packet: %lx\n", + ndisPacket); + } + + // + // update the number of bytes received; OK to do this + // unprotected since IndicationInProgress is still FALSE. + // + // + + Connection->MessageBytesReceived += BytesToTransfer; + + // + // We have to do this for two reasons: for MACs that + // are not receive-serialized, to keep track of it, + // and for MACs where transfer data can pend, so + // we have stuff saved to handle failure later (if + // the MAC is synchronous on transfers and it fails, + // we fill these fields in before calling CompleteTransferData). + // + + if (!deviceContext->MacInfo.SingleReceive) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + receiveTag->BytesToTransfer = BytesToTransfer; + if (Connection->TransferBytesPending == 0) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + } else { + Connection->TransferBytesPending += BytesToTransfer; + Connection->TotalTransferBytesPending += BytesToTransfer; + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // We have now updated all the connection counters (BUGBUG + // assuming the TransferData will succeed) and this + // packet's location in the request is secured, so we + // can be reentered. + // + + Connection->IndicationInProgress = FALSE; + + if (Loopback) { + + NbfTransferLoopbackData( + &ndisStatus, + deviceContext, + ReceiveContext, + deviceContext->MacInfo.TransferDataOffset + + DataOffset + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred + ); + + } else { + + NdisTransferData ( + &ndisStatus, + deviceContext->NdisBindingHandle, + ReceiveContext, + deviceContext->MacInfo.TransferDataOffset + + DataOffset + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + } + + // + // handle the various completion codes + // + + if ((ndisStatus == NDIS_STATUS_SUCCESS) && + (ndisBytesTransferred == BytesToTransfer)) { + + // + // deallocate the buffers and such that we've used if at indicate + // + + receiveTag->TransferDataPended = FALSE; + + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + + } else if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // Because TransferDataPended stays TRUE, this reference will + // be removed in TransferDataComplete. It is OK to do this + // now, even though TransferDataComplete may already have been + // called, because we also hold the ProcessIIndicate reference + // so there will be no "bounce". + // + + NbfReferenceConnection ("TransferData pended", Connection, CREF_TRANSFER_DATA); + + } else { + + // + // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status. We set things up + // to that NbfTransferDataComplete will do the right thing. + // + + if (deviceContext->MacInfo.SingleReceive) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + receiveTag->BytesToTransfer = BytesToTransfer; + } + + receiveTag->TransferDataPended = FALSE; + + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + + } + + return status; // which only means we've dealt with the packet + +} /* ProcessIndicateData */ diff --git a/private/ntos/tdi/nbf/info.c b/private/ntos/tdi/nbf/info.c new file mode 100644 index 000000000..f1cf8890b --- /dev/null +++ b/private/ntos/tdi/nbf/info.c @@ -0,0 +1,3487 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + info.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Only the following routine is active in this module. All is is commented +// out waiting for the definition of Get/Set info in TDI version 2. +// + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define NbfGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + +// +// Local functions used to satisfy various requests. +// + +VOID +NbfStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ); + +VOID +NbfStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ); + +VOID +NbfStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ); + + +NTSTATUS +NbfTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + PVOID adapterStatus; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PTA_NETBIOS_ADDRESS broadcastAddress; + PTDI_PROVIDER_STATISTICS ProviderStatistics; + PTDI_CONNECTION_INFO ConnectionInfo; + ULONG TargetBufferLength; + PFIND_NAME_HEADER FindNameHeader; + LARGE_INTEGER timeout = {0,0}; + PTP_REQUEST tpRequest; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + ULONG NamesWritten, TotalNameCount, BytesWritten; + BOOLEAN Truncated; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS TaAddressBuffer; + } AddressInfo; + PTRANSPORT_ADDRESS TaAddress; + TDI_DATAGRAM_INFO DatagramInfo; + BOOLEAN UsedConnection; + PLIST_ENTRY p; + KIRQL oldirql; + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&irpSp->Parameters; + + switch (query->QueryType) { + +#if 0 + case 0x12345678: + + { + typedef struct _NBF_CONNECTION_STATUS { + UCHAR LocalName[16]; + UCHAR RemoteName[16]; + BOOLEAN SendActive; + BOOLEAN ReceiveQueued; + BOOLEAN ReceiveActive; + BOOLEAN ReceiveWakeUp; + ULONG Flags; + ULONG Flags2; + } NBF_CONNECTION_STATUS, *PNBF_CONNECTION_STATUS; + + PNBF_CONNECTION_STATUS CurStatus; + ULONG TotalStatus; + ULONG AllowedStatus; + PLIST_ENRY q; + + CurStatus = MmGetSystemAddressForMdl (Irp->MdlAddress); + TotalStatus = 0; + AllowedStatus = MmGetMdlByteCount (Irp->MdlAddress) / sizeof(NBF_CONNECTION_STATUS); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + for (q = Address->ConnectionDatabase.Flink; + q != &Address->ConnectionDatabase; + q = q->Flink) { + + Connection = CONTAINING_RECORD (q, TP_CONNECTION, AddressList); + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + continue; + } + + if (TotalStatus >= AllowedStatus) { + continue; + } + + RtlMoveMemory (CurStatus->LocalName, Address->NetworkName->NetbiosName, 16); + RtlMoveMemory (CurStatus->RemoteName, Connection->RemoteName, 16); + + CurStatus->Flags = Connection->Flags; + CurStatus->Flags2 = Connection->Flags2; + CurStatus->SendActive = (BOOLEAN)(!IsListEmpty(&Connection->SendQueue)); + CurStatus->ReceiveQueued = (BOOLEAN)(!IsListEmpty(&Connection->ReceiveQueue)); + CurStatus->ReceiveActive = (BOOLEAN)((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0); + CurStatus->ReceiveWakeUp = (BOOLEAN)((Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) != 0); + + ++CurStatus; + ++TotalStatus; + + } + } + + Irp->IoStatus.Information = TotalStatus * sizeof(NBF_CONNECTION_STATUS); + status = STATUS_SUCCESS; + + } + + break; +#endif + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = irpSp->FileObject->FsContext; + + status = NbfVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid Connection %lx Irp %lx\n", Connection, Irp); +#endif + return status; + } + + ConnectionInfo = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TDI_CONNECTION_INFO), + ' FBN'); + + if (ConnectionInfo == NULL) { + + PANIC ("NbfQueryInfo: Cannot allocate connection info!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 6, + sizeof(TDI_CONNECTION_INFO), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + status = STATUS_INVALID_CONNECTION; + ExFreePool (ConnectionInfo); + + } else { + + PTP_LINK Link = Connection->Link; + + RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + + + // + // Get link delay and throughput. + // + + if (Link->Delay == 0xffffffff) { + + // + // If delay is not known, assume 0. + // + + ConnectionInfo->Delay.HighPart = 0; + ConnectionInfo->Delay.LowPart = 0; + + } else { + + // + // Copy the delay as an NT relative time. + // + + ConnectionInfo->Delay.HighPart = -1L; + ConnectionInfo->Delay.LowPart = (ULONG)-((LONG)(Link->Delay)); + + } + + if (DeviceContext->MacInfo.MediumAsync) { + + ULONG PacketsSent; + ULONG PacketsResent; + ULONG MultiplyFactor; + + // + // Calculate the packets sent and resent since the + // last time the throughput was queried. + // + + PacketsSent = Link->PacketsSent - Connection->LastPacketsSent; + PacketsResent = Link->PacketsResent - Connection->LastPacketsResent; + + // + // Save these for next time. + // + + Connection->LastPacketsSent = Link->PacketsSent; + Connection->LastPacketsResent = Link->PacketsResent; + + // + // To convert exactly from 100 bits-per-second to + // bytes-per-second, we need to multiply by 12.5. + // Using lower numbers will give worse throughput. + // If there have been no errors we use 12, if there + // have been 20% or more errors we use 1, and in + // between we subtract 11 * (error%/20%) from 12 + // and use that. + // + + if (PacketsResent == 0 || PacketsSent <= 10) { + + MultiplyFactor = 12; + + } else if ((PacketsSent / PacketsResent) <= 5) { + + MultiplyFactor = 1; + + } else { + + // + // error%/20% is error%/(1/5), which is 5*error%, + // which is 5 * (resent/send). + // + + ASSERT (((11 * 5 * PacketsResent) / PacketsSent) <= 11); + MultiplyFactor = 12 - ((11 * 5 * PacketsResent) / PacketsSent); + + } + + ConnectionInfo->Throughput.QuadPart = + UInt32x32To64(DeviceContext->MediumSpeed, MultiplyFactor); + + } else if (!Link->ThroughputAccurate) { + + // + // If throughput is not known, then guess. We + // have MediumSpeed in units of 100 bps; we + // return four times that number as the throughput, + // which corresponds to about 1/3 of the + // maximum bandwidth expressed in bytes/sec. + // + + ConnectionInfo->Throughput.QuadPart = + UInt32x32To64(DeviceContext->MediumSpeed, 4); + + } else { + + // + // Throughput is accurate, return it. + // + + ConnectionInfo->Throughput = Link->Throughput; + + } + + + // + // Calculate reliability using the sent/resent ratio, + // if there has been enough activity to make it + // worthwhile. >10% resent is unreliable. + // + + if ((Link->PacketsResent > 0) && + (Link->PacketsSent > 20)) { + + ConnectionInfo->Unreliable = + ((Link->PacketsSent / Link->PacketsResent) < 10); + + } else { + + ConnectionInfo->Unreliable = FALSE; + + } + + ConnectionInfo->TransmittedTsdus = Connection->TransmittedTsdus; + ConnectionInfo->ReceivedTsdus = Connection->ReceivedTsdus; + ConnectionInfo->TransmissionErrors = Connection->TransmissionErrors; + ConnectionInfo->ReceiveErrors = Connection->ReceiveErrors; + + status = TdiCopyBufferToMdl ( + (PVOID)ConnectionInfo, + 0L, + sizeof(TDI_CONNECTION_INFO), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ConnectionInfo); + } + + NbfDereferenceConnection ("query connection info", Connection, CREF_BY_ID); + + break; + + case TDI_QUERY_ADDRESS_INFO: + + if (irpSp->FileObject->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid AddressFile %lx Irp %lx\n", AddressFile, Irp); +#endif + return status; + } + + UsedConnection = FALSE; + + } else if (irpSp->FileObject->FsContext2 == (PVOID)TDI_CONNECTION_FILE) { + + Connection = irpSp->FileObject->FsContext; + + status = NbfVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid Connection %lx Irp %lx\n", Connection, Irp); +#endif + return status; + } + + AddressFile = Connection->AddressFile; + + UsedConnection = TRUE; + + } else { + + return STATUS_INVALID_ADDRESS; + + } + + Address = AddressFile->Address; + + TdiBuildNetbiosAddress( + Address->NetworkName->NetbiosName, + (BOOLEAN)(Address->Flags & ADDRESS_FLAGS_GROUP ? TRUE : FALSE), + &AddressInfo.TaAddressBuffer); + + // + // Count the active addresses. + // + + AddressInfo.ActivityCount = 0; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + ++AddressInfo.ActivityCount; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + status = TdiCopyBufferToMdl ( + &AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (UsedConnection) { + + NbfDereferenceConnection ("query address info", Connection, CREF_BY_ID); + + } else { + + NbfDereferenceAddress ("query address info", Address, AREF_VERIFY); + + } + + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + broadcastAddress = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TA_NETBIOS_ADDRESS), + ' FBN'); + if (broadcastAddress == NULL) { + PANIC ("NbfQueryInfo: Cannot allocate broadcast address!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 2, + sizeof(TA_NETBIOS_ADDRESS), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + } else { + + broadcastAddress->TAAddressCount = 1; + broadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + broadcastAddress->Address[0].AddressLength = 0; + + Irp->IoStatus.Information = + sizeof (broadcastAddress->TAAddressCount) + + sizeof (broadcastAddress->Address[0].AddressType) + + sizeof (broadcastAddress->Address[0].AddressLength); + + status = TdiCopyBufferToMdl ( + (PVOID)broadcastAddress, + 0L, + Irp->IoStatus.Information, + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (broadcastAddress); + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(DeviceContext->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BUGBUG: This information is probablt available somewhere else. + // + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + if (TargetBufferLength < sizeof(TDI_PROVIDER_STATISTICS) + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))) { + + Irp->IoStatus.Information = 0; + status = STATUS_BUFFER_OVERFLOW; + + } else { + + ProviderStatistics = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(TDI_PROVIDER_STATISTICS) + + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), + ' FBN'); + + if (ProviderStatistics == NULL) { + + PANIC ("NbfQueryInfo: Cannot allocate provider statistics!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 7, + sizeof(TDI_PROVIDER_STATISTICS), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + NbfStoreProviderStatistics (DeviceContext, ProviderStatistics); + + status = TdiCopyBufferToMdl ( + (PVOID)ProviderStatistics, + 0L, + sizeof(TDI_PROVIDER_STATISTICS) + + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ProviderStatistics); + } + + } + + break; + + case TDI_QUERY_SESSION_STATUS: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADAPTER_STATUS: + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + // + // Determine if this is a local or remote query. It is + // local if there is no remote address specific at all, + // or if it is equal to our reserved address. + // + + RemoteAdapterStatus = FALSE; + + if (query->RequestConnectionInformation != NULL) { + + if (!NbfValidateTdiAddress( + query->RequestConnectionInformation->RemoteAddress, + query->RequestConnectionInformation->RemoteAddressLength)) { + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress(query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (!RemoteAddress) { + return STATUS_BAD_NETWORK_PATH; + } + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + RemoteAdapterStatus = TRUE; + + } + } + + if (RemoteAdapterStatus) { + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the device context. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + DeviceContext, // context. + REQUEST_FLAGS_DC, // partial flags. + Irp->MdlAddress, // the data to be received. + TargetBufferLength, // length of the data. + timeout, // do this ourselves here. + &tpRequest); + + if (NT_SUCCESS (status)) { + + NbfReferenceDeviceContext ("Remote status", DeviceContext, DCREF_REQUEST); + tpRequest->Owner = DeviceContextType; + + // + // Allocate a temp buffer to hold our results. + // + + tpRequest->ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (tpRequest->ResponseBuffer == NULL) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 12, + TargetBufferLength, + 0); + NbfCompleteRequest (tpRequest, STATUS_INSUFFICIENT_RESOURCES, 0); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); + + } else { + + PUCHAR SingleSR; + UINT SingleSRLength; + + InsertTailList ( + &DeviceContext->StatusQueryQueue, + &tpRequest->Linkage); + + tpRequest->FrameContext = DeviceContext->UniqueIdentifier | 0x8000; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The request is queued. Now send out the first packet and + // start the timer. + // + + tpRequest->Retries = DeviceContext->GeneralRetries; + tpRequest->BytesWritten = 0; + + // + // STATUS_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + NbfSendStatusQuery( + DeviceContext, + tpRequest, + &DeviceContext->NetBIOSAddress, + SingleSR, + SingleSRLength); + + } + + } + + // + // As long as the request is created, pend here. + // The IRP will complete when the request completes. + // + + status = STATUS_PENDING; + + } + + } else { + + // + // Local. + // + + adapterStatus = ExAllocatePoolWithTag ( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (adapterStatus == NULL) { + PANIC("NbfQueryInfo: PANIC! Could not allocate adapter status buffer\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 3, + TargetBufferLength, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NbfStoreAdapterStatus ( + DeviceContext, + NULL, + 0, + adapterStatus); + + NbfStoreNameBuffers ( + DeviceContext, + (PUCHAR)adapterStatus + sizeof(ADAPTER_STATUS), + TargetBufferLength - sizeof(ADAPTER_STATUS), + 0, + &NamesWritten, + &TotalNameCount, + &Truncated); + + ((PADAPTER_STATUS)adapterStatus)->name_count = (WORD)TotalNameCount; + + BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); + + status = TdiCopyBufferToMdl ( + adapterStatus, + 0, + BytesWritten, + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (Truncated) { + status = STATUS_BUFFER_OVERFLOW; + } + + ExFreePool (adapterStatus); + + } + + break; + + case TDI_QUERY_FIND_NAME: + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + // + // Check that there is a valid Netbios remote address. + // + + if (!NbfValidateTdiAddress( + query->RequestConnectionInformation->RemoteAddress, + query->RequestConnectionInformation->RemoteAddressLength)) { + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress(query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (!RemoteAddress) { + return STATUS_BAD_NETWORK_PATH; + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the device context. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + DeviceContext, // context. + REQUEST_FLAGS_DC, // partial flags. + Irp->MdlAddress, // the data to be received. + TargetBufferLength, // length of the data. + timeout, // do this ourselves here. + &tpRequest); + + if (NT_SUCCESS (status)) { + + NbfReferenceDeviceContext ("Find name", DeviceContext, DCREF_REQUEST); + tpRequest->Owner = DeviceContextType; + + // + // Allocate a temp buffer to hold our results. + // + + tpRequest->ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (tpRequest->ResponseBuffer == NULL) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 4, + TargetBufferLength, + 0); + NbfCompleteRequest (tpRequest, STATUS_INSUFFICIENT_RESOURCES, 0); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); + + } else { + + InsertTailList ( + &DeviceContext->FindNameQueue, + &tpRequest->Linkage); + + tpRequest->FrameContext = DeviceContext->UniqueIdentifier | 0x8000; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The request is queued. Now send out the first packet and + // start the timer. + // + // We fill in the FIND_NAME_HEADER in the buffer, but + // set BytesWritten to 0; we don't include the header + // in BytesWritten until we get a response, so that + // a BytesWritten of 0 means "no response". + // + + tpRequest->Retries = DeviceContext->GeneralRetries; + tpRequest->BytesWritten = 0; + FindNameHeader = (PFIND_NAME_HEADER)tpRequest->ResponseBuffer; + FindNameHeader->node_count = 0; + FindNameHeader->unique_group = NETBIOS_NAME_TYPE_UNIQUE; + + NbfSendQueryFindName (DeviceContext, tpRequest); + + } + + } + + // + // As long as the request is created, pend here. + // The IRP will complete when the request completes. + // + + status = STATUS_PENDING; + } + + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS: + + TaAddress = (PTRANSPORT_ADDRESS)&AddressInfo.TaAddressBuffer; + TaAddress->TAAddressCount = 1; + TaAddress->Address[0].AddressLength = 6; + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + TaAddress->Address[0].AddressType = + DeviceContext->MacInfo.MediumAsync ? + NdisMediumWan : DeviceContext->MacInfo.MediumType; + } else { + TaAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_UNSPEC; + } + RtlCopyMemory (TaAddress->Address[0].Address, DeviceContext->LocalAddress.Address, 6); + + status = TdiCopyBufferToMdl ( + &AddressInfo.TaAddressBuffer, + 0, + sizeof(TRANSPORT_ADDRESS)+5, + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + DatagramInfo.MaximumDatagramBytes = 0; + DatagramInfo.MaximumDatagramCount = 0; + + status = TdiCopyBufferToMdl ( + &DatagramInfo, + 0, + sizeof(DatagramInfo), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* NbfTdiQueryInformation */ + +// +// Quick macros, assumes DeviceContext and ProviderStatistics exist. +// + +#define STORE_RESOURCE_STATS_1(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## MaxInUse; \ + if (DeviceContext->_ResourceName ## Samples > 0) { \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Total / DeviceContext->_ResourceName ## Samples; \ + } else { \ + RStats->AverageResourceUsed = 0; \ + } \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + +#define STORE_RESOURCE_STATS_2(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + + +VOID +NbfStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ) + +/*++ + +Routine Description: + + This routine writes the TDI_PROVIDER_STATISTICS structure + from the device context into ProviderStatistics. + +Arguments: + + DeviceContext - a pointer to the device context. + + ProviderStatistics - The buffer that holds the result. It is assumed + that it is long enough. + +Return Value: + + None. + +--*/ + +{ + + // + // Copy all the statistics up to NumberOfResources + // in one move. + // + + RtlCopyMemory( + ProviderStatistics, + &DeviceContext->Statistics, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, NumberOfResources)); + + // + // Calculate AverageSendWindow. + // + + if (DeviceContext->SendWindowSamples > 0) { + ProviderStatistics->AverageSendWindow = + DeviceContext->SendWindowTotal / DeviceContext->SendWindowSamples; + } else { + ProviderStatistics->AverageSendWindow = 1; + } + + // + // Copy the resource statistics. + // + + ProviderStatistics->NumberOfResources = NBF_TDI_RESOURCES; + + STORE_RESOURCE_STATS_1 (0, LINK_RESOURCE_ID, Link); + STORE_RESOURCE_STATS_1 (1, ADDRESS_RESOURCE_ID, Address); + STORE_RESOURCE_STATS_1 (2, ADDRESS_FILE_RESOURCE_ID, AddressFile); + STORE_RESOURCE_STATS_1 (3, CONNECTION_RESOURCE_ID, Connection); + STORE_RESOURCE_STATS_1 (4, REQUEST_RESOURCE_ID, Request); + + STORE_RESOURCE_STATS_2 (5, UI_FRAME_RESOURCE_ID, UIFrame); + STORE_RESOURCE_STATS_2 (6, PACKET_RESOURCE_ID, Packet); + STORE_RESOURCE_STATS_2 (7, RECEIVE_PACKET_RESOURCE_ID, ReceivePacket); + STORE_RESOURCE_STATS_2 (8, RECEIVE_BUFFER_RESOURCE_ID, ReceiveBuffer); + +} /* NbfStoreProviderStatistics */ + + +VOID +NbfStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ) + +/*++ + +Routine Description: + + This routine writes the ADAPTER_STATUS structure for the + device context into StatusBuffer. The name_count field is + initialized to zero; NbfStoreNameBuffers is used to write + name buffers. + +Arguments: + + DeviceContext - a pointer to the device context. + + SourceRouting - If this is a remote request, the source + routing information from the frame. + + SourceRoutingLength - The length of SourceRouting. + + StatusBuffer - The buffer that holds the result. It is assumed + that it is at least sizeof(ADAPTER_STATUS) bytes long. + +Return Value: + + None. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus = (PADAPTER_STATUS)StatusBuffer; + UINT MaxUserData; + + RtlZeroMemory ((PVOID)AdapterStatus, sizeof(ADAPTER_STATUS)); + + RtlCopyMemory (AdapterStatus->adapter_address, DeviceContext->LocalAddress.Address, 6); + AdapterStatus->rev_major = 0x03; + + switch (DeviceContext->MacInfo.MediumType) { + case NdisMedium802_5: AdapterStatus->adapter_type = 0xff; break; + default: AdapterStatus->adapter_type = 0xfe; break; + } + + AdapterStatus->frmr_recv = (WORD)DeviceContext->FrmrReceived; + AdapterStatus->frmr_xmit = (WORD)DeviceContext->FrmrTransmitted; + + AdapterStatus->recv_buff_unavail = (WORD)(DeviceContext->ReceivePacketExhausted + DeviceContext->ReceiveBufferExhausted); + AdapterStatus->xmit_buf_unavail = (WORD)DeviceContext->PacketExhausted; + + AdapterStatus->xmit_success = (WORD)(DeviceContext->Statistics.DataFramesSent - DeviceContext->Statistics.DataFramesResent); + AdapterStatus->recv_success = (WORD)DeviceContext->Statistics.DataFramesReceived; + AdapterStatus->iframe_recv_err = (WORD)DeviceContext->Statistics.DataFramesRejected; + AdapterStatus->iframe_xmit_err = (WORD)DeviceContext->Statistics.DataFramesResent; + + AdapterStatus->t1_timeouts = (WORD)DeviceContext->Statistics.ResponseTimerExpirations; + AdapterStatus->ti_timeouts = (WORD)DeviceContext->TiExpirations; + AdapterStatus->xmit_aborts = (WORD)0; + + + AdapterStatus->free_ncbs = (WORD)0xffff; + AdapterStatus->max_cfg_ncbs = (WORD)0xffff; + AdapterStatus->max_ncbs = (WORD)0xffff; + AdapterStatus->pending_sess = (WORD)DeviceContext->Statistics.OpenConnections; + AdapterStatus->max_cfg_sess = (WORD)0xffff; + AdapterStatus->max_sess = (WORD)0xffff; + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + TRUE, + &MaxUserData); + AdapterStatus->max_dgram_size = (WORD)(MaxUserData - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS))); + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->MaxSendPacketSize, + FALSE, + &MaxUserData); + AdapterStatus->max_sess_pkt_size = (WORD)(MaxUserData - (sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION))); + + return; + +} /* NbfStoreAdapterStatus */ + + +VOID +NbfStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ) + +/*++ + +Routine Description: + + This routine writes NAME_BUFFER structures for the + device context into NameBuffer. It can skip a specified + number of names at the beginning, and returns the number + of names written into NameBuffer. If a name will only + partially fit, it is not written. + +Arguments: + + DeviceContext - a pointer to the device context. + + NameBuffer - The buffer to write the names into. + + NameBufferLength - The length of NameBuffer. + + NamesToSkip - The number of names to skip. + + NamesWritten - Returns the number of names written. + + TotalNameCount - Returns the total number of names available, + if specified. + + Truncated - More names are available than were written. + +Return Value: + + None. + +--*/ + +{ + + ULONG NameCount = 0; + ULONG BytesWritten = 0; + KIRQL oldirql; + PLIST_ENTRY p; + PNAME_BUFFER NameBuffer = (PNAME_BUFFER)Buffer; + PTP_ADDRESS address; + + + // + // Spin through the address list for this device context. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + // + // Check if we are still skipping. + // + + if (NameCount < NamesToSkip) { + ++NameCount; + continue; + } + + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > BufferLength) { + break; + } + + RtlCopyMemory( + NameBuffer->name, + address->NetworkName->NetbiosName, + NETBIOS_NAME_LENGTH); + + ++NameCount; + NameBuffer->name_num = (UCHAR)NameCount; + + NameBuffer->name_flags = REGISTERED; + if (address->Flags & ADDRESS_FLAGS_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // BUGBUG: name_flags should be done more accurately. + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + *NamesWritten = NameBuffer - (PNAME_BUFFER)Buffer; + + if (p == &DeviceContext->AddressDatabase) { + + *Truncated = FALSE; + if (ARGUMENT_PRESENT(TotalNameCount)) { + *TotalNameCount = NameCount; + } + + } else { + + *Truncated = TRUE; + + // + // If requested, continue through the list and count + // all the addresses. + // + + if (ARGUMENT_PRESENT(TotalNameCount)) { + + for ( ; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address, since we count it no matter what. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + ++NameCount; + + } + + *TotalNameCount = NameCount; + + } + + } + + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + return; + +} /* NbfStoreNameBuffers */ + + +NTSTATUS +NbfProcessStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address OPTIONAL, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes a STATUS.QUERY packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + Address - The address we are responding from, or NULL if the STATUS.QUERY + was sent to the reserved address. + + UiFrame - The packet in question, starting at the Netbios header. + + SourceAddress - The source hardware address of the packet. + + SourceRouting - Source routing data in the query. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PTP_UI_FRAME RawFrame; + PVOID ResponseBuffer; + UINT ResponseBufferLength; + ULONG NamesWritten, TotalNameCount; + ULONG BytesWritten; + UCHAR RequestType; + BOOLEAN Truncated, UsersBufferTooShort; + USHORT UsersBufferLength; + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + PNDIS_BUFFER NdisBuffer; + + // + // Allocate a buffer to hold the status. + // + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->CurSendPacketSize, + FALSE, + &ResponseBufferLength); + + ResponseBufferLength -= (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + + UsersBufferLength = (UiFrame->Data2High * 256) + UiFrame->Data2Low; + + // + // See how big to make our buffer; if the amount remaining in the user's + // buffer is less than our max size, chop it down. + // + + if (UiFrame->Data1 <= 1) { + + // + // This is the initial request. + // + + if (ResponseBufferLength > (UINT)UsersBufferLength) { + ResponseBufferLength = UsersBufferLength; + } + + } else { + + // + // Subsequent request; compensate for already-sent data. + // + + UsersBufferLength -= (sizeof(ADAPTER_STATUS) + (UiFrame->Data1 * sizeof(NAME_BUFFER))); + + if (ResponseBufferLength > (UINT)UsersBufferLength) { + ResponseBufferLength = UsersBufferLength; + } + + } + + // + // If the remote station is asking for no data, ignore this request. + // This prevents us from trying to allocate 0 bytes of pool. + // + + if ( (LONG)ResponseBufferLength <= 0 ) { + return STATUS_ABANDONED; + } + + ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + ResponseBufferLength, + ' FBN'); + + if (ResponseBuffer == NULL) { + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 5, + ResponseBufferLength, + 0); + return STATUS_ABANDONED; + } + + + // + // Fill in the response buffer. + // + + if (UiFrame->Data1 <= 1) { + + // + // First request. + // + + NbfStoreAdapterStatus ( + DeviceContext, + SourceRouting, + SourceRoutingLength, + ResponseBuffer); + + NbfStoreNameBuffers ( + DeviceContext, + (PUCHAR)ResponseBuffer + sizeof(ADAPTER_STATUS), + ResponseBufferLength - sizeof(ADAPTER_STATUS), + 0, + &NamesWritten, + &TotalNameCount, + &Truncated); + + BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); + + // + // If the data was truncated, but we are returning the maximum + // that the user requested, report that as "user's buffer + // too short" instead of "truncated". + // + + if (Truncated && (ResponseBufferLength >= (UINT)UsersBufferLength)) { + Truncated = FALSE; + UsersBufferTooShort = TRUE; + } else { + UsersBufferTooShort = FALSE; + } + + ((PADAPTER_STATUS)ResponseBuffer)->name_count = (WORD)TotalNameCount; + + } else { + + NbfStoreNameBuffers ( + DeviceContext, + ResponseBuffer, + ResponseBufferLength, + UiFrame->Data1, + &NamesWritten, + NULL, + &Truncated); + + BytesWritten = NamesWritten * sizeof(NAME_BUFFER); + + if (Truncated && (ResponseBufferLength >= (UINT)UsersBufferLength)) { + Truncated = FALSE; + UsersBufferTooShort = TRUE; + } else { + UsersBufferTooShort = FALSE; + } + + } + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + ExFreePool (ResponseBuffer); + return STATUS_ABANDONED; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfProcessStatusQuery: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. STATUS_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS) + BytesWritten, + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + switch (UiFrame->Data1) { + case 0: // pre 2.1 request + RequestType = (UCHAR)0; + break; + case 1: // 2.1, first request + RequestType = (UCHAR)NamesWritten; + break; + default: // 2.1, subsequent request + RequestType = (UCHAR)(UiFrame->Data1 + NamesWritten); + break; + } + + ConstructStatusResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + RequestType, // request type. + Truncated, // more data. + UsersBufferTooShort, // user's buffer too small + (USHORT)BytesWritten, // bytes in response + RESPONSE_CORR(UiFrame), // correlator + UiFrame->SourceName, // receiver permanent name + (ARGUMENT_PRESENT(Address)) ? + Address->NetworkName->NetbiosName : + DeviceContext->ReservedNetBIOSAddress); // source name + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length (now, before we append the second + // buffer). + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + + // + // Now, if we have any name data, attach our buffer onto the frame. + // Note that it's possible at the end of the user's buffer for us + // to not have room for any names, and thus we'll have no data to + // send. + // + + if ( BytesWritten != 0 ) { + + RawFrame->DataBuffer = ResponseBuffer; + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + ResponseBuffer, + BytesWritten); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC ("ConstructStatusResponse: NdisAllocateBuffer failed.\n"); + NbfDestroyConnectionlessFrame (DeviceContext, RawFrame); + return STATUS_ABANDONED; + } + + NdisChainBufferAtBack (RawFrame->NdisPacket, NdisBuffer); + + } else { + + RawFrame->DataBuffer = NULL; + + } + + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + + return STATUS_ABANDONED; + +} /* NbfProcessStatusQuery */ + + +VOID +NbfSendQueryFindName( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine will send a FIND.NAME packet for the specified + find name request, and start the request timer. + +Arguments: + + DeviceContext - a pointer to the device context to send the find name on. + + Request - The find name request. + +Return Value: + + None. + +--*/ + +{ + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + NTSTATUS Status; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + LARGE_INTEGER Timeout; + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + + remoteAddress = NbfParseTdiAddress( + ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)(&irpSp->Parameters))-> + RequestConnectionInformation->RemoteAddress, FALSE); + + // + // Start the timer for this request. + // + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Find Name: timer", Request, RREF_TIMER); // one for the timer + Timeout.LowPart = (ULONG)(-(LONG)DeviceContext->GeneralTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfSendFindNames: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // call from a unique name. + NAME_QUERY_LSN_FIND_NAME, // LSN + Request->FrameContext, // corr. in 1st NAME_RECOGNIZED. + DeviceContext->ReservedNetBIOSAddress, + (PNAME)remoteAddress->NetbiosName); + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendQueryFindName */ + + +NTSTATUS +NbfProcessQueryNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Packet, + PNBF_HDR_CONNECTIONLESS UiFrame + ) + +/*++ + +Routine Description: + + This routine processes a NAME.RECOGNIZED request with a + correlator of 0, indicating it was a response to a previous + FIND.NAME packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + Packet - The packet in question, starting at the MAC header. + + UiFrame - The packet, starting at the Netbios header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PTP_REQUEST Request; + PFIND_NAME_BUFFER FindNameBuffer; + PFIND_NAME_HEADER FindNameHeader; + PUCHAR DestinationAddress; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PUCHAR TargetBuffer; + USHORT FrameContext; + PLIST_ENTRY p; + + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + Packet, + &DestinationAddress); + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + Packet, + &SourceAddressBuffer, + &SourceAddress, + NULL); + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + Packet, + &SourceRouting, + &SourceRoutingLength); + + // + // Find the request that this is for, using the frame context. + // + + FrameContext = TRANSMIT_CORR(UiFrame); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (p=DeviceContext->FindNameQueue.Flink; + p != &DeviceContext->FindNameQueue; + p=p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->FrameContext == FrameContext) { + + break; + + } + + } + + if (p == &DeviceContext->FindNameQueue) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + + } + + NbfReferenceRequest ("Name Recognized", Request, RREF_FIND_NAME); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // Make sure that this physical address has not + // responded yet. + // + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + // + // Make sure this request is not stopping. + // + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Stopping", Request, RREF_STATUS); + return STATUS_SUCCESS; + } + + // + // If this is the first response, update BytesWritten to include + // the header that is already written in ResponseBuffer. + // + + if (Request->BytesWritten == 0) { + Request->BytesWritten = sizeof(FIND_NAME_HEADER); + } + + TargetBuffer = Request->ResponseBuffer; + FindNameBuffer = (PFIND_NAME_BUFFER)(TargetBuffer + sizeof(FIND_NAME_HEADER)); + + for ( ; FindNameBuffer < (PFIND_NAME_BUFFER)(TargetBuffer + Request->BytesWritten); FindNameBuffer++) { + + if (RtlEqualMemory (FindNameBuffer->source_addr, SourceAddress->Address, 6)) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Duplicate NR", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + + } + + } + + // + // This is a new address, update if there is room. + // + + if ((Request->BytesWritten + sizeof(FIND_NAME_BUFFER)) > + Request->Buffer2Length) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfCompleteRequest (Request, STATUS_SUCCESS, Request->BytesWritten); + NbfDereferenceRequest ("No Buffer", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + + } + + FindNameHeader = (PFIND_NAME_HEADER)TargetBuffer; + FindNameHeader->unique_group = UiFrame->Data2High; + + Request->BytesWritten += sizeof(FIND_NAME_BUFFER); + ++FindNameHeader->node_count; + + RtlCopyMemory(FindNameBuffer->source_addr, SourceAddress->Address, 6); + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + RtlCopyMemory(FindNameBuffer->destination_addr, DestinationAddress, 6); + FindNameBuffer->length = 14; + + if (DeviceContext->MacInfo.MediumType == NdisMedium802_5) { + + // + // token-ring, copy the correct fields. + // + + FindNameBuffer->access_control = Packet[0]; + FindNameBuffer->frame_control = Packet[1]; + + if (SourceRouting != NULL) { + RtlCopyMemory (FindNameBuffer->routing_info, SourceRouting, SourceRoutingLength); + FindNameBuffer->length += SourceRoutingLength; + } + + } else { + + // + // non-token-ring, nothing else is significant. + // + + FindNameBuffer->access_control = 0x0; + FindNameBuffer->frame_control = 0x0; + + } + + + // + // If this is a unique name, complete the request now. + // + + if (UiFrame->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfCompleteRequest(Request, STATUS_SUCCESS, Request->BytesWritten); + + } + + NbfDereferenceRequest ("NR processed", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + +} /* NbfProcessQueryNameRecognized */ + + +VOID +NbfSendStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request, + IN PHARDWARE_ADDRESS DestinationAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine will send a STATUS.NAME packet for the specified + find name request, and start the request timer. + +Arguments: + + DeviceContext - a pointer to the device context to send the status query on. + + Request - The find name request. + + DestinationAddress - The hardware destination address of the frame. + + SourceRouting - Optional source routing information in the frame. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + NTSTATUS Status; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + LARGE_INTEGER Timeout; + UCHAR RequestType; + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + + remoteAddress = NbfParseTdiAddress( + ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)(&irpSp->Parameters))-> + RequestConnectionInformation->RemoteAddress, FALSE); + + // + // Start the timer for this request. + // + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Find Name: timer", Request, RREF_TIMER); // one for the timer + Timeout.LowPart = (ULONG)(-(LONG)DeviceContext->GeneralTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfSendFindNames: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. STATUS_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + // + // Determine what RequestType should be. + // + + if (Request->BytesWritten == 0) { + + // + // No way to know if he is 2.1 or not, so we put a 1 here + // instead of 0. + // + + RequestType = 1; + + } else { + + RequestType = (UCHAR)((Request->BytesWritten - sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER)); + + } + + ConstructStatusQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + RequestType, // request status type. + (USHORT)Request->Buffer2Length, // user's buffer length + Request->FrameContext, // corr. in 1st NAME_RECOGNIZED. + (PNAME)remoteAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress); + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendStatusQuery */ + + +NTSTATUS +NbfProcessStatusResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes a STATUS.RESPONSE packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + ReceiveContext - The context for calling NdisTransferData. + + UiFrame - The packet in question, starting at the Netbios header. + + SourceAddress - The source hardware address of the packet. + + SourceRouting - Source routing data in the query. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PTP_REQUEST Request; + PUCHAR TargetBuffer; + USHORT FrameContext; + USHORT NamesReceived; + USHORT ResponseLength, ResponseBytesToCopy; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY linkage; + NDIS_STATUS ndisStatus; + PNDIS_BUFFER NdisBuffer; + PNDIS_PACKET ndisPacket; + ULONG ndisBytesTransferred; + PRECEIVE_PACKET_TAG receiveTag; + NDIS_STATUS NdisStatus; + + + // + // Find the request that this is for, using the frame context. + // + + FrameContext = TRANSMIT_CORR(UiFrame); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (p=DeviceContext->StatusQueryQueue.Flink; + p != &DeviceContext->StatusQueryQueue; + p=p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->FrameContext == FrameContext) { + + break; + + } + + } + + if (p == &DeviceContext->StatusQueryQueue) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + + } + + NbfReferenceRequest ("Status Response", Request, RREF_STATUS); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + // + // Make sure this request is not stopping. + // + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Stopping", Request, RREF_STATUS); + return STATUS_SUCCESS; + } + + // + // See if this is packet has new data. + // + + if (Request->BytesWritten == 0) { + + NamesReceived = 0; + + } else { + + NamesReceived = (USHORT)(Request->BytesWritten - sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER); + + } + + if ((UiFrame->Data1 > 0) && (UiFrame->Data1 <= NamesReceived)) { + + // + // If it is a post-2.1 response, but we already got + // this data, ignore it. + // + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Duplicate SR", Request, RREF_STATUS); + return STATUS_SUCCESS; + + } + + + // + // This is new data, append if there is room. + // + + ResponseLength = ((UiFrame->Data2High & 0x3f) * 256) + UiFrame->Data2Low; + + if ((ULONG)(Request->BytesWritten + ResponseLength) > + Request->Buffer2Length) { + + ResponseBytesToCopy = (USHORT)(Request->Buffer2Length - Request->BytesWritten); + + } else { + + ResponseBytesToCopy = ResponseLength; + + } + + // + // Allocate a receive packer for this operation. + // + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + + // + // Could not get a packet, oh well, it is connectionless. + // + + DeviceContext->ReceivePacketExhausted++; + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved); + receiveTag->PacketType = TYPE_STATUS_RESPONSE; + receiveTag->Connection = (PTP_CONNECTION)Request; + + TargetBuffer = (PUCHAR)Request->ResponseBuffer + Request->BytesWritten; + + // + // Allocate an MDL to describe the part of the buffer we + // want transferred. + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + TargetBuffer, + ResponseBytesToCopy); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + linkage, + &DeviceContext->Interlock); + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + // + // Assume success, if not we fail the request. + // + + Request->BytesWritten += ResponseBytesToCopy; + + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + NdisChainBufferAtFront(ndisPacket, NdisBuffer); + + // + // See if the response was too big (we can complete the + // request here since we still reference it). + // + + if ((ResponseLength > ResponseBytesToCopy) || + (UiFrame->Data2High & 0x40)) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + receiveTag->CompleteReceive = TRUE; + receiveTag->EndOfMessage = FALSE; + + } else { + + // + // If we are done, complete the packet, otherwise send off + // the next request (unless it is a pre-2.1 response). + // + + if ((UiFrame->Data1 > 0) && (UiFrame->Data2High & 0x80)) { + + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + receiveTag->CompleteReceive = FALSE; + + // + // Try to cancel the timer, no harm if we fail. + // + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + if (KeCancelTimer (&Request->Timer)) { + NbfDereferenceRequest ("Status Response: stop timer", Request, RREF_TIMER); + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Request->Retries = DeviceContext->GeneralRetries; + + // + // Send a STATUS_QUERY directed. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + NbfSendStatusQuery( + DeviceContext, + Request, + SourceAddress, + ResponseSR, + SourceRoutingLength); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + receiveTag->CompleteReceive = TRUE; + receiveTag->EndOfMessage = TRUE; + + } + + } + + // + // Now do the actual data transfer. + // + + NdisTransferData ( + &ndisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset + + 3 + sizeof(NBF_HDR_CONNECTIONLESS), + ResponseBytesToCopy, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + if (ndisStatus != NDIS_STATUS_PENDING) { + + NbfTransferDataComplete( + (NDIS_HANDLE)DeviceContext, + ndisPacket, + ndisStatus, + ndisBytesTransferred); + + } + + return STATUS_SUCCESS; + +} /* NbfProcessStatusResponse */ + + +NTSTATUS +NbfTdiSetInformation( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Irp); // prevent compiler warnings + + return STATUS_NOT_IMPLEMENTED; + +} /* NbfTdiQueryInformation */ + +#if 0 + +NTSTATUS +NbfQueryInfoEndpoint( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_QUERY_INFORMATION TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_ENDPOINT_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified endpoint. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_ENDPOINT_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_SPIN_LOCK (&Endpoint->SpinLock, &oldirql); + + *InfoBuffer = Endpoint->Information; // structure copy. + + RELEASE_SPIN_LOCK (&Endpoint->SpinLock, oldirql); + + *InformationSize = sizeof (Endpoint->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoEndpoint */ + + +NTSTATUS +NbfQueryInfoConnection( + IN PTP_CONNECTION Connection, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_CONNECTION_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified connection. + +Arguments: + + Connection - Pointer to transport connection object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_CONNECTION_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + *InfoBuffer = Connection->Information; // structure copy. + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + *InformationSize = sizeof (Connection->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoConnection */ + + +NTSTATUS +NbfQueryInfoAddress( + IN PTP_ADDRESS Address, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_ADDRESS_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified address. We + don't acquire a spinlock in this routine because there are no statistics + which must be read atomically. + +Arguments: + + Address - Pointer to transport address object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + SHORT i; + PSZ p, q; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + // + // Calculate whether his buffer is big enough to return the entire + // information. The total size of the address information is the + // size of the fixed part, plus the size of the variable-length flat + // string in the NETWORK_NAME component of the TRANSPORT_ADDRESS + // component. + // + + if (InfoBufferLength < + sizeof (TDI_ADDRESS_INFO) + + Address->NetworkName.Length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy both the fixed part of the address information, and the variable + // part. The variable part comes from the NETWORK_NAME component of the + // TRANSPORT_ADDRESS structure. This component contains a FLAT_STRING, + // which is of variable length. + // + + InfoBuffer->Address.AddressComponents = Address->AddressComponents; + InfoBuffer->Address.Tsap = Address->Tsap; + + InfoBuffer->Address.NetworkName.Name.Length = + Address->NetworkName.Length; + + p = Address->NetworkName.Buffer; // p = ptr, source string. + q = InfoBuffer->Address.NetworkName.Name.Buffer; // q = ptr, dest string. + for (i=0; iAddress.NetworkName.Name.Length; i++) { + *(q++) = *(p++); + } + + *InformationSize = sizeof (TDI_ADDRESS_INFO) + + Address->NetworkName.Length; + + return STATUS_SUCCESS; +} /* NbfQueryInfoAddress */ + + +NTSTATUS +NbfQueryInfoProvider( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_PROVIDER_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the transport provider. + +Arguments: + + Provider - Pointer to device context for provider. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_PROVIDER_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + + *InfoBuffer = Provider->Information; // structure copy. + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + *InformationSize = sizeof (Provider->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoProvider */ + + +NTSTATUS +NbfQueryInfoNetman( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_NETMAN_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified network-managable + variable managed by the transport provider. + +Arguments: + + Provider - Pointer to device context for provider. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PFLAT_STRING p; + PTP_VARIABLE v; + PTDI_NETMAN_VARIABLE n; + USHORT i; + ULONG NameOffset, ValueOffset; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + InfoBufferLength, InformationSize; + + // + // check param lengths here. + // + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + NbfReferenceDeviceContext ("Query InfoNetMan", Provider, DCREF_QUERY_INFO); + for (v=Provider->NetmanVariables; v != NULL; v=v->Fwdlink) { + if (TdiRequest->Identification == v->VariableSerialNumber) { + + // + // Return the variable information here. + // + + NameOffset = sizeof (TDI_NETMAN_INFO); + ValueOffset = NameOffset + (sizeof (FLAT_STRING)-1) + + v->VariableName.Length; + + InfoBuffer->VariableName = NameOffset; + InfoBuffer->VariableValue = ValueOffset; + + // + // Copy the variable name to the user's buffer. + // + + p = (PFLAT_STRING)((PUCHAR)InfoBuffer + NameOffset); + p->MaximumLength = v->VariableName.Length; + p->Length = v->VariableName.Length; + for (i=0; iVariableName.Length; i++) { + p->Buffer [i] = v->VariableName.Buffer [i]; + } + + // + // Now copy the variable's contents to the user's buffer. + // + + n = (PTDI_NETMAN_VARIABLE)((PUCHAR)InfoBuffer + ValueOffset); + n->VariableType = v->VariableType; + + switch (v->VariableType) { + + case NETMAN_VARTYPE_ULONG: + n->Value.LongValue = v->Value.LongValue; + break; + + case NETMAN_VARTYPE_HARDWARE_ADDRESS: + n->Value.HardwareAddressValue = + v->Value.HardwareAddressValue; + break; + + case NETMAN_VARTYPE_STRING: + p = &n->Value.StringValue; + p->MaximumLength = v->Value.StringValue.Length; + p->Length = v->Value.StringValue.Length; + for (i=0; iValue.StringValue.Length; i++) { + p->Buffer [i] = v->Value.StringValue.Buffer [i]; + } + + } /* switch */ + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + NbfDereferenceDeviceContext ("Query InfoNetMan success", Provider, DCREF_QUERY_INFO); + return STATUS_SUCCESS; + } /* if */ + } /* for */ + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Query InfoNetMan no exist", Provider, DCREF_QUERY_INFO); + + return STATUS_INVALID_INFO_CLASS; // variable does not exist. +} /* NbfQueryInfoNetman */ + + +NTSTATUS +NbfSetInfoEndpoint( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified endpoint. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_ENDPOINT_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_ENDPOINT_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_ENDPOINT_INFO)&TdiRequest->InfoBuffer; + + if ((InfoBuffer->MinimumLookaheadData <= NBF_MAX_LOOKAHEAD_DATA) || + (InfoBuffer->MaximumLookaheadData <= NBF_MAX_LOOKAHEAD_DATA) || + (InfoBuffer->MinimumLookaheadData > InfoBuffer->MaximumLookaheadData)) { + return STATUS_INVALID_PARAMETER; + } + + ACQUIRE_SPIN_LOCK (&Endpoint->SpinLock, &oldirql); + + // + // Set minimum lookahead data size. This is the number of bytes of + // contiguous data that will be supplied to TDI_IND_RECEIVE and + // TDI_IND_RECEIVE_DATAGRAM event handlers at indication time. + // + + Endpoint->Information.MinimumLookaheadData = InfoBuffer->MinimumLookaheadData; + + // + // Set maximum lookahead data size. This is the number of bytes of + // contiguous data that will be supplied to TDI_IND_RECEIVE and + // TDI_IND_RECEIVE_DATAGRAM event handlers at indication time. + // + + Endpoint->Information.MaximumLookaheadData = InfoBuffer->MaximumLookaheadData; + + // + // Reset all the statistics to his new values. + // + + Endpoint->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Endpoint->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Endpoint->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Endpoint->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Endpoint->Information.PriorityLevel = InfoBuffer->PriorityLevel; + Endpoint->Information.SecurityLevel = InfoBuffer->SecurityLevel; + Endpoint->Information.SecurityCompartment = InfoBuffer->SecurityCompartment; + + // + // The State and Event fields are read-only, so we DON'T set them here. + // + + RELEASE_SPIN_LOCK (&Endpoint->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoEndpoint */ + + +NTSTATUS +NbfSetInfoAddress( + IN PTP_ADDRESS Address, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified address. Currently, + all the user-visible fields in the transport address object are read-only. + +Arguments: + + Address - Pointer to transport address object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + Address, TdiRequest, TdiRequestLength; // prevent compiler warnings + + return STATUS_SUCCESS; +} /* NbfSetInfoAddress */ + + +NTSTATUS +NbfSetInfoConnection( + IN PTP_CONNECTION Connection, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified connection. + +Arguments: + + Connection - Pointer to transport connection object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_CONNECTION_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_CONNECTION_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_CONNECTION_INFO)&TdiRequest->InfoBuffer; + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // Reset all the statistics to his new values. + // + + Connection->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Connection->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Connection->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Connection->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + + // + // The State and Event fields are read-only, so we DON'T set them here. + // + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoConnection */ + + +NTSTATUS +NbfSetInfoProvider( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified transport provider. + +Arguments: + + Provider - Pointer to device context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_PROVIDER_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_PROVIDER_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_PROVIDER_INFO)&TdiRequest->InfoBuffer; + + // + // By changing the service flags the caller can request additional + // or fewer services on the fly. Make sure that he is requesting + // services we can provide, or else fail the request. + // + + if (InfoBuffer->ServiceFlags & ~NBF_SERVICE_FLAGS) { + return STATUS_NOT_SUPPORTED; + } + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + + // + // Reset all the statistics to his new values. + // + + Provider->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Provider->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Provider->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Provider->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Provider->Information.DiscardedFrames = InfoBuffer->DiscardedFrames; + Provider->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Provider->Information.OversizeTsdusReceived = InfoBuffer->OversizeTsdusReceived; + Provider->Information.UndersizeTsdusReceived = InfoBuffer->UndersizeTsdusReceived; + Provider->Information.MulticastTsdusReceived = InfoBuffer->MulticastTsdusReceived; + Provider->Information.BroadcastTsdusReceived = InfoBuffer->BroadcastTsdusReceived; + Provider->Information.MulticastTsdusTransmitted = InfoBuffer->MulticastTsdusTransmitted; + Provider->Information.BroadcastTsdusTransmitted = InfoBuffer->BroadcastTsdusTransmitted; + Provider->Information.SendTimeouts = InfoBuffer->SendTimeouts; + Provider->Information.ReceiveTimeouts = InfoBuffer->ReceiveTimeouts; + Provider->Information.ConnectionIndicationsReceived = InfoBuffer->ConnectionIndicationsReceived; + Provider->Information.ConnectionIndicationsAccepted = InfoBuffer->ConnectionIndicationsAccepted; + Provider->Information.ConnectionsInitiated = InfoBuffer->ConnectionsInitiated; + Provider->Information.ConnectionsAccepted = InfoBuffer->ConnectionsAccepted; + + // + // The following fields are read-only, so we DON'T set them here: + // Version, MaxTsduSize, MaxConnectionUserData, MinimumLookaheadData, + // MaximumLookaheadData. + // + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoProvider */ + + +NTSTATUS +NbfSetInfoNetman( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQ_SET_INFORMATION TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified transport provider's + network-managable variable. + +Arguments: + + Provider - Pointer to device context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTDI_NETMAN_INFO InfoBuffer; + + Provider; // prevent compiler warnings + + if (TdiRequestLength != + sizeof (TDI_NETMAN_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_NETMAN_INFO)&TdiRequest->InfoBuffer; + + // + // BUGBUG: set the network-managable variable here. + // + + return STATUS_SUCCESS; +} /* NbfSetInfoNetman */ + + +NTSTATUS +NbfTdiQueryInformation( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_QUERY_INFORMATION TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_INFO_BUFFER InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PTP_CONNECTION Connection; + + switch (TdiRequest->InformationClass) { + + // + // ENDPOINT information: return information about the endpoint + // to which this request was submitted. + // + + case TDI_INFO_CLASS_ENDPOINT: + Status = NbfQueryInfoEndpoint ( + Endpoint, + TdiRequest, + TdiRequestLength, + (PTDI_ENDPOINT_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // CONNECTION information: return information about a connection + // that is associated with the endpoint on which this request was + // submitted. + // + + case TDI_INFO_CLASS_CONNECTION: + // This causes a connection reference which is removed below. + Connection = NbfLookupConnectionById ( + Endpoint, + TdiRequest->Identification); + if (Connection == NULL) { + Status = STATUS_INVALID_HANDLE; + break; + } + + Status = NbfQueryInfoConnection ( + Connection, + TdiRequest, + TdiRequestLength, + (PTDI_CONNECTION_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + + NbfDereferenceConnection("Query Connection Info", Connection, CREF_BY_ID); + break; + + // + // ADDRESS information: return information about the address object + // that is associated with the endpoint on which this request was + // submitted. + // + + case TDI_INFO_CLASS_ADDRESS: + Status = NbfQueryInfoAddress ( + Endpoint->BoundAddress, + TdiRequest, + TdiRequestLength, + (PTDI_ADDRESS_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // PROVIDER information: return information about the transport + // provider itself. + // + + case TDI_INFO_CLASS_PROVIDER: + Status = NbfQueryInfoProvider ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength, + (PTDI_PROVIDER_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // NETMAN information: return information about the network-managable + // variables managed by the provider itself. + // + + case TDI_INFO_CLASS_NETMAN: + Status = NbfQueryInfoNetman ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength, + (PTDI_NETMAN_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + default: + Status = STATUS_INVALID_INFO_CLASS; + + } /* switch */ + + return Status; +} /* TdiQueryInformation */ + + +NTSTATUS +TdiSetInformation( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_SET_INFORMATION TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PTP_CONNECTION Connection; + + switch (TdiRequest->InformationClass) { + + // + // ENDPOINT information: set information on the endpoint + // to which this request was submitted. + // + + case TDI_INFO_CLASS_ENDPOINT: + Status = NbfSetInfoEndpoint ( + Endpoint, + TdiRequest, + TdiRequestLength); + break; + + // + // CONNECTION information: set information for a connection + // that is associated with the endpoint on which this request + // was submitted. + // + + case TDI_INFO_CLASS_CONNECTION: + // This causes a connection reference which is removed below. + Connection = NbfLookupConnectionById ( + Endpoint, + TdiRequest->Identification); + if (Connection == NULL) { + Status = STATUS_INVALID_HANDLE; + break; + } + + Status = NbfSetInfoConnection ( + Connection, + TdiRequest, + TdiRequestLength); + + NbfDereferenceConnection("Set Connection Info", Connection, CREF_BY_ID); + break; + + // + // ADDRESS information: set information for the address object + // that is associated with the endpoint on which this request + // was submitted. + // + + case TDI_INFO_CLASS_ADDRESS: + Status = NbfSetInfoAddress ( + Endpoint->BoundAddress, + TdiRequest, + TdiRequestLength); + break; + + // + // PROVIDER information: set information for the transport + // provider itself. + // + + case TDI_INFO_CLASS_PROVIDER: + Status = NbfSetInfoProvider ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength); + break; + + // + // NETMAN information: set information for the network-managable + // variables managed by the provider itself. + // + + case TDI_INFO_CLASS_NETMAN: + Status = NbfSetInfoNetman ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength); + break; + + default: + Status = STATUS_INVALID_INFO_CLASS; + + } /* switch */ + + return Status; +} /* TdiSetInformation */ + +#endif diff --git a/private/ntos/tdi/nbf/link.c b/private/ntos/tdi/nbf/link.c new file mode 100644 index 000000000..e99bbb8f4 --- /dev/null +++ b/private/ntos/tdi/nbf/link.c @@ -0,0 +1,2315 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + link.c + +Abstract: + + This module contains code which implements the TP_LINK object. + Routines are provided to create, destroy, reference, and dereference, + transport link objects. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +extern ULONG StartTimerLinkDeferredAdd; +extern ULONG StartTimerLinkDeferredDelete; + +#if DBG +// The following is here for debugging purposes to make it easy to change +// the maximum packet size. + +ULONG MaxUserPacketData = 18000; +#endif + +#if 0 + +VOID +DisconnectCompletionHandler( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine is called as an I/O completion handler at the time a + TdiDisconnect request is completed. Here we dereference the link + object, and optionally reference it again and start up the link if + some transport connection started up on the link during the time we + were trying to shut it down. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("DisconnectCompletionHandler: Entered for link %lx.\n", + TransportLink); + } + + // + // The following call will dereference this link for the last time, + // unless another transport connection has been assigned to the link + // during the time the data link layer was bringing the link down and + // when we got here. If this condition exists, then now is the time + // to bring the link back up, else destroy it. + // + + // BUGBUG: don't forget to check for bringing it back up again. + + // BUGBUG: Is this right??? - ADB 6/26 + + NbfDereferenceLink ("Disconnecting", TransportLink, LREF_CONNECTION); // this makes it go away. +#if DBG + NbfPrint0("Disconnecting Completion Handler\n"); +#endif + +} /* DisconnectCompletionHandler */ +#endif + + +VOID +NbfCompleteLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called by the UA-r/x handler, NbfWaitLink, and + NbfActivateLink to startup the NBF connections associated with + a link because they were waiting for the link to become established. + + When we get here, the link has been established, so we need to + start the next set of connection-establishment protocols: + + SESSION_INIT -----------------> + <----------------- SESSION_CONFIRM + + (TdiConnect completes) (TdiListen completes) + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + BOOLEAN TimerWasCleared; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCompleteLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + // + // Officially declare that this link is ready for I-frame business. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // We can now send and receive I-frames on this link. We are in ABME. + // + + // + // This probably isn't necessary, but to be safe for now.. (adb 6/28) + // + if (Link->State == LINK_STATE_ADM) { + // Moving out of ADM, add special reference + NbfReferenceLinkSpecial("To READY in NbfCompleteLink", Link, LREF_NOT_ADM); + } + + Link->State = LINK_STATE_READY; + Link->SendState = SEND_STATE_READY; + Link->ReceiveState = RECEIVE_STATE_READY; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // Complete all of the listens first, so they will be expecting + // incoming SESSION_INITIALIZEs. Then do the connects. + // + + // This creates a connection reference which is removed below. + while ((Connection=NbfLookupPendingListenOnLink (Link)) != NULL) { + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // This loop looks unnecessary, let's make sure... - adb 9/11/91 + // + ASSERT(Connection->Flags & CONNECTION_FLAGS_WAIT_SI); + + Connection->Flags |= CONNECTION_FLAGS_WAIT_SI; // wait session initialize. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("Pending listen", Connection, CREF_P_LINK); + } /* while */ + + // + // And do the connects. If there are connections in progress, they'll + // also have timers associated with them. Cancel those timers. + // + + while ((Connection=NbfLookupPendingConnectOnLink (Link)) != NULL) { + TimerWasCleared = KeCancelTimer (&Connection->Timer); + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfCompleteLink: Timer for connection %lx %s canceled.\n", + Connection, TimerWasCleared ? "was" : "was NOT" ); + } + if (TimerWasCleared) { + NbfDereferenceConnection("Cancel timer", Connection, CREF_TIMER); + } + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_WAIT_SC; // wait session confirm. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // No timeout for this frame is required since the link is responsible + // for reliable delivery. If we can't send this frame, however, the + // data link connection will happily keep quiet without timeouts. + // + + NbfSendSessionInitialize (Connection); + NbfDereferenceConnection ("NbfCompleteLink", Connection, CREF_P_CONNECT); + } /* while */ + +} /* NbfCompleteLink */ + + +VOID +NbfAllocateLink( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_LINK *TransportLink + ) + +/*++ + +Routine Description: + + This routine allocates storage for a data link connection. It + performs minimal initialization of the object. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + TransportLink - Pointer to a place where this routine will return a + pointer to an allocated transport link structure. Returns + NULL if no storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_LINK Link; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_LINK)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate link: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 105, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + *TransportLink = NULL; + return; + } + Link = (PTP_LINK)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_LINK), + 'lFBN'); + if (Link == NULL) { + PANIC("NBF: Could not allocate link: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 205, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + *TransportLink = NULL; + return; + } + RtlZeroMemory (Link, sizeof(TP_LINK)); + + ++DeviceContext->LinkAllocated; + DeviceContext->MemoryUsage += sizeof(TP_LINK); + + Link->Type = NBF_LINK_SIGNATURE; + Link->Size = sizeof (TP_LINK); + + KeInitializeSpinLock (&Link->SpinLock); + Link->Provider = DeviceContext; + Link->ProviderInterlock = &DeviceContext->Interlock; + + InitializeListHead (&Link->Linkage); + InitializeListHead (&Link->ConnectionDatabase); + InitializeListHead (&Link->WackQ); + InitializeListHead (&Link->NdisSendQueue); + InitializeListHead (&Link->ShortList); + Link->OnShortList = FALSE; + InitializeListHead (&Link->LongList); + Link->OnLongList = FALSE; + InitializeListHead (&Link->PurgeList); + + Link->T1 = 0; // 0 indicates they are not in the list + Link->T2 = 0; + Link->Ti = 0; + + NbfAddSendPacket (DeviceContext); + NbfAddReceivePacket (DeviceContext); + + *TransportLink = Link; + +} /* NbfAllocateLink */ + + +VOID +NbfDeallocateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine frees storage for a data link connection. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + TransportLink - Pointer to the transport link structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportLink); + --DeviceContext->LinkAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_LINK); + + NbfRemoveSendPacket (DeviceContext); + NbfRemoveReceivePacket (DeviceContext); + +} /* NbfDeallocateLink */ + + +NTSTATUS +NbfCreateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS HardwareAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN USHORT LoopbackLinkIndex, + OUT PTP_LINK *TransportLink + ) + +/*++ + +Routine Description: + + This routine creates a data link connection between the local + data link station and the specified remote data link address. + As an option (Passive=TRUE), the caller may specify that instead + of a Connect activity, a Listen is to be performed instead. + + Normally, if a link to the remote address is not already active, + then a link object is allocated, the reference count in the link + is set to 1, and the reference count of the device context is + incremented. + + If a link is already active to the remote address, then the existing + link object is referenced with NbfReferenceLink() so that it can be + shared between the transport connections. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + HardwareAddress - Pointer to a HARDWARE_ADDRESS type containing the + hardware address of the REMOTE link station to connect to/listen for. + + LoopbackLinkIndex - In the case that this turns out to be created + as one of the LoopbackLinks, this will indicate which one to + use. + + TransportLink - Pointer to a place where this routine will return a + pointer to an allocated transport link structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_LINK Link; + PLIST_ENTRY p; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + USHORT i; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCreateLink: Entered, DeviceContext: %lx\n", DeviceContext); + } + + // + // Walk the list of addresses to see if we already have a link to this + // remote address. + // + + // This adds a reference if the link is found. + + Link = NbfFindLink (DeviceContext, HardwareAddress->Address); + + + if (Link == (PTP_LINK)NULL) { + + // + // If necessary, check whether we are looking for one of + // the loopback links (NbfFindLink won't find those). + // + + if (RtlEqualMemory( + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + Link = DeviceContext->LoopbackLinks[LoopbackLinkIndex]; + + if (Link != (PTP_LINK)NULL) { + + // + // Add a reference to simulate the one from NbfFindLink + // + // BUGBUG: This needs to be atomically done with + // the assignment above. + // + + NbfReferenceLink ("Found loopback link", Link, LREF_TREE); + + } else { + + // + // May have the first loopback link; need to make sure the + // buffer for indications is allocated. + // + + if (DeviceContext->LookaheadContiguous == NULL) { + + DeviceContext->LookaheadContiguous = + ExAllocatePoolWithTag ( + NonPagedPool, + NBF_MAX_LOOPBACK_LOOKAHEAD, + ' FBN'); + if (DeviceContext->LookaheadContiguous == NULL) { + PANIC ("NbfCreateLink: Could not allocate loopback buffer!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } + + } + + } + + } + + + if (Link != (PTP_LINK)NULL) { + + // + // Found the link structure here, so use the existing link. + // + +#if DBG + // + // These two operations have no net effect, so if not in debug + // mode we can remove them. + // + + // This reference is removed by NbfDisconnectFromLink + // (this assumes that NbfConnectToLink is always called + // if this function returns success). + + NbfReferenceLink ("New Ref, Found existing link", Link, LREF_CONNECTION); // extra reference. + + // Now we can remove the NbfFindLinkInTree reference. + + NbfDereferenceLink ("Found link in tree", Link, LREF_TREE); +#endif + + *TransportLink = Link; // return pointer to the link. + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfCreateLink: returning ptr to existing link object.\n"); + } + return STATUS_SUCCESS; // all done. + + } /* if LINK != NULL */ + + + // + // We don't have an existing link, so we have to create one. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfCreateLink: using new link object.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + p = RemoveHeadList (&DeviceContext->LinkPool); + if (p == &DeviceContext->LinkPool) { + + if ((DeviceContext->LinkMaxAllocated == 0) || + (DeviceContext->LinkAllocated < DeviceContext->LinkMaxAllocated)) { + + NbfAllocateLink (DeviceContext, &Link); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated link at %lx\n", Link); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 405, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + Link = NULL; + + } + + if (Link == NULL) { + ++DeviceContext->LinkExhausted; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + PANIC ("NbfCreateConnection: Could not allocate link object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Link = CONTAINING_RECORD (p, TP_LINK, Linkage); + + } + + ++DeviceContext->LinkInUse; + ASSERT(DeviceContext->LinkInUse > 0); + + if (DeviceContext->LinkInUse > DeviceContext->LinkMaxInUse) { + ++DeviceContext->LinkMaxInUse; + } + + DeviceContext->LinkTotal += DeviceContext->LinkInUse; + ++DeviceContext->LinkSamples; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCreateLink: Link at %lx.\n", Link); + } + + // + // Initialize all of the static data for this link. + // + + Link->SpecialRefCount = 1; + Link->ReferenceCount = 0; +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_LREFS; Counter++) { + Link->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfDisconnectFromLink + // (this assumes that NbfConnectToLink is always called + // if this function returns success). + // + + Link->RefTypes[LREF_CONNECTION] = 1; + Link->RefTypes[LREF_SPECIAL_TEMP] = 1; + } + Link->Destroyed = FALSE; + Link->TotalReferences = 0; + Link->TotalDereferences = 0; + Link->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalLinkList, &Link->GlobalLinkage, &NbfGlobalInterlock); + StoreLinkHistory (Link, TRUE); +#endif + Link->Flags = 0; // in the beginning, the link is closed. + Link->DeferredFlags = 0; + Link->State = LINK_STATE_ADM; // async disconnected mode. + + Link->NdisSendsInProgress = 0; + Link->ResendingPackets = FALSE; + + // + // Initialize the counters + // + + Link->FrmrsReceived = 0; + Link->FrmrsTransmitted = 0; + Link->ErrorIFramesReceived = 0; + Link->ErrorIFramesTransmitted = 0; + Link->AbortedTransmissions = 0; + Link->BuffersNotAvailable = 0; + Link->SuccessfulTransmits = 0; + Link->SuccessfulReceives = 0; + Link->T1Expirations = 0; + Link->TiExpirations = 0; + +#if DBG + Link->CreatePacketFailures = 0; +#endif + + + // + // At first, the delay and throughput are unknown. + // + + Link->Delay = 0xffffffff; + Link->Throughput.HighPart = 0xffffffff; + Link->Throughput.LowPart = 0xffffffff; + Link->ThroughputAccurate = FALSE; + Link->CurrentT1Backoff = FALSE; + + Link->OnDeferredRrQueue = FALSE; + InitializeListHead (&Link->DeferredRrLinkage); + + + // + // Determine the maximum sized data frame that can be sent + // on this link, based on the source routing information and + // the size of the MAC header ("data frame" means the frame + // without the MAC header). We don't assume the worst case + // about source routing since we create a link in response + // to a received frame, so if there is no source routing it + // is because we are not going over a bridge. The exception + // is if we are creating a link to a group name, in which + // case we come back later and hack the MaxFrameSize in. + // + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->CurSendPacketSize, + FALSE, + (PUINT)&(Link->MaxFrameSize)); + + +#if DBG + if (Link->MaxFrameSize > MaxUserPacketData) { + Link->MaxFrameSize = MaxUserPacketData; + } +#endif + + // Link->Provider = DeviceContext; + + // + // Build the default MAC header. I-frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + Link->Header, + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + 0, // PacketLength, filled in later + ResponseSR, + SourceRoutingLength, + (PUINT)&(Link->HeaderLength)); + + // + // We optimize for fourteen-byte headers by putting + // the correct Dsap/Ssap at the end, so we can fill + // in new packets as one 16-byte move. + // + + if (Link->HeaderLength <= 14) { + Link->Header[Link->HeaderLength] = DSAP_NETBIOS_OVER_LLC; + Link->Header[Link->HeaderLength+1] = DSAP_NETBIOS_OVER_LLC; + } + + Link->RespondToPoll = FALSE; + Link->NumberOfConnectors = 0; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfResetLink (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + Link->ActiveConnectionCount = 0; + if (!IsListEmpty(&Link->ConnectionDatabase)) { + + // + // Not good; we've got something left over... + // +#if DBG + NbfPrint1 ("NbfCreateLink: Link 0x%lx has connections at startup, disconnecting...\n", Link); + DbgBreakPoint(); +#endif + // + // BUGBUG: This won't work, the link ref count will be bad. + // + NbfStopLink (Link); + } + + for (i=0; i<(USHORT)DeviceContext->MacInfo.AddressLength; i++) { + Link->HardwareAddress.Address[i] = HardwareAddress->Address[i]; + } + MacReturnMagicAddress (&DeviceContext->MacInfo, HardwareAddress, &Link->MagicAddress); + + // + // Determine if this is a loopback link. + // + + if (RtlEqualMemory( + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + // + // Yes, just fill it in, no need to do deferred processing + // since this link does not go in the tree. + // + + if (LoopbackLinkIndex == LISTENER_LINK) { + Link->LoopbackDestinationIndex = LOOPBACK_TO_CONNECTOR; + } else { + Link->LoopbackDestinationIndex = LOOPBACK_TO_LISTENER; + } + + Link->Loopback = TRUE; + DeviceContext->LoopbackLinks[LoopbackLinkIndex] = Link; + + } else { + + Link->Loopback = FALSE; + + // + // Now put the link in the deferred operations queue and go away. We'll + // insert this link in the tree at some future time (soon). + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfCreateLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + Link, Link->DeferredList.Flink, Link->DeferredList.Blink, + DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink, + Link->Flags); + } + + // + // We should not have any deferred flags yet! + // + + ASSERT ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) == 0) { + Link->DeferredFlags |= LINK_FLAGS_DEFERRED_ADD; + InsertTailList (&DeviceContext->LinkDeferred, &Link->DeferredList); + + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredAdd++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + } + else { + Link->DeferredFlags = LINK_FLAGS_DEFERRED_ADD; + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredAdd++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfCreateLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + Link, Link->DeferredList.Flink, Link->DeferredList.Blink, + DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink, + Link->DeferredFlags); + } + + } + + NbfReferenceDeviceContext ("Create Link", DeviceContext, DCREF_LINK); // count refs to the device context. + *TransportLink = Link; // return a pointer to the link object. + return STATUS_SUCCESS; +} /* NbfCreateLink */ + + +NTSTATUS +NbfDestroyLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine destroys a transport link and removes all references + made to it by other objects in the transport. The link is expected + to still be on the splay tree of links. This routine merely marks the + link as needing to be deleted and pushes it onto the deferred operations + queue. The deferred operations processor actually removes the link from + tree and returns the link to pool. + +Arguments: + + TransportLink - Pointer to a transport link structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTP_PACKET packet; + PLIST_ENTRY pkt; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfDestroyLink: Entered for link %lx.\n", TransportLink); + } + +#if DBG + if (TransportLink->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed link 0x%lx\n", TransportLink); + DbgBreakPoint (); + } + TransportLink->Destroyed = TRUE; +#if 1 + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&TransportLink->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#else + ExInterlockedRemoveHeadList (TransportLink->GlobalLinkage.Blink, &NbfGlobalInterlock); +#endif +#endif + + DeviceContext = TransportLink->Provider; + + // + // In case there's a holdover from the DISC link shutdown protocol + // + + // + // We had better be in ADM, otherwise the reference count should + // be non-zero and what are we doing in NbfDestroyLink? + // + + ASSERT(TransportLink->State == LINK_STATE_ADM); + // TransportLink->State = LINK_STATE_ADM; + + StopT1 (TransportLink); + StopT2 (TransportLink); + StopTi (TransportLink); + + + // + // Make sure we are not in the deferred timer queue. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql); + + if (TransportLink->OnShortList) { + TransportLink->OnShortList = FALSE; + RemoveEntryList (&TransportLink->ShortList); + } + + if (TransportLink->OnLongList) { + TransportLink->OnLongList = FALSE; + RemoveEntryList (&TransportLink->LongList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql); + + ASSERT (!TransportLink->OnDeferredRrQueue); + + // + // Now free this link object's resources. + // BUGBUG: later, we'll spin through the WackQ and verify that sequencing + // is correct and we've gotten an implicit ack for these packets. This + // maybe should be handled in ResendLlcPackets for non-final, non-command + // packets. + // + + while (!IsListEmpty (&TransportLink->WackQ)) { + pkt = RemoveHeadList (&TransportLink->WackQ); + packet = CONTAINING_RECORD (pkt, TP_PACKET, Linkage); +#if DBG + // IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfDereferenceLink: Destroying packets on Link WackQ! %lx\n", packet); + // } +#endif + NbfDereferencePacket (packet); + + } + + // + // The NDIS send queue should be empty!! + // + + ASSERT (IsListEmpty (&TransportLink->NdisSendQueue)); + +#if DBG + if (!IsListEmpty (&TransportLink->ConnectionDatabase)) { + NbfPrint1 ("NbfDestroyLink: link 0x%lx still has connections\n", TransportLink); + DbgBreakPoint (); + } +#endif + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->LinkTotal += DeviceContext->LinkInUse; + ++DeviceContext->LinkSamples; + ASSERT(DeviceContext->LinkInUse > 0); + --DeviceContext->LinkInUse; + + ASSERT(DeviceContext->LinkAllocated > DeviceContext->LinkInUse); + + if ((DeviceContext->LinkAllocated - DeviceContext->LinkInUse) > + DeviceContext->LinkInitAllocated) { + NbfDeallocateLink (DeviceContext, TransportLink); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated link at %lx\n", TransportLink); + } + } else { + InsertTailList (&DeviceContext->LinkPool, &TransportLink->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Destroy Link", DeviceContext, DCREF_LINK); // just housekeeping. + + return STATUS_SUCCESS; + +} /* NbfDestroyLink */ + + +VOID +NbfDisconnectLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine calls the data link provider to disconnect a data link + connection associated with a TP_LINK object. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfDisconnectLink: Entered for link %lx.\n", Link); + } + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); + + if ((Link->Flags & LINK_FLAGS_LOCAL_DISC) != 0) { + + Link->Flags &= ~LINK_FLAGS_LOCAL_DISC; + + if (Link->State == LINK_STATE_ADM) { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + } else { + + PLIST_ENTRY p; + PTP_PACKET packet; + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is in W_DISC_RSP. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); + } + + Link->SendRetries = (UCHAR)Link->LlcRetries; + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + NbfSendDisc (Link, TRUE); // send DISC-c/p. + + } + + } else { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + } + +} /* NbfDisconnectLink */ + +#if DBG + +VOID +NbfRefLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport link. If we are + currently in the state waiting for disconnect response, we do not + reference; this avoids the link "bouncing" during disconnect (trying to + disconnect multiple times). + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfReferenceLink: Entered for link %lx, current level=%ld.\n", + TransportLink, TransportLink->ReferenceCount); + } + +#if DBG + StoreLinkHistory( TransportLink, TRUE ); +#endif + + result = InterlockedIncrement (&TransportLink->ReferenceCount); + + if (result == 0) { + + // + // The first increment causes us to increment the + // "ref count is not zero" special ref. + // + + NbfReferenceLinkSpecial ("first ref", TransportLink, LREF_SPECIAL_TEMP); + + } + + ASSERT (result >= 0); + +} /* NbfRefLink */ +#endif + + +VOID +NbfDerefLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine dereferences a transport link by decrementing the + reference count contained in the structure. + + There are two special reference counts, 1 and 0. If, after dereferencing, + the reference count is one (1), then we initiate a disconnect protocol + sequence (DISC/UA) to terminate the connection. When this request + completes, the completion routine will dereference the link object again. + While this protocol is in progress, we will not allow the link to be + incremented again. + + If the reference count becomes 0 after dereferencing, then we are in + the disconnection request completion handler, and we should actually + destroy the link object. We place the link on the deferred operations + queue and let the link get deleted later at a safe time. + + Warning: Watch out for cases where a link is going down, and it is + suddenly needed again. Keep a bitflag for that in the link object. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfDereferenceLink: Entered for link %lx, current level=%ld.\n", + TransportLink, TransportLink->ReferenceCount); + } + +#if DBG + StoreLinkHistory( TransportLink, FALSE ); +#endif + + result = InterlockedDecrement(&TransportLink->ReferenceCount); + + // + // If all the normal references to this link are gone, then + // we can remove the special reference that stood for + // "the regular ref count is non-zero". + // + + + if (result < 0) { + + // + // If the refcount is -1 we want to call DisconnectLink, + // we do this before removing the special ref so that + // the link does not go away during the call. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfDereferenceLink: refcnt=1, disconnecting Link object.\n"); + } + + NbfDisconnectLink (TransportLink); + + // + // Now it is OK to let the link go away. + // + + NbfDereferenceLinkSpecial ("Regular ref 0", TransportLink, LREF_SPECIAL_TEMP); + + } + +} /* NbfDerefLink */ + + +VOID +NbfRefLinkSpecial( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine increments the special reference count on a transport link. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint3 ("NbfRefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n", + TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount); + } + +#if DBG + StoreLinkHistory( TransportLink, TRUE ); +#endif + + result = ExInterlockedAddUlong ( + (PULONG)&TransportLink->SpecialRefCount, + 1, + TransportLink->ProviderInterlock); + +} /* NbfRefLinkSpecial */ + + +VOID +NbfDerefLinkSpecial( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine dereferences a transport link by decrementing the + special reference count contained in the structure. + + The special reference may be decremented at any time, however + the effect of those dereferences only happen when the normal + reference count is 0, to prevent the link from going away + while the operations due to the ->0 transition of the + normal reference count are done. + + If the special reference count becomes 0 after dereferencing, then we + are in the disconnection request completion handler, and we should actually + destroy the link object. We place the link on the deferred operations + queue and let the link get deleted later at a safe time. + + Warning: Watch out for cases where a link is going down, and it is + suddenly needed again. Keep a bitflag for that in the link object. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + ULONG OldRefCount; + PDEVICE_CONTEXT DeviceContext = TransportLink->Provider; + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint3 ("NbfDerefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n", + TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount); + } + +#if DBG + StoreLinkHistory( TransportLink, FALSE ); +#endif + + // + // Links stay in the device context tree with a ref count + // of 0. Routines that scan this queue check the DEFERRED_DELETE + // flag, so we need to synchronize the decrementing of the + // ref count with setting that flag. DeviceContext->LinkSpinLock + // is used to synchronize this. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->LinkSpinLock, &oldirql1); + + OldRefCount = ExInterlockedAddUlong ( + (PULONG)&TransportLink->SpecialRefCount, + (ULONG)-1, + TransportLink->ProviderInterlock); + + ASSERT (OldRefCount > 0); + + if ((OldRefCount == 1) && + (TransportLink->ReferenceCount == -1)) { + + if (TransportLink->Loopback) { + + // + // It is a loopback link, hence not in the link + // tree so we don't need to queue a deferred removal. + // + + if (TransportLink == DeviceContext->LoopbackLinks[0]) { + DeviceContext->LoopbackLinks[0] = NULL; + } else if (TransportLink == DeviceContext->LoopbackLinks[1]) { + DeviceContext->LoopbackLinks[1] = NULL; + } else { +#if DBG + NbfPrint0("Destroying unknown loopback link!!\n"); +#endif + ASSERT(FALSE); + } + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + NbfDestroyLink (TransportLink); + + } else { + + // + // Not only are all transport connections gone, but the data link + // provider does not have a reference to this object, so we can + // safely delete it from the system. Make sure we haven't already + // been here before we try to insert this link. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfDerefLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + TransportLink, TransportLink->DeferredList.Flink, + TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, TransportLink->Flags); + } + + if ((TransportLink->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql); + InsertTailList (&DeviceContext->LinkDeferred, &TransportLink->DeferredList); + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredDelete++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql); + + } else { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfDereferenceLink: refcnt=0, link placed on deferred operations queue.\n"); + } + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfDerefLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + TransportLink, TransportLink->DeferredList.Flink, + TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, TransportLink->DeferredFlags); + } + + } + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + } + +} /* NbfDerefLinkSpecial */ + + +NTSTATUS +NbfAssignGroupLsn( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine is called to assign a global LSN to the connection + in question. If successful, it fills in the connection's LSN + appropriately. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + STATUS_SUCCESS if we got an LSN for the connection; + STATUS_INSUFFICIENT_RESOURCES if we didn't. + +--*/ + +{ + KIRQL oldirql; + UCHAR Lsn; + PDEVICE_CONTEXT DeviceContext; + BOOLEAN FoundLsn = FALSE; + + DeviceContext = TransportConnection->Provider; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + // + // Scan through the device context tables to find an LSN that + // is not in use, starting with NextLsnStart+128. + // + + Lsn = (UCHAR)DeviceContext->NextLsnStart; + + do { + + if (DeviceContext->LsnTable[Lsn] == 0) { + DeviceContext->LsnTable[Lsn] = LSN_TABLE_MAX; + FoundLsn = TRUE; + break; + } + + Lsn = (Lsn % NETBIOS_SESSION_LIMIT) + 1; + + } while (Lsn != DeviceContext->NextLsnStart); + + DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1; + + if (!FoundLsn) { + + // + // Could not find an empty LSN; have to fail. + // + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + TransportConnection->Lsn = Lsn; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + +} + + +NTSTATUS +NbfConnectToLink( + IN PTP_LINK Link, + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine is called to establish a linkage between a transport + connection and a transport link. We find a session number in one + of two ways. If the last connection on the link's list has a number less + than the maximum session number, we simply increment it's number and + assign it to this session. If that doesn't work, we scan through the + sessions associated with this link until we find a hole in the LSNs; + we then use the first number in that hole. If that fails, we've used + the number of sessions we can create on this link and we fail. + + It is assumed that the caller holds at least temporary references + on both the connection and link objects, or they could go away during + the call sequence or during this routine's execution. + +Arguments: + + Link - Pointer to a transport link object. + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + STATUS_SUCCESS if we got an LSN for the connection; + STATUS_INSUFFICIENT_RESOURCES if we didn't. + +--*/ + +{ + KIRQL oldirql; + UCHAR lastSession=0; + PTP_CONNECTION connection; + PLIST_ENTRY p; + PDEVICE_CONTEXT DeviceContext; + UCHAR Lsn; + BOOLEAN FoundLsn; + + // + // Assign an LSN for a new connection. If this connection makes for more + // connections than the maximum, blow off the creation. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfConnectToLink: Entered for connection %lx, link %lx.\n", + TransportConnection, Link); + } + + DeviceContext = Link->Provider; + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); +#if DBG + if (!(IsListEmpty(&TransportConnection->LinkList)) || + (TransportConnection->Link != NULL)) { + DbgPrint ("Connecting C %lx to L %lx, appears to be in use\n", TransportConnection, Link); + DbgBreakPoint(); + } +#endif + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) == 0) { + + // + // This connection is to a remote unique name, which means + // we need to assign the LSN here based on the link. We + // scan through our LSN table starting with NextLsnStart + // (which cycles from 1 to 64) to find an LSN which is not + // used by any connections on this link. + // + + ASSERT (TransportConnection->Lsn == 0); + + FoundLsn = FALSE; + Lsn = (UCHAR)DeviceContext->NextLsnStart; + + // + // First scan through the database until we reach + // Lsn (or hit the end of the database). + // + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (connection->Lsn >= Lsn) { + break; + } + } + + // + // p now points to the first element after Lsn's spot. + // We now scan forwards until we hit NETBIOS_SESSION_LIMIT, + // looking for an Lsn that is available. + // + + for ( ; Lsn <= NETBIOS_SESSION_LIMIT; ++Lsn) { + + // + // At some point (perhaps right away) we may + // pass the end of the database without finding + // an LSN. If we have not yet done this, see + // if we need to skip this lsn because it is + // in use by a connection on this link. + // + + if (p != &Link->ConnectionDatabase) { + if (connection->Lsn == Lsn) { + p = p->Flink; + if (p != &Link->ConnectionDatabase) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + } + continue; + } + } + + // + // This lsn is not in use on this link, see if + // there is room for it to be used. + // + + if (DeviceContext->LsnTable[Lsn] < LSN_TABLE_MAX) { + ++(DeviceContext->LsnTable[Lsn]); + TransportConnection->Lsn = Lsn; + InsertTailList (p, &TransportConnection->LinkList); + FoundLsn = TRUE; + break; + } + + } + + DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1; + + } else { + + // + // This connection is to a group name; we already assigned + // the LSN on a global basis. + // + + FoundLsn = TRUE; + + // + // Find the spot for this LSN in the database. + // + + p = Link->ConnectionDatabase.Flink; + while (p != &Link->ConnectionDatabase) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (TransportConnection->Lsn < connection->Lsn) { + InsertTailList (p, &TransportConnection->LinkList); + break; + } + p = p->Flink; + + } + + if (p == &Link->ConnectionDatabase) { + InsertTailList (&Link->ConnectionDatabase, &TransportConnection->LinkList); + } + + } + + if (!FoundLsn) { + + ULONG DumpData = NETBIOS_SESSION_LIMIT; + + ASSERT (Link->ActiveConnectionCount == NETBIOS_SESSION_LIMIT); + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + PANIC ("NbfConnectToLink: PANIC! too many active connections!\n"); + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TOO_MANY_LINKS, + 602, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 1, + &DumpData); + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + TransportConnection->Link = Link; + TransportConnection->LinkSpinLock = &Link->SpinLock; + TransportConnection->Flags |= CONNECTION_FLAGS_WAIT_LINK_UP; + + TransportConnection->LastPacketsSent = Link->PacketsSent; + TransportConnection->LastPacketsResent = Link->PacketsResent; + + Link->ActiveConnectionCount++; + + // + // Note that the connection is already inserted in the + // link's ConnectionDatabase. + // + + // This reference is removed in NbfDisconnectFromLink + NbfReferenceConnection("Adding link", TransportConnection, CREF_LINK); + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + return STATUS_SUCCESS; // we did it! + +} /* NbfConnectToLink */ + + +BOOLEAN +NbfDisconnectFromLink( + IN PTP_CONNECTION TransportConnection, + IN BOOLEAN VerifyReferenceCount + ) + +/*++ + +Routine Description: + + This routine is called to terminate a linkage between a transport + connection and its associated transport link. If it turns out that + this is the last connection to be removed from this link, then the + link's disconnection protocol is engaged. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + + VerifyReferenceCount - TRUE if we should check that the refcount + is still -1 before removing the connection from the link. + If it is not, it means someone just referenced us and we + exit. + +Return Value: + + FALSE if VerifyReferenceCount was TRUE but the refcount was + not -1; TRUE otherwise. + + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_LINK Link; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfDisconnectFromLink: Entered for connection %lx, link %lx.\n", + TransportConnection, TransportConnection->Link); + } + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + Link = TransportConnection->Link; + if (Link != NULL) { + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql1); + + if ((VerifyReferenceCount) && + (TransportConnection->ReferenceCount != -1)) { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1); + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + return FALSE; + + } + + TransportConnection->Link = NULL; + TransportConnection->LinkSpinLock = NULL; + RemoveEntryList (&TransportConnection->LinkList); +#if DBG + InitializeListHead (&TransportConnection->LinkList); +#endif + + // + // If this was the last connection being serviced by this link, + // then we can shut the link down. It still has a reference + // from the device context, which will go away in the DM/UA + // DLC frame handler. + // + + if (--Link->ActiveConnectionCount == 0) { + + // + // only want to send DISC if the remote was NOT the originator + // of the disconnect. + // + + if ((TransportConnection->Status == STATUS_LOCAL_DISCONNECT) || + (TransportConnection->Status == STATUS_CANCELLED)) { + + // + // This is a local disconnect of the last connection + // on the link, let's get the disconnect ball rolling. + // + + Link->Flags |= LINK_FLAGS_LOCAL_DISC; + + // + // When the link reference count drops down to 1, + // that will cause the DISC to get sent. + // + + } + + } + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1); + + // + // Clear these now that we are off the link's database. + // + + NbfClearConnectionLsn (TransportConnection); + TransportConnection->Rsn = 0; + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) != 0) { + + (VOID)InterlockedDecrement(&Link->NumberOfConnectors); + } + + // + // All done with this connection's reference to link. + // + + NbfDereferenceLink ("Disconnecting connection",Link, LREF_CONNECTION); + + } else { + + // + // A group LSN may have been assigned even though Link is NULL. + // + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) != 0) { + NbfClearConnectionLsn (TransportConnection); + } + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + return TRUE; + +} /* NbfDisconnectFromLink */ + + +PTP_CONNECTION +NbfLookupPendingListenOnLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine scans the LSN database on a transport link object to find + a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and + CONNECTION_FLAGS2_LISTENER flags set. It returns a pointer to the found + connection object (and simultaneously resets the LINK_UP flag) or NULL + if it could not be found. The reference count is also incremented + atomically on the connection. + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) && + (connection->Flags2 & CONNECTION_FLAGS2_LISTENER) && + ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) { + // This reference is removed by the calling function + NbfReferenceConnection ("Found Pending Listen", connection, CREF_P_LINK); + connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return connection; + } + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + return NULL; + +} /* NbfLookupPendingListenOnLink */ + + +PTP_CONNECTION +NbfLookupPendingConnectOnLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine scans the LSN database on a transport link object to find + a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and + CONNECTION_FLAGS2_CONNECTOR flags set. It returns a pointer to the found + connection object (and simultaneously resets the LINK_UP flag) or NULL + if it could not be found. The reference count is also incremented + atomically on the connection. + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) && + (connection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) && + ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) { + // This reference is removed by the calling function + NbfReferenceConnection ("Found pending Connect", connection, CREF_P_CONNECT); + connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return connection; + } + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + return NULL; + +} /* NbfLookupPendingConnectOnLink */ + + +VOID +NbfActivateLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine activates a link if it is not already active. The other + related routines, NbfCreateLink and NbfConnectToLink, simply set up data + structures which represent active links so that we can reuse links + wherever possible. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfActivateLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + switch (Link->State) { + case LINK_STATE_READY: + NbfCompleteLink (Link); + break; + + case LINK_STATE_ADM: + + // Moving out of ADM, add reference + + NbfReferenceLinkSpecial("Wait on ADM", Link, LREF_NOT_ADM); + + // + // Intentionally fall through to the next case. + // + + case LINK_STATE_W_DISC_RSP: + case LINK_STATE_CONNECTING: + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + break; + + } +} /* NbfActivateLink */ + + +VOID +NbfWaitLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine waits for a remote link activation if it is not already + active. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfWaitLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + switch (Link->State) { + case LINK_STATE_READY: + NbfCompleteLink (Link); + break; + + case LINK_STATE_W_DISC_RSP: + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + break; + + } +} /* NbfWaitLink */ + + +VOID +NbfStopLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine terminates a link and all outstanding connections attached + to the link. It is called from routines such as ExpireT2Timer, because + the remote connection partner seems dead or inoperative. As a consequence + of this routine being called, every outstanding connection will have its + disconnect handler called (in NbfStopConnection). + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_PACKET packet; + PTP_CONNECTION connection; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfStopLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + // Take a reference so the link won't go away inside this function + + NbfReferenceLink("Temp in NbfStopLink", Link, LREF_STOPPING); + + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + p = RemoveHeadList (&Link->ConnectionDatabase); + + while (p != &Link->ConnectionDatabase) { + + // + // This will allow this connection to be "removed" + // from its link's list in NbfDisconnectFromLink, even if + // its not on a list. + // + InitializeListHead (p); + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfStopLink stopping connection, refcnt=%ld", + connection->ReferenceCount); + } +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TpStopLink stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + NbfStopConnection (connection, STATUS_LINK_FAILED); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = RemoveHeadList (&Link->ConnectionDatabase); + } + + // + // We hold the link spinlock here. + // + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is in ADM mode. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + + // + // Make sure we are not waiting for a deferred RR to be sent. + // + + if (Link->OnDeferredRrQueue) { + + ACQUIRE_DPC_SPIN_LOCK (Link->ProviderInterlock); + if (Link->OnDeferredRrQueue) { + RemoveEntryList (&Link->DeferredRrLinkage); + Link->OnDeferredRrQueue = FALSE; + } + RELEASE_DPC_SPIN_LOCK (Link->ProviderInterlock); + + } + + // Remove the temporary reference. + + NbfDereferenceLink ("Temp in NbfStopLink", Link, LREF_STOPPING); + + +} /* NbfStopLink */ + + +VOID +NbfResetLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called by DLC.C routines only to reset this link + object and restart in-progress transport data transfers. + + NOTE: This routine is called with the link spinlock acquired + at *OldIrqlP, and will return with it held, although it may + release it in the interim. + +Arguments: + + Link - Pointer to a transport link object. + + OldIrqlP - Pointer to where the IRQL at which Link->SpinLock + was acquired is stored. + +Return Value: + + none. + +--*/ + +{ + PTP_PACKET packet; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfResetLink: Entered for link %lx.\n", Link); + } + + // + // Reset the link state to waiting for connection to start. + // Note that this is NOT the same as initiating a new link, as some things + // don't change, such as provider (devicecontext binding stays the same), + // Max Packet Length (can't change if provider doesn't), and other things + // that would bind this link structure to a different provider or provider + // type. Note also that we acquire the spinlock because, in the case of a + // link that's dropped (remotely) and is restarting, activities on this + // link could be occurring while we're in this routine. + // + + StopT1 (Link); + StopT2 (Link); + // StopTi (Link); + Link->Flags = 0; // clear this, keep DeferredFlags + + Link->SendState = SEND_STATE_DOWN; // send side is down. + Link->NextSend = 0; + Link->LastAckReceived = 0; + if (Link->Provider->MacInfo.MediumAsync) { + Link->SendWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow; + Link->PrevWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow; + } else { + Link->SendWindowSize = (UCHAR)1; + Link->PrevWindowSize = (UCHAR)1; + } + Link->WindowsUntilIncrease = 1; + Link->LinkBusy = FALSE; + Link->ConsecutiveLastPacketLost = 0; + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is resetting. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + Link->ReceiveState = RECEIVE_STATE_DOWN; // receive side down. + Link->NextReceive = 0; + Link->LastAckSent = 0; + Link->ReceiveWindowSize = 1; + + Link->WindowErrors = 0; + Link->BestWindowSize = 1; + Link->WorstWindowSize = (UCHAR)Link->MaxWindowSize; + Link->Flags |= LINK_FLAGS_JUMP_START; + + // + // This must be accurate before we set up timeouts. + // + + Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout; + Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY; + Link->MinimumBaseT1Timeout = Link->Provider->MinimumT1Timeout << DLC_TIMER_ACCURACY; + Link->BaseT1RecalcThreshhold = Link->MaxFrameSize / 2; + Link->CurrentPollRetransmits = 0; + Link->CurrentT1Backoff = FALSE; + Link->CurrentPollOutstanding = FALSE; + Link->RemoteNoPoll = TRUE; + Link->ConsecutiveIFrames = 0; + Link->T2Timeout = Link->Provider->DefaultT2Timeout; + Link->TiTimeout = Link->Provider->DefaultTiTimeout; + Link->LlcRetries = Link->Provider->LlcRetries; + Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize; + + Link->SendRetries = (UCHAR)Link->LlcRetries; + +} /* NbfResetLink */ + + +VOID +NbfDumpLinkInfo ( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when any of the link timers fire and the + link send state is not ready. This gives us a way to track the + link state when strange things are happening. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ +{ + Link; // avoid compiler warnings in non-debug versions + +#if DBG + NbfPrint4 ("NbfDumpLinkInfo: Link %lx : State: %x SendState: %x ReceiveState: %x\n", + Link, Link->State, Link->SendState, Link->ReceiveState); + NbfPrint1 (" Flags: %lx\n",Link->Flags); + NbfPrint4 (" NextReceive: %d LastAckRcvd: %d NextSend: %d LastAckSent: %d\n", + Link->NextReceive, Link->LastAckReceived, Link->NextSend, Link->LastAckSent); +#endif + +} diff --git a/private/ntos/tdi/nbf/linktree.c b/private/ntos/tdi/nbf/linktree.c new file mode 100644 index 000000000..5c62fd8b8 --- /dev/null +++ b/private/ntos/tdi/nbf/linktree.c @@ -0,0 +1,537 @@ +/*++ + +Copyright (c) 1990, 1991 Microsoft Corporation + +Module Name: + + linktree.c + +Abstract: + + This module contains code which implements the management of the link + splay tree. This splay tree is maintained to minimize the lookup time + needed with each individual packet that comes in. To this end, we create a + ULARGE_INTEGER that contains the transport address of the remote and + do a ULARGE_INTEGER comaprison of the addresses (rather than comparing + the bytes 1 by 1). Assuming that the ULARGE_INTEGER comparison routines are + optimized for the hardware on the machine, this should be as fast as or + faster than comparing bytes. + + DEBUG: there is currently code in the comparison routines that will let + me fine-tune the search and ordering algorithm as we gain more + experience with it. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfAddLinkToTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds a link to the tree of links maintained for this device. + Note that since this routine needs to modify the link tree, it is called + in the context of a deferred processing routine, and must have exclusive + access to the tree. The spinlock is taken by the routine that calls this + one, as this operation must be atomic in the eyes of the rest of NBF. + Note further that this routine insists that there not be a link with this + address in the tree. + + As the final operation of this insertion, the splay tree is balanced. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + STATUS_SUCCESS if the link is successfully added, + STATUS_DRIVER_INTERNAL_ERROR if there was a problem adding + the link (implying the tree was in a bad state). + +--*/ +{ + PTP_LINK treeLink; + PRTL_SPLAY_LINKS linkLink; + + // + // initialize the link and check for the trivial case. + // + + RtlInitializeSplayLinks (Link); + linkLink = DeviceContext->LinkTreeRoot; + if (linkLink == NULL) { // null tree, make this the parent + DeviceContext->LinkTreeRoot = (PRTL_SPLAY_LINKS)Link; + DeviceContext->LinkTreeElements++; + DeviceContext->LastLink = Link; + return STATUS_SUCCESS; + } + + // + // Wasn't a null tree, so set up for the addition + // + + treeLink = (PTP_LINK) linkLink; + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint1 ("NbfAddLinkToTree: starting insert, Elements: %ld \n",DeviceContext->LinkTreeElements); + } + + // + // find the proper spot to put this link. + // + + do { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfAddLinkToTree: searching, Link: %lx LC: %lx RC: %lx\n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + + // + // Bad news == means we already have this link, someone is messed up. + // it's possible to be adding and deleting things at the same time; + // that's + // + + if ((treeLink->MagicAddress).QuadPart == (Link->MagicAddress).QuadPart) { + + // + // First make sure we don't have the splay tree in a loop. + // + + ASSERT (treeLink != Link); + + // + // This link is already in the tree. This is OK if it is + // due to be deleted; we can just do the delete right now, + // since AddLinkToTree is only called from the deferred + // timer routine. + // + + if (treeLink->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) { + + // + // It will be in the deferred list. We remove it, + // we don't worry about LinkDeferredActive since + // the timeout routine that is calling us handles + // that. + // + + RemoveEntryList (&treeLink->DeferredList); + + treeLink->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfRemoveLinkFromTree (DeviceContext, treeLink); + NbfDestroyLink (treeLink); + +#if DBG + NbfPrint2 ("NbfAddLinkToTree: Link %lx removed for %lx\n", + treeLink, Link); +#endif + + // + // Now that that link is out of the tree, call + // ourselves recursively to do the insert. + // + + return NbfAddLinkToTree (DeviceContext, Link); + + } else { + + ASSERTMSG ("NbfAddLinkToTree: Found identical Link in tree!\n", FALSE); + return STATUS_DRIVER_INTERNAL_ERROR; + + } + + } + + // + // traverse the tree for the correct spot + // + + if ((Link->MagicAddress).QuadPart < (treeLink->MagicAddress).QuadPart) { + if ((linkLink = RtlLeftChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfAddLinkToTree: Adding link as LC.\n"); + } + RtlInsertAsLeftChild ((PRTL_SPLAY_LINKS)treeLink, + (PRTL_SPLAY_LINKS)Link); + // DeviceContext->LinkTreeRoot = RtlSplay (DeviceContext->LinkTreeRoot); + DeviceContext->LinkTreeElements++; + return STATUS_SUCCESS; + + } else { + treeLink = (PTP_LINK) linkLink; + continue; + } // Left Child + + } else { // is greater + if ((linkLink = RtlRightChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfAddLinkToTree: Adding link as RC.\n"); + } + RtlInsertAsRightChild ((PRTL_SPLAY_LINKS)treeLink, + (PRTL_SPLAY_LINKS)Link); + // DeviceContext->LinkTreeRoot = RtlSplay (DeviceContext->LinkTreeRoot); + DeviceContext->LinkTreeElements++; + return STATUS_SUCCESS; + + } else { + treeLink = (PTP_LINK) linkLink; + continue; + } // Right Child + + } // end else addresses comparison + + } while (TRUE); + + +} // NbfAddLinkToTree + + +NTSTATUS +NbfRemoveLinkFromTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine removes a link from the tree of links. + Note that since this routine needs to modify the link tree, it is called + in the context of a deferred processing routine, and must have exclusive + access to the tree. The spinlock is taken by the routine that calls this + one, as this operation must be atomic in the eyes of the rest of NBF. + Note further that this routine insists that there not be a link with this + address in the tree. + +Arguments: + + Link - Pointer to a transport link object. + DeviceContext - pointer to the device context on which this + +Return Value: + + STATUS_SUCCESS if the link was removed, + STATUS_DRIVER_INTERNAL_ERROR if there was a problem removing + the link (implying the tree was in a bad state). + +--*/ +{ + DeviceContext->LinkTreeRoot = RtlDelete ((PRTL_SPLAY_LINKS)Link); + DeviceContext->LinkTreeElements--; + if (DeviceContext->LastLink == Link) { + DeviceContext->LastLink = (PTP_LINK)DeviceContext->LinkTreeRoot; + } + return STATUS_SUCCESS; + +} //NbfRemoveLinkFromTree + + + +PTP_LINK +NbfFindLinkInTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ) + +/*++ + +Routine Description: + + This routine traverses the link tree looking for the given remote address. + The link tree spinlock is held while looking for the link. After the link + is found, it's reference count is incremented. + + NOTE: This function is called with the device context LinkSpinLock + held. + +Arguments: + + DeviceContext - pointer to the device this address is associated with. + + Remote - pointer to the hardware address of the remote node. + +Return Value: + + Pointer to the link in the tree that matches this remote address. If + no link is found, NULL is returned. + +--*/ +{ + PTP_LINK link; + PRTL_SPLAY_LINKS linkLink; + ULARGE_INTEGER Magic = {0,0}; + + + // + // Are there even any links in the tree? + // + + if (DeviceContext->LinkTreeElements <= 0) { + return NULL; + } + + linkLink = DeviceContext->LinkTreeRoot; + + // + // Make a magic number for this link + // + + MacReturnMagicAddress (&DeviceContext->MacInfo, Remote, &Magic); + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint1 ("NbfFindLinkInTree: starting search, Elements: %ld \n", + DeviceContext->LinkTreeElements); + } + + // + // Do a quick check if the last link found is this one. + // + + ASSERT (DeviceContext->LastLink != NULL); + + if ((DeviceContext->LastLink->MagicAddress).QuadPart == Magic.QuadPart) { + + link = DeviceContext->LastLink; + + } else { + + // + // find the link. + // + + link = (PTP_LINK) linkLink; // depends upon splay links being first + // subfield in link! + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: searching, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + + do { + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint4 ("NbfFindLinkInTree: Comparing: %lx%lx to %lx%lx\n", + link->MagicAddress.HighPart,link->MagicAddress.LowPart, + Magic.HighPart, Magic.LowPart); + } + + if ((link->MagicAddress).QuadPart == Magic.QuadPart) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: equal, going to end.\n"); + } + break; + + } else { + if ((link->MagicAddress).QuadPart < Magic.QuadPart) { + if ((linkLink = RtlRightChild (linkLink)) == NULL) { + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Not Found.\n"); + } + return NULL; + + } else { + link = (PTP_LINK) linkLink; + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: less, took right child, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + continue; + } + + } else { // is greater + if ((linkLink = RtlLeftChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Not Found.\n"); + } + return NULL; + + } else { + link = (PTP_LINK) linkLink; + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: greater, took left child, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + continue; + } // got left child branch + } // greater branch + } // equal to branch + } while (TRUE); + + DeviceContext->LastLink = link; + + } + + // + // Only break out when we've actually found a match.. + // + + if ((link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Found but delete pending.\n"); + } + return NULL; + } + + // + // Mark the link as in use and say we don't need the tree stable any more. + // + + NbfReferenceLink ("Found in tree", link, LREF_TREE); + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Found.\n"); + } + + return link; + +} // NbfFindLinkInTree + + +PTP_LINK +NbfFindLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ) + +/*++ + +Routine Description: + + This routine looks for a link in the link tree, and if + not found there in the deferred queue. + +Arguments: + + DeviceContext - pointer to the device this address is associated with. + + Remote - pointer to the hardware address of the remote node. + +Return Value: + + Pointer to the link in the tree that matches this remote address. If + no link is found, NULL is returned. + +--*/ + +{ + PTP_LINK Link; + BOOLEAN MatchedLink; + PLIST_ENTRY p; + UINT i; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + Link = NbfFindLinkInTree (DeviceContext, Remote); + + if (Link == NULL) { + + // + // Not found there, try in deferred queue. + // + + MatchedLink = FALSE; // Assume failure + + // + // Hold the spinlock while we walk the deferred list. We need + // TimerSpinLock to stop the list from changing, and we need + // LinkSpinLock to synchronize checking DEFERRED_DELETE and + // referencing the link. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + for (p = DeviceContext->LinkDeferred.Flink; + p != &DeviceContext->LinkDeferred; + p = p->Flink) { + + // + // BUGBUG: What about taking a lock while we walk + // this list? It won't be removed from at the front, + // but it may be added to at the back. + // + + // + // We're probably still getting this link to the splay tree. + // find it and process normally. + // + + Link = CONTAINING_RECORD (p, TP_LINK, DeferredList); + + // + // NOTE: We know that the link is not going to be destroyed + // now, because we have increased the semaphore. We + // reference the link if DEFERRED_DELETE is not on; the + // setting of this flag is synchronized (also using + // DeviceContext->LinkSpinLock) with the refcount going + // to 0). + // + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + continue; // we're deleting link, can't handle + } + + for (i=0; i<(UINT)DeviceContext->MacInfo.AddressLength; i++) { + if (Remote[i] != Link->HardwareAddress.Address[i]){ + break; + } + } + + if (i == (UINT)DeviceContext->MacInfo.AddressLength) { // addresses match. Deliver packet. + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfFindLink: Found link on deferred queue, Link: %lx\n", + Link); + } + NbfReferenceLink ("Got Frame on Deferred", Link, LREF_TREE); + MatchedLink = TRUE; + break; + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If this didn't find the link, make note of that. + // + + if (MatchedLink == FALSE) { + + Link = (PTP_LINK)NULL; + + } + + } else { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfFindLink: Found link in tree, Link: %lx\n", Link); + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + return Link; + +} diff --git a/private/ntos/tdi/nbf/makefile b/private/ntos/tdi/nbf/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/nbf/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/nbf/nbf.h b/private/ntos/tdi/nbf/nbf.h new file mode 100644 index 000000000..2efb3f50d --- /dev/null +++ b/private/ntos/tdi/nbf/nbf.h @@ -0,0 +1,166 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbf.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport + provider subcomponent of the NTOS project. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove PDI and PC586-specific support; add NDIS support + +--*/ + +#ifndef _NBF_ +#define _NBF_ + +#include + +typedef struct _RTL_SPLAY_LINKS { + struct _RTL_SPLAY_LINKS *Parent; + struct _RTL_SPLAY_LINKS *LeftChild; + struct _RTL_SPLAY_LINKS *RightChild; +} RTL_SPLAY_LINKS; +typedef RTL_SPLAY_LINKS *PRTL_SPLAY_LINKS; + +#define RtlInitializeSplayLinks(Links) { \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ + } + +#define RtlLeftChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->LeftChild \ + ) + +#define RtlRightChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->RightChild \ + ) + +#define RtlInsertAsLeftChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +#define RtlInsertAsRightChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + + +PRTL_SPLAY_LINKS +NTAPI +RtlDelete ( + PRTL_SPLAY_LINKS Links + ); + + +VOID +NTAPI +RtlGetCallersAddress( + OUT PVOID *CallersAddress, + OUT PVOID *CallersCaller + ); + +#include // Transport Driver Interface. + +#include // Physical Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "nbfconst.h" // private NETBEUI constants. +#include "nbfmac.h" // mac-specific definitions +#include "nbfhdrs.h" // private NETBEUI protocol headers. +#include "nbftypes.h" // private NETBEUI types. +#include "nbfcnfg.h" // configuration information. +#include "nbfprocs.h" // private NETBEUI function prototypes. +#ifdef MEMPRINT +#include "memprint.h" // drt's memory debug print +#endif + + +#ifndef NBF_LOCKS + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) + +#if 0 +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { KIRQL OldIrql; ASSERT ((lock != NULL) && (KeGetCurrentIrql() == DISPATCH_LEVEL)); KeAcquireSpinLock(lock,&OldIrql); } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + { ASSERT(lock != NULL); KeReleaseSpinLock(lock,DISPATCH_LEVEL); } +#else +#define ACQUIRE_DPC_SPIN_LOCK(lock) KeAcquireSpinLockAtDpcLevel(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) KeReleaseSpinLockFromDpcLevel(lock) +#endif + +#define ENTER_NBF +#define LEAVE_NBF + +#else + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +#define ACQUIRE_SPIN_LOCK(lock,irql) \ + NbfAcquireSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) +#define RELEASE_SPIN_LOCK(lock,irql) \ + NbfReleaseSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) + +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { \ + KIRQL OldIrql; \ + NbfAcquireSpinLock( lock, &OldIrql, #lock, __FILE__, __LINE__ ); \ + } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + NbfReleaseSpinLock( lock, DISPATCH_LEVEL, #lock, __FILE__, __LINE__ ) + +#define ENTER_NBF \ + NbfAcquireSpinLock( (PKSPIN_LOCK)NULL, (PKIRQL)NULL, "(Global)", __FILE__, __LINE__ ) +#define LEAVE_NBF \ + NbfReleaseSpinLock( (PKSPIN_LOCK)NULL, (KIRQL)-1, "(Global)", __FILE__, __LINE__ ) + +#endif + + +#endif // def _NBF_ diff --git a/private/ntos/tdi/nbf/nbf.rc b/private/ntos/tdi/nbf/nbf.rc new file mode 100644 index 000000000..be3b9d409 --- /dev/null +++ b/private/ntos/tdi/nbf/nbf.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NetBEUI Frames Protocol Driver" +#define VER_INTERNALNAME_STR "nbf.sys" +#define VER_ORIGINALFILENAME_STR "nbf.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/nbf/nbfcnfg.c b/private/ntos/tdi/nbf/nbfcnfg.c new file mode 100644 index 000000000..30d0a02f3 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfcnfg.c @@ -0,0 +1,1155 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfconfig.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of NBF. Note that the parts of this file that are + called at initialization time will be replaced by calls to the configuration manager over time. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modified to use new tdi interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local functions used to access the registry. +// + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +NbfOpenParametersKey( + IN HANDLE NbfConfigHandle, + OUT PHANDLE ParametersHandle + ); + +VOID +NbfCloseParametersKey( + IN HANDLE ParametersHandle + ); + +NTSTATUS +NbfCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbfAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbfAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +VOID +NbfReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ); + +ULONG +NbfReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ); + +VOID +NbfWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ); + +UINT +NbfWstrLength( + IN PWSTR Wstr + ); + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfWstrLength) +#pragma alloc_text(INIT,NbfConfigureTransport) +#pragma alloc_text(INIT,NbfFreeConfigurationInfo) +#pragma alloc_text(INIT,NbfOpenParametersKey) +#pragma alloc_text(INIT,NbfCloseParametersKey) +#pragma alloc_text(INIT,NbfCountEntries) +#pragma alloc_text(INIT,NbfAddBind) +#pragma alloc_text(INIT,NbfAddExport) +#pragma alloc_text(INIT,NbfReadLinkageInformation) +#pragma alloc_text(INIT,NbfReadSingleParameter) +#pragma alloc_text(INIT,NbfWriteSingleParameter) +#else +#pragma alloc_text(PAGE,NbfWstrLength) +#pragma alloc_text(PAGE,NbfConfigureTransport) +#pragma alloc_text(PAGE,NbfFreeConfigurationInfo) +#pragma alloc_text(PAGE,NbfOpenParametersKey) +#pragma alloc_text(PAGE,NbfCloseParametersKey) +#pragma alloc_text(PAGE,NbfCountEntries) +#pragma alloc_text(PAGE,NbfAddBind) +#pragma alloc_text(PAGE,NbfAddExport) +#pragma alloc_text(PAGE,NbfReadLinkageInformation) +#pragma alloc_text(PAGE,NbfReadSingleParameter) +#pragma alloc_text(PAGE,NbfWriteSingleParameter) +#endif +#endif + +UINT +NbfWstrLength( + IN PWSTR Wstr + ) +{ + UINT Length = 0; + while (*Wstr++) { + Length += sizeof(WCHAR); + } + return Length; +} + +#define InsertAdapter(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = NbfWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, _L, ' FBN'); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[Subscript], _S); \ + } \ +} + +#define InsertDevice(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = NbfWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, _L, ' FBN'); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript], _S); \ + } \ +} + + +#define RemoveAdapter(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[Subscript].Buffer) + +#define RemoveDevice(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript].Buffer) + + + +// +// These strings are used in various places by the registry. +// + +#define DECLARE_STRING(_str_) WCHAR Str ## _str_[] = L#_str_ + + +#define READ_HIDDEN_CONFIG(_Field) \ +{ \ + ConfigurationInfo->_Field = \ + NbfReadSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + +#define WRITE_HIDDEN_CONFIG(_Field) \ +{ \ + NbfWriteSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + + + +NTSTATUS +NbfConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ) +/*++ + +Routine Description: + + This routine is called by NBF to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in nbfcnfg.h file. + +Arguments: + + RegistryPath - The name of NBF's node in the registry. + + ConfigurationInfoPtr - A pointer to the configuration information structure. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + + NTSTATUS OpenStatus; + HANDLE ParametersHandle; + HANDLE NbfConfigHandle; + NTSTATUS Status; + ULONG Disposition; + PWSTR RegistryPathBuffer; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PCONFIG_DATA ConfigurationInfo; + + DECLARE_STRING(InitRequests); + DECLARE_STRING(InitLinks); + DECLARE_STRING(InitConnections); + DECLARE_STRING(InitAddressFiles); + DECLARE_STRING(InitAddresses); + + DECLARE_STRING(MaxRequests); + DECLARE_STRING(MaxLinks); + DECLARE_STRING(MaxConnections); + DECLARE_STRING(MaxAddressFiles); + DECLARE_STRING(MaxAddresses); + + DECLARE_STRING(InitPackets); + DECLARE_STRING(InitReceivePackets); + DECLARE_STRING(InitReceiveBuffers); + DECLARE_STRING(InitUIFrames); + + DECLARE_STRING(SendPacketPoolSize); + DECLARE_STRING(ReceivePacketPoolSize); + DECLARE_STRING(MaxMemoryUsage); + + DECLARE_STRING(DefaultT1Timeout); + DECLARE_STRING(DefaultT2Timeout); + DECLARE_STRING(DefaultTiTimeout); + DECLARE_STRING(LlcRetries); + DECLARE_STRING(LlcMaxWindowSize); + DECLARE_STRING(MaximumIncomingFrames); + + DECLARE_STRING(NameQueryRetries); + DECLARE_STRING(NameQueryTimeout); + DECLARE_STRING(AddNameQueryRetries); + DECLARE_STRING(AddNameQueryTimeout); + DECLARE_STRING(GeneralRetries); + DECLARE_STRING(GeneralTimeout); + DECLARE_STRING(WanNameQueryRetries); + + DECLARE_STRING(UseDixOverEthernet); + DECLARE_STRING(QueryWithoutSourceRouting); + DECLARE_STRING(AllRoutesNameRecognized); + DECLARE_STRING(MinimumSendWindowLimit); + + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &NbfConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) { + NbfPrint1("NBF: Could not open/create NBF key: %lx\n", Status); + return Status; + } + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint2("%s NBF key: %lx\n", + (Disposition == REG_CREATED_NEW_KEY) ? "created" : "opened", + NbfConfigHandle); + } + + + OpenStatus = NbfOpenParametersKey (NbfConfigHandle, &ParametersHandle); + + if (OpenStatus != STATUS_SUCCESS) { + return OpenStatus; + } + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbfReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)ExAllocatePoolWithTag( + NonPagedPool, + RegistryPath->Length + sizeof(WCHAR), + ' FBN'); + if (RegistryPathBuffer == NULL) { + NbfCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + NbfReadLinkageInformation (RegistryPathBuffer, ConfigurationInfoPtr); + + if (*ConfigurationInfoPtr == NULL) { + ExFreePool (RegistryPathBuffer); + NbfCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + ConfigurationInfo = *ConfigurationInfoPtr; + + + // + // Configure the initial values for some NBF resources. + // + + ConfigurationInfo->InitRequests = 1; + ConfigurationInfo->InitLinks = 2; + ConfigurationInfo->InitConnections = 2; + ConfigurationInfo->InitAddressFiles = 0; + ConfigurationInfo->InitAddresses = 0; + + // + // These are the initial values; remember that the + // resources above also allocate some of these each + // time they are allocated (shown in the comment). + // + + ConfigurationInfo->InitPackets = 30; // + link + 2*conn + ConfigurationInfo->InitReceivePackets = 10; // + link + addr + ConfigurationInfo->InitReceiveBuffers = 5; // + addr + ConfigurationInfo->InitUIFrames = 5; // + addr + conn + + // + // Set the size of the packet pools and the total + // allocateable by NBF. + // + + ConfigurationInfo->SendPacketPoolSize = 100; + ConfigurationInfo->ReceivePacketPoolSize = 30; + ConfigurationInfo->MaxMemoryUsage = 0; // no limit + + + // + // Now initialize the timeout etc. values. + // + + ConfigurationInfo->DefaultT1Timeout = DLC_DEFAULT_T1; + ConfigurationInfo->DefaultT2Timeout = DLC_DEFAULT_T2; + ConfigurationInfo->DefaultTiTimeout = DLC_DEFAULT_TI; + ConfigurationInfo->LlcRetries = DLC_RETRIES; + ConfigurationInfo->LlcMaxWindowSize = DLC_WINDOW_LIMIT; + ConfigurationInfo->MaximumIncomingFrames = 4; + ConfigurationInfo->NameQueryRetries = NAME_QUERY_RETRIES; + ConfigurationInfo->NameQueryTimeout = NAME_QUERY_TIMEOUT; + ConfigurationInfo->AddNameQueryRetries = ADD_NAME_QUERY_RETRIES; + ConfigurationInfo->AddNameQueryTimeout = ADD_NAME_QUERY_TIMEOUT; + ConfigurationInfo->GeneralRetries = NAME_QUERY_RETRIES; + ConfigurationInfo->GeneralTimeout = NAME_QUERY_TIMEOUT; + ConfigurationInfo->WanNameQueryRetries = WAN_NAME_QUERY_RETRIES; + + ConfigurationInfo->UseDixOverEthernet = 0; + ConfigurationInfo->QueryWithoutSourceRouting = 0; + ConfigurationInfo->AllRoutesNameRecognized = 0; + ConfigurationInfo->MinimumSendWindowLimit = 2; + + + // + // Now read the optional "hidden" parameters; if these do + // not exist then the current values are used. Note that + // the current values will be 0 unless they have been + // explicitly initialized above. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + READ_HIDDEN_CONFIG (InitRequests); + READ_HIDDEN_CONFIG (InitLinks); + READ_HIDDEN_CONFIG (InitConnections); + READ_HIDDEN_CONFIG (InitAddressFiles); + READ_HIDDEN_CONFIG (InitAddresses); + + READ_HIDDEN_CONFIG (MaxRequests); + READ_HIDDEN_CONFIG (MaxLinks); + READ_HIDDEN_CONFIG (MaxConnections); + READ_HIDDEN_CONFIG (MaxAddressFiles); + READ_HIDDEN_CONFIG (MaxAddresses); + + READ_HIDDEN_CONFIG (InitPackets); + READ_HIDDEN_CONFIG (InitReceivePackets); + READ_HIDDEN_CONFIG (InitReceiveBuffers); + READ_HIDDEN_CONFIG (InitUIFrames); + + READ_HIDDEN_CONFIG (SendPacketPoolSize); + READ_HIDDEN_CONFIG (ReceivePacketPoolSize); + READ_HIDDEN_CONFIG (MaxMemoryUsage); + + READ_HIDDEN_CONFIG (DefaultT1Timeout); + READ_HIDDEN_CONFIG (DefaultT2Timeout); + READ_HIDDEN_CONFIG (DefaultTiTimeout); + READ_HIDDEN_CONFIG (LlcRetries); + READ_HIDDEN_CONFIG (LlcMaxWindowSize); + READ_HIDDEN_CONFIG (MaximumIncomingFrames); + + READ_HIDDEN_CONFIG (NameQueryRetries); + READ_HIDDEN_CONFIG (NameQueryTimeout); + READ_HIDDEN_CONFIG (AddNameQueryRetries); + READ_HIDDEN_CONFIG (AddNameQueryTimeout); + READ_HIDDEN_CONFIG (GeneralRetries); + READ_HIDDEN_CONFIG (GeneralTimeout); + READ_HIDDEN_CONFIG (WanNameQueryRetries); + + READ_HIDDEN_CONFIG (UseDixOverEthernet); + READ_HIDDEN_CONFIG (QueryWithoutSourceRouting); + READ_HIDDEN_CONFIG (AllRoutesNameRecognized); + READ_HIDDEN_CONFIG (MinimumSendWindowLimit); + + + // + // Print out some config info, to make sure it is read right. + // + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint2("Links: init %d, max %d\n", + ConfigurationInfo->InitLinks, + ConfigurationInfo->MaxLinks); + NbfPrint3("Timeouts (NBF ticks): T1 %d, T2 %d, Ti %d\n", + ConfigurationInfo->DefaultT1Timeout / SHORT_TIMER_DELTA, + ConfigurationInfo->DefaultT2Timeout / SHORT_TIMER_DELTA, + ConfigurationInfo->DefaultTiTimeout / LONG_TIMER_DELTA); + NbfPrint2("Pools: send %d, receive %d\n", + ConfigurationInfo->SendPacketPoolSize, + ConfigurationInfo->ReceivePacketPoolSize); + NbfPrint1("Max mem %d\n", + ConfigurationInfo->MaxMemoryUsage); + NbfPrint2("NQRetries %d, NQTimeout %d\n", + ConfigurationInfo->NameQueryRetries, + ConfigurationInfo->NameQueryTimeout / SHORT_TIMER_DELTA); + } + + // + // Save the "hidden" parameters, these may not exist in + // the registry. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + // + // 5/22/92 - don't write the parameters that are set + // based on Size, since otherwise these will overwrite + // those values since hidden parameters are set up + // after the Size-based configuration is done. + // + + WRITE_HIDDEN_CONFIG (MaxRequests); + WRITE_HIDDEN_CONFIG (MaxLinks); + WRITE_HIDDEN_CONFIG (MaxConnections); + WRITE_HIDDEN_CONFIG (MaxAddressFiles); + WRITE_HIDDEN_CONFIG (MaxAddresses); + + WRITE_HIDDEN_CONFIG (DefaultT1Timeout); + WRITE_HIDDEN_CONFIG (DefaultT2Timeout); + WRITE_HIDDEN_CONFIG (DefaultTiTimeout); + WRITE_HIDDEN_CONFIG (LlcRetries); + WRITE_HIDDEN_CONFIG (LlcMaxWindowSize); + WRITE_HIDDEN_CONFIG (MaximumIncomingFrames); + + WRITE_HIDDEN_CONFIG (NameQueryRetries); + WRITE_HIDDEN_CONFIG (NameQueryTimeout); + WRITE_HIDDEN_CONFIG (AddNameQueryRetries); + WRITE_HIDDEN_CONFIG (AddNameQueryTimeout); + WRITE_HIDDEN_CONFIG (GeneralRetries); + WRITE_HIDDEN_CONFIG (GeneralTimeout); + WRITE_HIDDEN_CONFIG (WanNameQueryRetries); + + WRITE_HIDDEN_CONFIG (UseDixOverEthernet); + WRITE_HIDDEN_CONFIG (QueryWithoutSourceRouting); + WRITE_HIDDEN_CONFIG (AllRoutesNameRecognized); + + // ZwFlushKey (ParametersHandle); + + ExFreePool (RegistryPathBuffer); + NbfCloseParametersKey (ParametersHandle); + ZwClose (NbfConfigHandle); + + return STATUS_SUCCESS; + +} /* NbfConfigureTransport */ + + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by NBF to get free any storage that was allocated + by NbfConfigureTransport in producing the specified CONFIG_DATA structure. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + UINT i; + + for (i=0; iNumAdapters; i++) { + RemoveAdapter (ConfigurationInfo, i); + RemoveDevice (ConfigurationInfo, i); + } + ExFreePool (ConfigurationInfo); + +} /* NbfFreeConfigurationInfo */ + + +NTSTATUS +NbfOpenParametersKey( + IN HANDLE NbfConfigHandle, + OUT PHANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by NBF to open the NBF "Parameters" key. + +Arguments: + + ParametersHandle - Returns the handle used to read parameters. + +Return Value: + + The status of the request. + +--*/ +{ + + NTSTATUS Status; + HANDLE ParamHandle; + PWSTR ParametersString = L"Parameters"; + UNICODE_STRING ParametersKeyName; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + // + // Open the NBF parameters key. + // + + RtlInitUnicodeString (&ParametersKeyName, ParametersString); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &ParametersKeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + NbfConfigHandle, // root + NULL // security descriptor + ); + + + Status = ZwOpenKey( + &ParamHandle, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + NbfPrint1("Could not open parameters key: %lx\n", Status); + return Status; + + } + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint1("Opened parameters key: %lx\n", ParamHandle); + } + + + *ParametersHandle = ParamHandle; + + + // + // All keys successfully opened or created. + // + + return STATUS_SUCCESS; + +} /* NbfOpenParametersKey */ + +VOID +NbfCloseParametersKey( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by NBF to close the "Parameters" key. + It closes the handles passed in and does any other work needed. + +Arguments: + + ParametersHandle - The handle used to read other parameters. + +Return Value: + + None. + +--*/ + +{ + + ZwClose (ParametersHandle); + +} /* NbfCloseParametersKey */ + + +NTSTATUS +NbfCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called with the "Bind" and "Export" multi-strings. + It counts the number of name entries required in the + CONFIGURATION_DATA structure and then allocates it. + +Arguments: + + ValueName - The name of the value ("Bind" or "Export" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to a pointer to the ConfigurationInfo structure. + When the "Export" callback is made this is filled in + with the allocate structure. + + EntryContext - A pointer to a counter holding the total number + of name entries required. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + ULONG StringCount; + PWCHAR ValuePointer = (PWCHAR)ValueData; + PCONFIG_DATA * ConfigurationInfo = (PCONFIG_DATA *)Context; + PULONG TotalCount = ((PULONG)EntryContext); + ULONG OldTotalCount = *TotalCount; + +#if DBG + ASSERT (ValueType == REG_MULTI_SZ); +#else + UNREFERENCED_PARAMETER(ValueType); +#endif + + // + // Count the number of strings in the multi-string; first + // check that it is NULL-terminated to make the rest + // easier. + // + + if ((ValueLength < 2) || + (ValuePointer[(ValueLength/2)-1] != (WCHAR)'\0')) { + return STATUS_INVALID_PARAMETER; + } + + StringCount = 0; + while (*ValuePointer != (WCHAR)'\0') { + while (*ValuePointer != (WCHAR)'\0') { + ++ValuePointer; + } + ++StringCount; + ++ValuePointer; + if ((ULONG)((PUCHAR)ValuePointer - (PUCHAR)ValueData) >= ValueLength) { + break; + } + } + + (*TotalCount) += StringCount; + + if (*ValueName == (WCHAR)'E') { + + // + // This is "Export", allocate the config data structure. + // + + *ConfigurationInfo = ExAllocatePoolWithTag( + NonPagedPool, + sizeof (CONFIG_DATA) + + ((*TotalCount-1) * sizeof(NDIS_STRING)), + ' FBN'); + + if (*ConfigurationInfo == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( + *ConfigurationInfo, + sizeof(CONFIG_DATA) + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + (*ConfigurationInfo)->DevicesOffset = OldTotalCount; + + } + + return STATUS_SUCCESS; + +} /* NbfCountEntries */ + + +NTSTATUS +NbfAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurBindNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertAdapter( + ConfigurationInfo, + *CurBindNum, + (PWSTR)(ValueData)); + + ++(*CurBindNum); + + return STATUS_SUCCESS; + +} /* NbfAddBind */ + + +NTSTATUS +NbfAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of exports that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurExportNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertDevice( + ConfigurationInfo, + *CurExportNum, + (PWSTR)(ValueData)); + + ++(*CurExportNum); + + return STATUS_SUCCESS; + +} /* NbfAddExport */ + + +VOID +NbfReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by NBF to read its linkage information + from the registry. If there is none present, then ConfigData + is filled with a list of all the adapters that are known + to NBF. + +Arguments: + + RegistryPathBuffer - The null-terminated root of the NBF registry tree. + + ConfigurationInfo - Returns NBF's current configuration. + +Return Value: + + None. + +--*/ + +{ + + UINT ConfigBindings; + UINT NameCount = 0; + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[6]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG BindCount, ExportCount; + UINT i; + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below NBF + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call NbfCountEntries for the "Bind" multi-string + // + + QueryTable[1].QueryRoutine = NbfCountEntries; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&NameCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call NbfCountEntries for the "Export" multi-string + // + + QueryTable[2].QueryRoutine = NbfCountEntries; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[2].Name = Export; + QueryTable[2].EntryContext = (PVOID)&NameCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4) Call NbfAddBind for each string in "Bind" + // + + QueryTable[3].QueryRoutine = NbfAddBind; + QueryTable[3].Flags = 0; + QueryTable[3].Name = Bind; + QueryTable[3].EntryContext = (PVOID)&BindCount; + QueryTable[3].DefaultType = REG_NONE; + + // + // 5) Call NbfAddExport for each string in "Export" + // + + QueryTable[4].QueryRoutine = NbfAddExport; + QueryTable[4].Flags = 0; + QueryTable[4].Name = Export; + QueryTable[4].EntryContext = (PVOID)&ExportCount; + QueryTable[4].DefaultType = REG_NONE; + + // + // 6) Stop + // + + QueryTable[5].QueryRoutine = NULL; + QueryTable[5].Flags = 0; + QueryTable[5].Name = NULL; + + + BindCount = 0; + ExportCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPathBuffer, + QueryTable, + (PVOID)ConfigurationInfo, + NULL); + + if (Status != STATUS_SUCCESS) { + return; + } + + // + // Make sure that BindCount and ExportCount match, if not + // remove the extras. + // + + if (BindCount < ExportCount) { + + for (i=BindCount; iNumAdapters = ConfigBindings; + +} /* NbfReadLinkageInformation */ + + +ULONG +NbfReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ) + +/*++ + +Routine Description: + + This routine is called by NBF to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + ULONG InformationBuffer[32]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + UNICODE_STRING ValueKeyName; + ULONG InformationLength; + ULONG ReturnValue; + NTSTATUS Status; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwQueryValueKey( + ParametersHandle, + &ValueKeyName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + if ((Status == STATUS_SUCCESS) && + (Information->DataLength == sizeof(ULONG))) { + + RtlCopyMemory( + (PVOID)&ReturnValue, + ((PUCHAR)Information) + Information->DataOffset, + sizeof(ULONG)); + + if (ReturnValue < 0) { + + ReturnValue = DefaultValue; + + } + + } else { + + ReturnValue = DefaultValue; + + } + + return ReturnValue; + +} /* NbfReadSingleParameter */ + + +VOID +NbfWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ) + +/*++ + +Routine Description: + + This routine is called by NBF to write a single parameter + from the registry. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to store. + + ValueData - The data to store at the value. + +Return Value: + + None. + +--*/ + +{ + UNICODE_STRING ValueKeyName; + NTSTATUS Status; + ULONG TmpValueData = ValueData; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwSetValueKey( + ParametersHandle, + &ValueKeyName, + 0, + REG_DWORD, + (PVOID)&TmpValueData, + sizeof(ULONG)); + + if (!NT_SUCCESS(Status)) { + NbfPrint1("NBF: Could not write dword key: %lx\n", Status); + } + +} /* NbfWriteSingleParameter */ + diff --git a/private/ntos/tdi/nbf/nbfcnfg.h b/private/ntos/tdi/nbf/nbfcnfg.h new file mode 100644 index 000000000..ca8c382f3 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfcnfg.h @@ -0,0 +1,102 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfcnfg.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport. This + file defines all constants and structures necessary for support of + the dynamic configuration of NBF. Note that this file will be replaced + by calls to the configuration manager over time. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Revision History: + +--*/ + +#ifndef _NBFCONFIG_ +#define _NBFCONFIG_ + +// +// Define the devices we support; this is in leiu of a real configuration +// manager. +// + +#define NBF_SUPPORTED_ADAPTERS 10 + +#define NE3200_ADAPTER_NAME L"\\Device\\NE320001" +#define ELNKII_ADAPTER_NAME L"\\Device\\Elnkii" // adapter we will talk to +#define ELNKMC_ADAPTER_NAME L"\\Device\\Elnkmc01" +#define ELNK16_ADAPTER_NAME L"\\Device\\Elnk1601" +#define SONIC_ADAPTER_NAME L"\\Device\\Sonic01" +#define LANCE_ADAPTER_NAME L"\\Device\\Lance01" +#define PC586_ADAPTER_NAME L"\\Device\\Pc586" +#define IBMTOK_ADAPTER_NAME L"\\Device\\Ibmtok01" +#define PROTEON_ADAPTER_NAME L"\\Device\\Proteon01" +#define WDLAN_ADAPTER_NAME L"\\Device\\Wdlan01" + + +// +// configuration structure. +// + +typedef struct { + + ULONG InitRequests; + ULONG InitLinks; + ULONG InitConnections; + ULONG InitAddressFiles; + ULONG InitAddresses; + ULONG MaxRequests; + ULONG MaxLinks; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + ULONG InitPackets; + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG InitUIFrames; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxMemoryUsage; + ULONG DefaultT1Timeout; + ULONG DefaultT2Timeout; + ULONG DefaultTiTimeout; + ULONG LlcRetries; + ULONG LlcMaxWindowSize; + ULONG MaximumIncomingFrames; + ULONG NameQueryRetries; + ULONG NameQueryTimeout; + ULONG AddNameQueryRetries; + ULONG AddNameQueryTimeout; + ULONG GeneralRetries; + ULONG GeneralTimeout; + ULONG WanNameQueryRetries; + + ULONG UseDixOverEthernet; + ULONG QueryWithoutSourceRouting; + ULONG AllRoutesNameRecognized; + ULONG MinimumSendWindowLimit; + + // + // Names contains NumAdapters pairs of NDIS adapter names (which + // nbf binds to) and device names (which nbf exports). The nth + // adapter name is in location n and the device name is in + // DevicesOffset+n (DevicesOffset may be different from NumAdapters + // if the registry Bind and Export strings are different sizes). + // + + ULONG NumAdapters; + ULONG DevicesOffset; + NDIS_STRING Names[1]; + +} CONFIG_DATA, *PCONFIG_DATA; + +#endif diff --git a/private/ntos/tdi/nbf/nbfconst.h b/private/ntos/tdi/nbf/nbfconst.h new file mode 100644 index 000000000..9ca5f4072 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfconst.h @@ -0,0 +1,436 @@ + +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbfconst.h + +Abstract: + + This header file defines manifest constants for the NT NBF transport + provider. It is included by nbf.h. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove pc586- and PDI-specific support. Add NDIS support. Note + changes to be made here if MAC dependence of NDIS changes. (search + for (PDI) + +--*/ + +#ifndef _NBFCONST_ +#define _NBFCONST_ + + +// +// DEBUGGING SUPPORT. DBG is a macro that is turned on at compile time +// to enable debugging code in the system. If this is turned on, then +// you can use the IF_NBFDBG(flags) macro in the NBF code to selectively +// enable a piece of debugging code in the transport. This macro tests +// NbfDebug, a global ULONG defined in NBFDRVR.C. +// + +#if DBG + +#define NBF_DEBUG_SENDENG 0x00000001 // sendeng.c debugging. +#define NBF_DEBUG_RCVENG 0x00000002 // rcveng.c debugging. +#define NBF_DEBUG_IFRAMES 0x00000004 // displays sent/rec'd iframes. +#define NBF_DEBUG_UFRAMES 0x00000008 // displays sent/rec'd uframes. +#define NBF_DEBUG_DLCFRAMES 0x00000010 // displays sent/rec'd dlc frames. +#define NBF_DEBUG_ADDRESS 0x00000020 // address.c debugging. +#define NBF_DEBUG_CONNECT 0x00000040 // connect.c debugging. +#define NBF_DEBUG_CONNOBJ 0x00000080 // connobj.c debugging. +#define NBF_DEBUG_DEVCTX 0x00000100 // devctx.c debugging. +#define NBF_DEBUG_DLC 0x00000200 // dlc.c data link engine debugging. +#define NBF_DEBUG_INFO 0x00000400 // info.c debugging +#define NBF_DEBUG_EVENT 0x00000800 // event.c debugging. +#define NBF_DEBUG_FRAMECON 0x00001000 // framecon.c debugging. +#define NBF_DEBUG_FRAMESND 0x00002000 // framesnd.c debugging. +#define NBF_DEBUG_DYNAMIC 0x00004000 // dynamic allocation debugging. +#define NBF_DEBUG_LINK 0x00008000 // link.c debugging. +#define NBF_DEBUG_RESOURCE 0x00010000 // resource allocation debugging. +#define NBF_DEBUG_DISPATCH 0x00020000 // IRP request dispatching. +#define NBF_DEBUG_PACKET 0x00040000 // packet.c debugging. +#define NBF_DEBUG_REQUEST 0x00080000 // request.c debugging. +#define NBF_DEBUG_TIMER 0x00100000 // timer.c debugging. +#define NBF_DEBUG_DLCRETRANSMIT 0x00200000 // DLC REJ debugging. +#define NBF_DEBUG_REGISTRY 0x00400000 // registry access. +#define NBF_DEBUG_NDIS 0x00800000 // NDIS related information +#define NBF_DEBUG_LINKTREE 0x01000000 // Link splay tree debugging +#define NBF_DEBUG_TEARDOWN 0x02000000 // link/connection teardown info +#define NBF_DEBUG_REFCOUNTS 0x04000000 // link/connection ref/deref information +#define NBF_DEBUG_IRP 0x08000000 // irp completion debugging +#define NBF_DEBUG_DATAGRAMS 0x10000000 // datagram send/receive +#define NBF_DEBUG_SETUP 0x20000000 // debug session setup +#define NBF_DEBUG_CONFIG 0x40000000 // debug configuration + +// +// past here are debug things that are really frequent; don't use them +// unless you want LOTS of output +// +#define NBF_DEBUG_TIMERDPC 0x10000000 // the timer DPC +#define NBF_DEBUG_PKTCONTENTS 0x20000000 // dump packet contents in dbg +#define NBF_DEBUG_TIMERCODE 0x40000000 // enable check code in timer +#define NBF_DEBUG_TRACKTDI 0x80000000 // store tdi info when set + + +extern ULONG NbfDebug; // in NBFDRVR.C. +extern BOOLEAN NbfDisconnectDebug; // in NBFDRVR.C. + +#define TRACK_TDI_LIMIT 25 +#define TRACK_TDI_CAPTURE 36 // chosen to make debug line up nice +typedef struct { + PVOID Request; + PIRP Irp; + PVOID Connection; + UCHAR Contents[TRACK_TDI_CAPTURE]; + } NBF_SEND; + +typedef struct { + PVOID Request; + PIRP Irp; + NTSTATUS Status; + PVOID NothingYet; + } NBF_SEND_COMPLETE; + +typedef struct { + PVOID Request; + PIRP Irp; + PVOID Connection; + PVOID NothingYet; + } NBF_RECEIVE; + +typedef struct { + PVOID Request; + PIRP Irp; + NTSTATUS Status; + UCHAR Contents[TRACK_TDI_CAPTURE]; + } NBF_RECEIVE_COMPLETE; + +extern NBF_SEND NbfSends[TRACK_TDI_LIMIT+1]; +extern LONG NbfSendsNext; + +extern NBF_SEND_COMPLETE NbfCompletedSends[TRACK_TDI_LIMIT+1]; +extern LONG NbfCompletedSendsNext; + +extern NBF_RECEIVE NbfReceives[TRACK_TDI_LIMIT+1]; +extern LONG NbfReceivesNext; + +extern NBF_RECEIVE_COMPLETE NbfCompletedReceives[TRACK_TDI_LIMIT+1]; +extern LONG NbfCompletedReceivesNext; + +#endif + +// +// some convenient constants used for timing. All values are in clock ticks. +// + +#define MICROSECONDS 10 +#define MILLISECONDS 10000 // MICROSECONDS*1000 +#define SECONDS 10000000 // MILLISECONDS*1000 + + +// +// BUGBUG: temporary things used by nbf that are caused by the change-over from +// (never implimented) PDI support to NDIS support. They may be removed pending +// resolution of NDIS issues about MAC support. +// + +#define PDI_SOURCE_ROUTE 0x00000002 // source routing field is specified. +#define PDI_HARDWARE_ADDRESS 0x00000004 // hardware address field is specified. +#define PDI_TRUNCATED 0x00000001 // PSDU was truncated. +#define PDI_FRAGMENT 0x00000002 // PSDU was fragmented. +#define PDI_BROADCAST 0x00000004 // PSDU was broadcast. +#define PDI_MULTICAST 0x00000008 // PSDU was multicast/functional. +#define PDI_SOURCE_ROUTING 0x00000010 // PSDU contained source routing information. + + + +// +// MAJOR PROTOCOL IDENTIFIERS THAT CHARACTERIZE THIS DRIVER. +// + +#define NBF_DEVICE_NAME L"\\Device\\Nbf"// name of our driver. +#ifdef _PNP_POWER +#define NBF_NAME L"Nbf" // name for protocol chars. +#endif +#define DSAP_NETBIOS_OVER_LLC 0xf0 // NETBEUI always has DSAP 0xf0. +#define PSAP_LLC 0 // LLC always runs over PSAP 0. +#define MAX_SOURCE_ROUTE_LENGTH 32 // max. bytes of SR. info. +#define MAX_NETWORK_NAME_LENGTH 128 // # bytes in netname in TP_ADDRESS. +#define MAX_USER_PACKET_DATA 1500 // max. user bytes per DFM/DOL. + +#define NBF_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// MAJOR CONFIGURATION PARAMETERS THAT WILL BE MOVED TO THE INIT-LARGE_INTEGER +// CONFIGURATION MANAGER. +// + +#define MAX_REQUESTS 30 +#define MAX_UI_FRAMES 25 +#define MAX_SEND_PACKETS 40 +#define MAX_RECEIVE_PACKETS 30 +#define MAX_RECEIVE_BUFFERS 15 +#define MAX_LINKS 10 +#define MAX_CONNECTIONS 10 +#define MAX_ADDRESSFILES 10 +#define MAX_ADDRESSES 10 + +#define MIN_UI_FRAMES 5 // + one per address + one per connection +#define MIN_SEND_PACKETS 20 // + one per link + one per connection +#define MIN_RECEIVE_PACKETS 10 // + one per link + one per address +#define MIN_RECEIVE_BUFFERS 5 // + one per address + +#define SEND_PACKET_RESERVED_LENGTH (sizeof (SEND_PACKET_TAG)) +#define RECEIVE_PACKET_RESERVED_LENGTH (sizeof (RECEIVE_PACKET_TAG)) + + +#define ETHERNET_HEADER_SIZE 14 // BUGBUG: used for current NDIS compliance +#define ETHERNET_PACKET_SIZE 1514 + +#define MAX_DEFERRED_TRAVERSES 6 // number of times we can go through + // the deferred operations queue and + // not do anything without causing an + // error indication + + +// +// NETBIOS PROTOCOL CONSTANTS. +// + +#define NETBIOS_NAME_LENGTH 16 +#define NETBIOS_SESSION_LIMIT 254 // max # of sessions/link. (abs limit is 254) + +#define NAME_QUERY_RETRIES 3 // 2 retrie(s), plus the first one. +#define ADD_NAME_QUERY_RETRIES 3 // 1 retrie(s) plus the first one. +#define WAN_NAME_QUERY_RETRIES 5 // for NdisMediumWan only. + +#define NAME_QUERY_TIMEOUT (500*MILLISECONDS) +#define ADD_NAME_QUERY_TIMEOUT (500*MILLISECONDS) + +// +// DATA LINK PROTOCOL CONSTANTS. +// +// There are two timers, short and long. T1, T2, and the purge +// timer are run off of the short timer, Ti and the adaptive timer +// is run off of the long one. +// + +#define SHORT_TIMER_DELTA (50*MILLISECONDS) +#define LONG_TIMER_DELTA (1*SECONDS) + +#define DLC_DEFAULT_T1 (600 * MILLISECONDS) +#define DLC_DEFAULT_T2 (150 * MILLISECONDS) +#define DLC_DEFAULT_TI (30 * SECONDS) +#define DLC_RETRIES (8) // number of poll retries at LLC level. +#define DLC_RETRANSMIT_THRESHOLD (10) // up to n retransmissions acceptable. +#define DLC_WINDOW_LIMIT (10) // incr. to 127 when packet pool expanded. + +#define DLC_TIMER_ACCURACY 8 // << between BaseT1Timeout and CurrentT1Timeout + + +#define TIMER_ADAPTIVE_TICKS ((DLC_DEFAULT_T1*60)/LONG_TIMER_DELTA) // time between adaptive runs. +#define TIMER_PURGE_TICKS ((DLC_DEFAULT_T1*10)/SHORT_TIMER_DELTA) // time between adaptive purges. + + +// +// TDI defined timeouts +// + +#define TDI_TIMEOUT_SEND 60L // sends go 120 seconds +#define TDI_TIMEOUT_RECEIVE 0L // receives +#define TDI_TIMEOUT_CONNECT 60L +#define TDI_TIMEOUT_LISTEN 0L // listens default to never. +#define TDI_TIMEOUT_DISCONNECT 60L // should be 30 +#define TDI_TIMEOUT_NAME_REGISTRATION 60L + + + +// +// GENERAL CAPABILITIES STATEMENTS THAT CANNOT CHANGE. +// + +#define NBF_MAX_TSDU_SIZE 65535 // maximum TSDU size supported by NetBIOS. +#define NBF_MAX_DATAGRAM_SIZE 512 // maximum Datagram size supported by NetBIOS. +#define NBF_MAX_CONNECTION_USER_DATA 0 // no user data supported on connect. +#define NBF_SERVICE_FLAGS ( \ + TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY | \ + TDI_SERVICE_BROADCAST_SUPPORTED | \ + TDI_SERVICE_MULTICAST_SUPPORTED | \ + TDI_SERVICE_DELAYED_ACCEPTANCE ) + +#define NBF_MIN_LOOKAHEAD_DATA 256 // minimum guaranteed lookahead data. +#define NBF_MAX_LOOKAHEAD_DATA 256 // maximum guaranteed lookahead data. + +#define NBF_MAX_LOOPBACK_LOOKAHEAD 192 // how much is copied over for loopback + +// +// Number of TDI resources that we report. +// + +#define NBF_TDI_RESOURCES 9 + + +// +// NetBIOS name types used in the NetBIOS Frames Protocol Connectionless PDUs. +// + +#define NETBIOS_NAME_TYPE_UNIQUE 0x00 // name is unique on the network. +#define NETBIOS_NAME_TYPE_GROUP 0x01 // name is a group name. +#define NETBIOS_NAME_TYPE_EITHER 0x02 // used in NbfMatchNetbiosAddress + +// +// STATUS_QUERY request types. If the sender is following pre-2.1 protocol, +// then a simple request-response exchange is performed. Later versions +// store the "total number of names received so far" in the request type +// field, except for the first request, which must contain a 1 in this field. +// + +#define STATUS_QUERY_REQUEST_TYPE_PRE21 0x00 // request is 1.x or 2.0. +#define STATUS_QUERY_REQUEST_TYPE_FIRST 0x01 // first request, 2.1 or above. + +// +// If the LocalSessionNumber field contains a 0, then the request is really +// a FIND.NAME. If the field is non-zero, then it is the local session +// number that will be provided in all connection-oriented headers thereafter. +// + +#define NAME_QUERY_LSN_FIND_NAME 0x00 // LSN for FIND.NAME request. + +// +// NAME_RECOGNIZED LocalSessionNumber status values. If the connection +// request was rejected, then one of the following values is placed in +// the LocalSessionNumber field. NAME_RECOGNIZED can also be used as a +// FIND.NAME response, in which case the NO_LISTENS status is overloaded +// to also mean a FIND.NAME. +// + +#define NAME_RECOGNIZED_LSN_NO_LISTENS 0x00 // no listens available. +#define NAME_RECOGNIZED_LSN_FIND_NAME 0x00 // this is a find name response. +#define NAME_RECOGNIZED_LSN_NO_RESOURCE 0xff // listen available, but no resources. + +// +// STATUS_RESPONSE response types. If the sender is following pre-2.1 +// protocol, then a simple request-response exchange is performed. Later +// versions store the "total number of names sent so far" in the request +// type field. This value is cumulative, and includes the count of names +// sent with the current response, as well as from previous responses. +// + +#define STATUS_RESPONSE_PRE21 0x00 // request is 1.x or 2.0. +#define STATUS_RESPONSE_FIRST 0x01 // first request, 2.1 or above. + +// +// DATA_FIRST_MIDDLE option bitflags. +// + +#define DFM_OPTIONS_RECEIVE_CONTINUE 0x01 // RECEIVE_CONTINUE requested. +#define DFM_OPTIONS_NO_ACK 0x02 // no DATA_ACK frame expected. +#define DFM_OPTIONS_RESYNCH 0x04 // set resynch indicator/this frame. +#define DFM_OPTIONS_ACK_INCLUDED 0x08 // piggyback ack included. + +// +// DATA_ONLY_LAST option bitflags. +// + +#define DOL_OPTIONS_RESYNCH 0x01 // set resynch indicator/this frame. +#define DOL_OPTIONS_NO_ACK 0x02 // no DATA_ACK frame expected. +#define DOL_OPTIONS_ACK_W_DATA_ALLOWED 0x04 // piggyback ack allowed. +#define DOL_OPTIONS_ACK_INCLUDED 0x08 // piggyback ack included. + +// +// SESSION_CONFIRM option bitflags. +// + +#define SESSION_CONFIRM_OPTIONS_20 0x01 // set if NETBIOS 2.0 or above. +#define SESSION_CONFIRM_NO_ACK 0x80 // set if NO.ACK protocol supported. + +// +// SESSION_END reason codes. +// + +#define SESSION_END_REASON_HANGUP 0x0000 // normal termination via HANGUP. +#define SESSION_END_REASON_ABEND 0x0001 // abnormal session termination. + +// +// SESSION_INITIALIZE option bitflags. +// + +#define SESSION_INIT_OPTIONS_20 0x01 // set if NETBIOS 2.0 or above. +#define SESSION_INIT_OPTIONS_LF 0x0E // Maximum largest frame value +#define SESSION_INIT_NO_ACK 0x80 // set if NO.ACK protocol supported. + +// +// NO_RECEIVE option bitflags. +// + +#define NO_RECEIVE_PARTIAL_NO_ACK 0x02 // NO.ACK data partially received. + +// +// Resource IDs for query and error logging. +// + +#define LINK_RESOURCE_ID 11 +#define ADDRESS_RESOURCE_ID 12 +#define ADDRESS_FILE_RESOURCE_ID 13 +#define CONNECTION_RESOURCE_ID 14 +#define REQUEST_RESOURCE_ID 15 + +#define UI_FRAME_RESOURCE_ID 21 +#define PACKET_RESOURCE_ID 22 +#define RECEIVE_PACKET_RESOURCE_ID 23 +#define RECEIVE_BUFFER_RESOURCE_ID 24 + + +// +// memory management additions +// + +// +// Fake IOCTLs used for kernel mode testing. +// + +#define IOCTL_NBF_BASE FILE_DEVICE_TRANSPORT + +#define _NBF_CONTROL_CODE(request,method) \ + ((IOCTL_NBF_BASE)<<16 | (request<<2) | method) + +#define IOCTL_TDI_SEND_TEST _NBF_CONTROL_CODE(26,0) +#define IOCTL_TDI_RECEIVE_TEST _NBF_CONTROL_CODE(27,0) +#define IOCTL_TDI_SERVER_TEST _NBF_CONTROL_CODE(28,0) + +// +// More debugging stuff +// + +#define NBF_REQUEST_SIGNATURE ((CSHORT)0x4702) +#define NBF_LINK_SIGNATURE ((CSHORT)0x4703) +#define NBF_CONNECTION_SIGNATURE ((CSHORT)0x4704) +#define NBF_ADDRESSFILE_SIGNATURE ((CSHORT)0x4705) +#define NBF_ADDRESS_SIGNATURE ((CSHORT)0x4706) +#define NBF_DEVICE_CONTEXT_SIGNATURE ((CSHORT)0x4707) +#define NBF_PACKET_SIGNATURE ((CSHORT)0x4708) + +#if DBG +extern PVOID * NbfConnectionTable; +extern PVOID * NbfRequestTable; +extern PVOID * NbfUiFrameTable; +extern PVOID * NbfSendPacketTable; +extern PVOID * NbfLinkTable; +extern PVOID * NbfAddressFileTable; +extern PVOID * NbfAddressTable; +#endif + +#endif // _NBFCONST_ diff --git a/private/ntos/tdi/nbf/nbfdebug.c b/private/ntos/tdi/nbf/nbfdebug.c new file mode 100644 index 000000000..2f5aadbd7 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfdebug.c @@ -0,0 +1,646 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfdebug.c + +Abstract: + + This module contains code that implements debug things for NBF. It is + compiled only if debug is on in the compile phase. + +Author: + + David Beaver (dbeaver) 18-Apr-1991 + +Environment: + + Kernel mode + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modified to use new TDI interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG + +VOID +DisplayOneFrame( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine is a temporary debugging aid that displays an I-frame + before it is sent. This ensures that we have formatted all our packets + correctly. + +Arguments: + + Packet - Pointer to a TP_PACKET representing an I-frame to be displayed. + +Return Value: + + none. + +--*/ + +{ + PCH s, e; + ULONG ns, nr; // I-frame (NetBIOS) cracking. + PNBF_HDR_CONNECTION NbfHeader; + PDLC_I_FRAME DlcHeader; + BOOLEAN Command, PollFinal; + BOOLEAN IsUFrame=FALSE; + UCHAR CmdByte; + + PDLC_S_FRAME SFrame; // DLC frame cracking. + PDLC_U_FRAME UFrame; + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[14]); + NbfHeader = (PNBF_HDR_CONNECTION)&(Packet->Header[18]); + ns = DlcHeader->SendSeq >> 1; + nr = DlcHeader->RcvSeq >> 1; + PollFinal = (BOOLEAN)(DlcHeader->RcvSeq & DLC_I_PF); + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + + if (DlcHeader->SendSeq & DLC_I_INDICATOR) { + IF_NBFDBG (NBF_DEBUG_DLCFRAMES) { + } else { + return; // if DLCFRAMES not set, don't print. + } + + SFrame = (PDLC_S_FRAME)DlcHeader; // alias. + UFrame = (PDLC_U_FRAME)DlcHeader; // alias. + CmdByte = SFrame->Command; + IsUFrame = (BOOLEAN)((UFrame->Command & DLC_U_INDICATOR) == DLC_U_INDICATOR); + if (IsUFrame) { + CmdByte = (UCHAR)(UFrame->Command & ~DLC_U_PF); + } + + switch (CmdByte) { + case DLC_CMD_RR: + s = "RR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_RNR: + s = "RNR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_REJ: + s = "REJ"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_SABME: + s = "SABME"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DISC: + s = "DISC"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_UA: + s = "UA"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DM: + s = "DM"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_FRMR: + s = "FRMR"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_XID: + s = "XID"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_TEST: + s = "TEST"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + default: + s = "(UNKNOWN)"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + } + return; + } + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + } else { + return; // if IFRAMES not set, don't print. + } + + switch (NbfHeader->Command) { + case NBF_CMD_ADD_GROUP_NAME_QUERY: + s = "ADD_GROUP_NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_QUERY: + s = "ADD_NAME_QUERY"; break; + + case NBF_CMD_NAME_IN_CONFLICT: + s = "NAME_IN_CONFLICT"; break; + + case NBF_CMD_STATUS_QUERY: + s = "STATUS_QUERY"; break; + + case NBF_CMD_TERMINATE_TRACE: + s = "TERMINATE_TRACE"; break; + + case NBF_CMD_DATAGRAM: + s = "DATAGRAM"; break; + + case NBF_CMD_DATAGRAM_BROADCAST: + s = "BROADCAST_DATAGRAM"; break; + + case NBF_CMD_NAME_QUERY: + s = "NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_RESPONSE: + s = "ADD_NAME_RESPONSE"; break; + + case NBF_CMD_NAME_RECOGNIZED: + s = "NAME_RECOGNIZED"; break; + + case NBF_CMD_STATUS_RESPONSE: + s = "STATUS_RESPONSE"; break; + + case NBF_CMD_TERMINATE_TRACE2: + s = "TERMINATE_TRACE2"; break; + + case NBF_CMD_DATA_ACK: + s = "DATA_ACK"; break; + + case NBF_CMD_DATA_FIRST_MIDDLE: + s = "DATA_FIRST_MIDDLE"; break; + + case NBF_CMD_DATA_ONLY_LAST: + s = "DATA_ONLY_LAST"; break; + + case NBF_CMD_SESSION_CONFIRM: + s = "SESSION_CONFIRM"; break; + + case NBF_CMD_SESSION_END: + s = "SESSION_END"; break; + + case NBF_CMD_SESSION_INITIALIZE: + s = "SESSION_INITIALIZE"; break; + + case NBF_CMD_NO_RECEIVE: + s = "NO_RECEIVE"; break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + s = "RECEIVE_OUTSTANDING"; break; + + case NBF_CMD_RECEIVE_CONTINUE: + s = "RECEIVE_CONTINUE"; break; + + case NBF_CMD_SESSION_ALIVE: + s = "SESSION_ALIVE"; break; + + default: + s = "<<<>>>"; + } /* switch */ + + if (HEADER_LENGTH(NbfHeader) != 14) { + e = "(LENGTH IN ERROR) "; + } else if (HEADER_SIGNATURE(NbfHeader) != NETBIOS_SIGNATURE) { + e = "(SIGNATURE IN ERROR) "; + } else { + e = ""; + } + + DbgPrint ("DLC: I-%s/%s, N(S)=%ld, N(R)=%ld %s", + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + ns, nr, e); + DbgPrint (s); + DbgPrint (" ( D1=%ld, D2=%ld, XC=%ld, RC=%ld )\n", + (ULONG)NbfHeader->Data1, + (ULONG)(NbfHeader->Data2Low+NbfHeader->Data2High*256), + TRANSMIT_CORR(NbfHeader), + RESPONSE_CORR(NbfHeader)); +} /* DisplayOneFrame */ + + +VOID +NbfDisplayUIFrame( + PTP_UI_FRAME OuterFrame + ) + +/*++ + +Routine Description: + + This routine is a temporary debugging aid that displays a UI frame + before it is sent by NbfSendUIFrame. This ensures that we have formatted + all our UI frames correctly. + +Arguments: + + RawFrame - Pointer to a connectionless frame to be sent. + +Return Value: + + none. + +--*/ + +{ + PCH s, e; + UCHAR ReceiverName [17]; + UCHAR SenderName [17]; + BOOLEAN PollFinal, Command; + PDLC_S_FRAME SFrame; + PDLC_U_FRAME UFrame; + USHORT i; + PDLC_FRAME DlcHeader; + PNBF_HDR_CONNECTIONLESS NbfHeader; + + // + + DlcHeader = (PDLC_FRAME)&(OuterFrame->Header[14]); + NbfHeader = (PNBF_HDR_CONNECTIONLESS)&(OuterFrame->Header[17]); + + if (DlcHeader->Byte1 != DLC_CMD_UI) { + + IF_NBFDBG (NBF_DEBUG_DLCFRAMES) { + } else { + return; // don't print this if DLCFRAMES is off. + } + + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + SFrame = (PDLC_S_FRAME)DlcHeader; // alias. + UFrame = (PDLC_U_FRAME)DlcHeader; // alias. + switch (DlcHeader->Byte1) { + case DLC_CMD_RR: + s = "RR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_RNR: + s = "RNR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_REJ: + s = "REJ"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_SABME: + s = "SABME"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DISC: + s = "DISC"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_UA: + s = "UA"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DM: + s = "DM"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_FRMR: + s = "FRMR"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_XID: + s = "XID"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_TEST: + s = "TEST"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + default: + s = "(UNKNOWN)"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + } + return; + } + + // + // We know that this is an I-frame, because the bottom bit of the + // first byte in the DLC header is cleared. Go ahead and print it + // as though it were a NetBIOS packet, which it should be. + // + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + } else { + return; // don't print this if IFRAMES is off. + } + + switch (NbfHeader->Command) { + case NBF_CMD_ADD_GROUP_NAME_QUERY: + s = "ADD_GROUP_NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_QUERY: + s = "ADD_NAME_QUERY"; break; + + case NBF_CMD_NAME_IN_CONFLICT: + s = "NAME_IN_CONFLICT"; break; + + case NBF_CMD_STATUS_QUERY: + s = "STATUS_QUERY"; break; + + case NBF_CMD_TERMINATE_TRACE: + s = "TERMINATE_TRACE"; break; + + case NBF_CMD_DATAGRAM: + s = "DATAGRAM"; break; + + case NBF_CMD_DATAGRAM_BROADCAST: + s = "BROADCAST_DATAGRAM"; break; + + case NBF_CMD_NAME_QUERY: + s = "NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_RESPONSE: + s = "ADD_NAME_RESPONSE"; break; + + case NBF_CMD_NAME_RECOGNIZED: + s = "NAME_RECOGNIZED"; break; + + case NBF_CMD_STATUS_RESPONSE: + s = "STATUS_RESPONSE"; break; + + case NBF_CMD_TERMINATE_TRACE2: + s = "TERMINATE_TRACE2"; break; + + case NBF_CMD_DATA_ACK: + s = "DATA_ACK"; break; + + case NBF_CMD_DATA_FIRST_MIDDLE: + s = "DATA_FIRST_MIDDLE"; break; + + case NBF_CMD_DATA_ONLY_LAST: + s = "DATA_ONLY_LAST"; break; + + case NBF_CMD_SESSION_CONFIRM: + s = "SESSION_CONFIRM"; break; + + case NBF_CMD_SESSION_END: + s = "SESSION_END"; break; + + case NBF_CMD_SESSION_INITIALIZE: + s = "SESSION_INITIALIZE"; break; + + case NBF_CMD_NO_RECEIVE: + s = "NO_RECEIVE"; break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + s = "RECEIVE_OUTSTANDING"; break; + + case NBF_CMD_RECEIVE_CONTINUE: + s = "RECEIVE_CONTINUE"; break; + + case NBF_CMD_SESSION_ALIVE: + s = "SESSION_ALIVE"; break; + + default: + s = "<<<>>>"; + } /* switch */ + + for (i=0; i<16; i++) { // copy NetBIOS names. + SenderName [i] = NbfHeader->SourceName [i]; + ReceiverName [i] = NbfHeader->DestinationName [i]; + } + SenderName [16] = 0; // install zero bytes. + ReceiverName [16] = 0; + + if (HEADER_LENGTH(NbfHeader) != 44) { + e = "(LENGTH IN ERROR) "; + } else if (HEADER_SIGNATURE(NbfHeader) != NETBIOS_SIGNATURE) { + e = "(SIGNATURE IN ERROR) "; + } else { + e = ""; + } + + DbgPrint ("[UI] %s", e); + DbgPrint (s); + DbgPrint (" ( D1=%ld, D2=%ld, XC=%ld, RC=%ld, ", + (ULONG)NbfHeader->Data1, + (ULONG)(NbfHeader->Data2Low+NbfHeader->Data2High*256), + TRANSMIT_CORR(NbfHeader), + RESPONSE_CORR(NbfHeader)); + DbgPrint ("'%s'->'%s' ) ---->\n", SenderName, ReceiverName); +} /* NbfDisplayUIFrame */ + + +VOID +NbfHexDumpLine( + PCHAR pch, + ULONG len, + PCHAR s, + PCHAR t + ) +/*++ + +Routine Description: + + This routine builds a line of text containing hex and printable characters. + +Arguments: + + IN pch - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + IN s - Supplies the start of the buffer to be loaded with the string + of hex characters. + IN t - Supplies the start of the buffer to be loaded with the string + of printable ascii characters. + + +Return Value: + + none. + +--*/ +{ + static UCHAR rghex[] = "0123456789ABCDEF"; + + UCHAR c; + UCHAR *hex, *asc; + + + hex = s; + asc = t; + + *(asc++) = '*'; + while (len--) { + c = *(pch++); + *(hex++) = rghex [c >> 4] ; + *(hex++) = rghex [c & 0x0F]; + *(hex++) = ' '; + *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c; + } + *(asc++) = '*'; + *asc = 0; + *hex = 0; + +} + + +VOID +NbfFormattedDump( + PCHAR far_p, + ULONG len + ) +/*++ + +Routine Description: + + This routine outputs a buffer in lines of text containing hex and + printable characters. + +Arguments: + + IN far_p - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + +Return Value: + + none. + +--*/ +{ + ULONG l; + char s[80], t[80]; + + while (len) { + l = len < 16 ? len : 16; + + DbgPrint ("\n%lx ", far_p); + NbfHexDumpLine (far_p, l, s, t); + DbgPrint ("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t); + + len -= l; + far_p += l; + } + DbgPrint ("\n"); +} + +#endif diff --git a/private/ntos/tdi/nbf/nbfdrvr.c b/private/ntos/tdi/nbf/nbfdrvr.c new file mode 100644 index 000000000..2f42766ff --- /dev/null +++ b/private/ntos/tdi/nbf/nbfdrvr.c @@ -0,0 +1,2493 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfdrvr.c + +Abstract: + + This module contains code which defines the NetBIOS Frames Protocol + transport provider's device object. + +Author: + + David Beaver (dbeaver) 2-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop +//#pragma warning(error:4101) // Unreferenced local variable + +// +// This is a list of all the device contexts that NBF owns, +// used while unloading. +// + +LIST_ENTRY NbfDeviceList = {0,0}; // initialized for real at runtime. + +#ifdef _PNP_POWER + +// +// Global variables this is a copy of the path in the registry for +// configuration data. +// + +UNICODE_STRING NbfRegistryPath; + +// +// We need the driver object to create device context structures. +// + +PDRIVER_OBJECT NbfDriverObject; + +#endif + + +#ifdef NBF_LOCKS // see spnlckdb.c + +extern KSPIN_LOCK NbfGlobalLock; + +#endif // def NBF_LOCKS + +// +// The debugging longword, containing a bitmask as defined in NBFCONST.H. +// If a bit is set, then debugging is turned on for that component. +// + +#if DBG + +ULONG NbfDebug = 0; +BOOLEAN NbfDisconnectDebug; + +NBF_SEND NbfSends[TRACK_TDI_LIMIT+1]; +LONG NbfSendsNext; + +NBF_SEND_COMPLETE NbfCompletedSends[TRACK_TDI_LIMIT+1]; +LONG NbfCompletedSendsNext; + +NBF_RECEIVE NbfReceives[TRACK_TDI_LIMIT+1]; +LONG NbfReceivesNext; + +NBF_RECEIVE_COMPLETE NbfCompletedReceives[TRACK_TDI_LIMIT+1]; +LONG NbfCompletedReceivesNext=0; + +PVOID * NbfConnectionTable; +PVOID * NbfRequestTable; +PVOID * NbfUiFrameTable; +PVOID * NbfSendPacketTable; +PVOID * NbfLinkTable; +PVOID * NbfAddressFileTable; +PVOID * NbfAddressTable; + + +LIST_ENTRY NbfGlobalRequestList; +LIST_ENTRY NbfGlobalLinkList; +LIST_ENTRY NbfGlobalConnectionList; +KSPIN_LOCK NbfGlobalInterlock; +KSPIN_LOCK NbfGlobalHistoryLock; + +PVOID +TtdiSend (); + +PVOID +TtdiReceive (); + +PVOID +TtdiServer (); + +KEVENT TdiSendEvent; +KEVENT TdiReceiveEvent; +KEVENT TdiServerEvent; + +#endif + +#if MAGIC + +BOOLEAN NbfEnableMagic = FALSE; // Controls sending of magic bullets. + +#endif // MAGIC + +// +// This prevents us from having a bss section +// + +ULONG _setjmpexused = 0; + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbfUnload( + IN PDRIVER_OBJECT DriverObject + ); + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +NbfDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfDeallocateResources( + IN PDEVICE_CONTEXT DeviceContext + ); + +#ifdef RASAUTODIAL +VOID +NbfAcdBind(); + +VOID +NbfAcdUnbind(); +#endif // RASAUTODIAL + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#ifdef _PNP_POWER +#pragma alloc_text(PAGE,NbfInitializeOneDeviceContext) +#endif +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the NetBIOS Frames Protocol + transport driver. It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of NBF's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + ULONG j; + UNICODE_STRING nameString; + NTSTATUS status; + UINT SuccessfulOpens; + + PCONFIG_DATA NbfConfig = NULL; + + // + + ASSERT (sizeof (SHORT) == 2); + +#ifdef MEMPRINT + MemPrintInitialize (); +#endif + +#ifdef NBF_LOCKS + KeInitializeSpinLock( &NbfGlobalLock ); +#endif + +#if DBG + InitializeListHead (&NbfGlobalRequestList); + InitializeListHead (&NbfGlobalLinkList); + InitializeListHead (&NbfGlobalConnectionList); + KeInitializeSpinLock (&NbfGlobalInterlock); + KeInitializeSpinLock (&NbfGlobalHistoryLock); +#endif + +#ifdef _PNP_POWER + + NbfRegistryPath = *RegistryPath; + NbfRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, + RegistryPath->MaximumLength, + ' FBN'); + + if (NbfRegistryPath.Buffer == NULL) { + PANIC(" Failed to allocate Registry Path!\n"); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory(NbfRegistryPath.Buffer, RegistryPath->Buffer, + RegistryPath->MaximumLength); + NbfDriverObject = DriverObject; + RtlInitUnicodeString( &nameString, NBF_NAME); + TdiInitialize(); + +#else // NOT _PNP_POWER + + // + // This allocates the CONFIG_DATA structure and returns + // it in NbfConfig. + // + + status = NbfConfigureTransport(RegistryPath, &NbfConfig); + + if (!NT_SUCCESS (status)) { + PANIC (" Failed to initialize transport, Nbf initialization failed.\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // make ourselves known to the NDIS wrapper. + // + + RtlInitUnicodeString( &nameString, NBF_DEVICE_NAME ); +#endif + + status = NbfRegisterProtocol (&nameString); + + if (!NT_SUCCESS (status)) { + +#ifdef _PNP_POWER + + // + // No configuration info read at startup when using PNP + // + + ExFreePool(NbfRegistryPath.Buffer); +#else + // + // Free up config info for non PNP mode operation. + // + + NbfFreeConfigurationInfo(NbfConfig); +#endif + PANIC ("NbfInitialize: RegisterProtocol failed!\n"); + + NbfWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbfDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbfDispatch; + + DriverObject->DriverUnload = NbfUnload; + + // + // Initialize the global list of devices. + // + + InitializeListHead (&NbfDeviceList); + +#ifndef _PNP_POWER + +#if DBG + + // + // Allocate the debugging tables. In the PNP case we don't need these + // until we are activated by ProtocolBindAdapter + // + + NbfConnectionTable = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, + sizeof(PVOID) * + (NbfConfig->InitConnections + 2 + + NbfConfig->InitRequests + 2 + + NbfConfig->InitUIFrames + 2 + + NbfConfig->InitPackets + 2 + + NbfConfig->InitLinks + 2 + + NbfConfig->InitAddressFiles + 2 + + NbfConfig->InitAddresses + 2), + ' FBN'); + + ASSERT (NbfConnectionTable); + + NbfRequestTable = NbfConnectionTable + (NbfConfig->InitConnections + 2); + NbfUiFrameTable = NbfRequestTable + (NbfConfig->InitRequests + 2); + NbfSendPacketTable = NbfUiFrameTable + (NbfConfig->InitUIFrames + 2); + NbfLinkTable = NbfSendPacketTable + (NbfConfig->InitPackets + 2); + NbfAddressFileTable = NbfLinkTable + (NbfConfig->InitLinks + 2); + NbfAddressTable = NbfAddressFileTable + (NbfConfig->InitAddressFiles + 2); +#endif + + + SuccessfulOpens = 0; + + for (j=0;jNumAdapters;j++ ) { + + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + SuccessfulOpens += NbfInitializeOneDeviceContext(&status, + DriverObject, + NbfConfig, j + ); + + } + + NbfFreeConfigurationInfo(NbfConfig); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + if (SuccessfulOpens > 0) + NbfAcdBind(); +#endif // RASAUTODIAL + + return ((SuccessfulOpens>0) ? STATUS_SUCCESS :STATUS_DEVICE_DOES_NOT_EXIST); +#else // _PNP_POWER + return(status); +#endif + +} + +VOID +NbfUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the NetBIOS Frames Protocol transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has NBF open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbfAcdUnbind(); +#endif // RASAUTODIAL + + // + // Walk the list of device contexts. + // + + while (!IsListEmpty (&NbfDeviceList)) { + + p = RemoveHeadList (&NbfDeviceList); + DeviceContext = CONTAINING_RECORD (p, DEVICE_CONTEXT, Linkage); + + DeviceContext->State = DEVICECONTEXT_STATE_STOPPING; + + // + // Remove all the storage associated with the device. + // + + NbfFreeResources (DeviceContext); + + // + // Free the packet pools, etc. and close the + // adapter. + // + + NbfCloseNdis (DeviceContext); + + // + // And remove the creation reference from the device + // context. + // + + NbfDereferenceDeviceContext ("Unload", DeviceContext, DCREF_CREATION); + + } + + // + // Finally, remove ourselves as an NDIS protocol. + // + + NbfDeregisterProtocol(); + + return; + +} + + +VOID +NbfFreeResources ( + IN PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine is called by NBF to clean up the data structures associated + with a given DeviceContext. When this routine exits, the DeviceContext + should be deleted as it no longer has any assocaited resources. + +Arguments: + + DeviceContext - Pointer to the DeviceContext we wish to clean up. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PTP_UI_FRAME uiFrame; + PTP_ADDRESS address; + PTP_CONNECTION connection; + PTP_REQUEST request; + PTP_LINK link; + PTP_ADDRESS_FILE addressFile; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; + + + // + // Stop the timers. + // + + NbfStopTimerSystem (DeviceContext); + + + // + // Clean up packet pool. + // + + while ( DeviceContext->PacketPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbfDeallocateSendPacket (DeviceContext, packet); + } + + // + // Clean up UI frame pool. + // + + while ( !IsListEmpty( &DeviceContext->UIFramePool ) ) { + p = RemoveHeadList( &DeviceContext->UIFramePool ); + uiFrame = CONTAINING_RECORD (p, TP_UI_FRAME, Linkage ); + + NbfDeallocateUIFrame (DeviceContext, uiFrame); + } + + // + // Clean up address pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressPool) ) { + p = RemoveHeadList (&DeviceContext->AddressPool); + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + NbfDeallocateAddress (DeviceContext, address); + } + + // + // Clean up address file pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressFilePool) ) { + p = RemoveHeadList (&DeviceContext->AddressFilePool); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + NbfDeallocateAddressFile (DeviceContext, addressFile); + } + + // + // Clean up connection pool. + // + + while ( !IsListEmpty (&DeviceContext->ConnectionPool) ) { + p = RemoveHeadList (&DeviceContext->ConnectionPool); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + NbfDeallocateConnection (DeviceContext, connection); + } + + // + // Clean up link pool. + // + + while ( !IsListEmpty (&DeviceContext->LinkPool) ) { + p = RemoveHeadList (&DeviceContext->LinkPool); + link = CONTAINING_RECORD (p, TP_LINK, Linkage); + + NbfDeallocateLink (DeviceContext, link); + } + + // + // Clean up request pool. + // + + while ( !IsListEmpty( &DeviceContext->RequestPool ) ) { + p = RemoveHeadList( &DeviceContext->RequestPool ); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage ); + + NbfDeallocateRequest (DeviceContext, request); + } + + // + // Clean up receive packet pool + // + + while ( DeviceContext->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&DeviceContext->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbfDeallocateReceivePacket (DeviceContext, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( DeviceContext->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbfDeallocateReceiveBuffer (DeviceContext, BufferTag); + } + + + return; + +} /* NbfFreeResources */ + + +NTSTATUS +NbfDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the NBF device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PDEVICE_CONTEXT DeviceContext; + + ENTER_NBF; + + // + // Check to see if NBF has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_DEVICE_CONTROL.\n"); + } + + Status = NbfDeviceControl (DeviceObject, Irp, IrpSp); + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: OTHER (DEFAULT).\n"); + } + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; + return Status; +} /* NbfDispatch */ + + +NTSTATUS +NbfDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the NBF device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PFILE_FULL_EA_INFORMATION openType; + USHORT i; + BOOLEAN found; + PTP_ADDRESS_FILE AddressFile; + PTP_CONNECTION Connection; + + ENTER_NBF; + + // + // Check to see if NBF has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CREATE.\n"); + } + + openType = + (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbfOpenAddress (DeviceObject, Irp, IrpSp); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbfOpenConnection (DeviceObject, Irp, IrpSp); + break; + } + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchOpenClose: IRP_MJ_CREATE on invalid type, type was: %s\n", + openType->EaName); + } + + } else { + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchOpenClose: IRP_MJ_CREATE on control channel!\n"); + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + IrpSp->FileObject->FsContext = (PVOID)(DeviceContext->ControlChannelIdentifier); + ++DeviceContext->ControlChannelIdentifier; + if (DeviceContext->ControlChannelIdentifier == 0) { + DeviceContext->ControlChannelIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + IrpSp->FileObject->FsContext2 = (PVOID)NBF_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CLOSE.\n"); + } + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + + // + // This creates a reference to AddressFile->Address + // which is removed by NbfCloseAddress. + // + + Status = NbfVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbfCloseAddress (DeviceObject, Irp, IrpSp); + } + + break; + + case TDI_CONNECTION_FILE: + + // + // This is a connection + // + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = NbfVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + + Status = NbfCloseConnection (DeviceObject, Irp, IrpSp); + NbfDereferenceConnection ("Temporary Use",Connection, CREF_BY_ID); + + } + + break; + + case NBF_FILE_TYPE_CONTROL: + + // + // this always succeeds + // + + Status = STATUS_SUCCESS; + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatch: IRP_MJ_CLOSE on unknown file type %lx.\n", + IrpSp->FileObject->FsContext2); + } + + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CLEANUP.\n"); + } + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + Status = NbfVerifyAddressObject(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbfStopAddressFile (AddressFile, AddressFile->Address); + NbfDereferenceAddress ("IRP_MJ_CLEANUP", AddressFile->Address, AREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = NbfVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + KeLowerIrql (oldirql); + Status = STATUS_SUCCESS; + NbfDereferenceConnection ("Temporary Use",Connection, CREF_BY_ID); + } + + break; + + case NBF_FILE_TYPE_CONTROL: + + NbfStopControlChannel( + (PDEVICE_CONTEXT)DeviceObject, + (USHORT)IrpSp->FileObject->FsContext + ); + + Status = STATUS_SUCCESS; + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatch: IRP_MJ_CLEANUP on unknown file type %lx.\n", + IrpSp->FileObject->FsContext2); + } + + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: OTHER (DEFAULT).\n"); + } + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; + return Status; +} /* NbfDispatchOpenClose */ + + +NTSTATUS +NbfDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Entered.\n"); + } + + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + +#if MAGIC + case IOCTL_TDI_MAGIC_BULLET: + + // + // Special: send the magic bullet (to trigger the Sniffer). + // + + NbfPrint1 ("NBF: Sending user MagicBullet on %lx\n", DeviceContext); + { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } + + if (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer != NULL) { + NbfPrint0 ("NBF: DbgBreakPoint after MagicBullet\n"); + DbgBreakPoint(); + } + + Status = STATUS_SUCCESS; + break; +#endif + +#if DBG + case IOCTL_TDI_SEND_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start send side test\n"); + } + + (VOID) KeSetEvent( &TdiSendEvent, 0, FALSE ); + + break; + + case IOCTL_TDI_RECEIVE_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start receive side test\n"); + } + + (VOID) KeSetEvent( &TdiReceiveEvent, 0, FALSE ); + + break; + + case IOCTL_TDI_SERVER_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start receive side test\n"); + } + + (VOID) KeSetEvent( &TdiServerEvent, 0, FALSE ); + + break; +#endif + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: invalid request type.\n"); + } + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + // NbfDispatchInternal expects to complete the IRP, + // so we change Status to PENDING so we don't. + // + + (VOID)NbfDispatchInternal (DeviceObject, Irp); + Status = STATUS_PENDING; + + } + } + + return Status; +} /* NbfDeviceControl */ + + +NTSTATUS +NbfDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PIO_STACK_LOCATION IrpSp; +#if DBG + KIRQL IrqlOnEnter = KeGetCurrentIrql(); +#endif + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfInternalDeviceControl: Entered.\n"); + } + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + { + PULONG Temp=(PULONG)&IrpSp->Parameters; + NbfPrint5 ("Got IrpSp %lx %lx %lx %lx %lx\n", Temp++, Temp++, + Temp++, Temp++, Temp++); + } + } + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->MinorFunction) { + + case TDI_ACCEPT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAccept request.\n"); + } + + Status = NbfTdiAccept (Irp); + break; + + case TDI_ACTION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAction request.\n"); + } + + Status = NbfTdiAction (DeviceContext, Irp); + break; + + case TDI_ASSOCIATE_ADDRESS: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAccept request.\n"); + } + + Status = NbfTdiAssociateAddress (Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiDisassociateAddress request.\n"); + } + + Status = NbfTdiDisassociateAddress (Irp); + break; + + case TDI_CONNECT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiConnect request\n"); + } + + Status = NbfTdiConnect (Irp); + + break; + + case TDI_DISCONNECT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiDisconnect request.\n"); + } + + Status = NbfTdiDisconnect (Irp); + break; + + case TDI_LISTEN: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiListen request.\n"); + } + + Status = NbfTdiListen (Irp); + break; + + case TDI_QUERY_INFORMATION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiQueryInformation request.\n"); + } + + Status = NbfTdiQueryInformation (DeviceContext, Irp); + break; + + case TDI_RECEIVE: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiReceive request.\n"); + } + + Status = NbfTdiReceive (Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiReceiveDatagram request.\n"); + } + + Status = NbfTdiReceiveDatagram (Irp); + break; + + case TDI_SEND: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSend request.\n"); + } + + Status = NbfTdiSend (Irp); + break; + + case TDI_SEND_DATAGRAM: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSendDatagram request.\n"); + } + + Status = NbfTdiSendDatagram (Irp); + break; + + case TDI_SET_EVENT_HANDLER: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSetEventHandler request.\n"); + } + + // + // Because this request will enable direct callouts from the + // transport provider at DISPATCH_LEVEL to a client-specified + // routine, this request is only valid in kernel mode, denying + // access to this request in user mode. + // + + Status = NbfTdiSetEventHandler (Irp); + break; + + case TDI_SET_INFORMATION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSetInformation request.\n"); + } + + Status = NbfTdiSetInformation (Irp); + break; + +#if DBG + case 0x7f: + + // + // Special: send the magic bullet (to trigger the Sniffer). + // + + NbfPrint1 ("NBF: Sending MagicBullet on %lx\n", DeviceContext); + { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } + + Status = STATUS_SUCCESS; + break; +#endif + + // + // Something we don't know about was submitted. + // + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchInternal: invalid request type %lx\n", + IrpSp->MinorFunction); + } + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchInternal: exiting, status: %lx\n",Status); + } + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; +#if DBG + ASSERT (KeGetCurrentIrql() == IrqlOnEnter); +#endif + + return Status; + +} /* NbfDispatchInternal */ + + +VOID +NbfWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN ULONG BytesNeeded, + IN ULONG ResourceId + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. It will handle event codes + RESOURCE_POOL, RESOURCE_LIMIT, and RESOURCE_SPECIFIC. + +Arguments: + + DeviceContext - Pointer to the device context. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + ResourceId - The resource ID of the allocated structure. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PWSTR SecondString; + ULONG SecondStringSize; + PUCHAR StringLoc; + WCHAR ResourceIdBuffer[3]; + WCHAR SizeBuffer[2]; + WCHAR SpecificMaxBuffer[11]; + ULONG SpecificMax; + INT i; + + switch (ErrorCode) { + + case EVENT_TRANSPORT_RESOURCE_POOL: + SecondString = NULL; + SecondStringSize = 0; + break; + + case EVENT_TRANSPORT_RESOURCE_LIMIT: + SecondString = SizeBuffer; + SecondStringSize = sizeof(SizeBuffer); + + switch (DeviceContext->MemoryLimit) { + case 100000: SizeBuffer[0] = L'1'; break; + case 250000: SizeBuffer[0] = L'2'; break; + case 0: SizeBuffer[0] = L'3'; break; + default: SizeBuffer[0] = L'0'; break; + } + SizeBuffer[1] = 0; + break; + + case EVENT_TRANSPORT_RESOURCE_SPECIFIC: + switch (ResourceId) { + case UI_FRAME_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize; break; + case PACKET_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize; break; + case RECEIVE_PACKET_RESOURCE_ID: SpecificMax = DeviceContext->ReceivePacketPoolSize; break; + case RECEIVE_BUFFER_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize+DeviceContext->ReceivePacketPoolSize; break; + case ADDRESS_RESOURCE_ID: SpecificMax = DeviceContext->MaxAddresses; break; + case ADDRESS_FILE_RESOURCE_ID: SpecificMax = DeviceContext->MaxAddressFiles; break; + case CONNECTION_RESOURCE_ID: SpecificMax = DeviceContext->MaxConnections; break; + case LINK_RESOURCE_ID: SpecificMax = DeviceContext->MaxLinks; break; + case REQUEST_RESOURCE_ID: SpecificMax = DeviceContext->MaxRequests; break; + } + + for (i=9; i>=0; i--) { + SpecificMaxBuffer[i] = (WCHAR)((SpecificMax % 10) + L'0'); + SpecificMax /= 10; + if (SpecificMax == 0) { + break; + } + } + SecondString = SpecificMaxBuffer + i; + SecondStringSize = sizeof(SpecificMaxBuffer) - (i * sizeof(WCHAR)); + SpecificMaxBuffer[10] = 0; + break; + + default: + ASSERT (FALSE); + break; + } + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + DeviceContext->DeviceNameLength + + sizeof(ResourceIdBuffer) + + SecondStringSize; + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the resource ID into a buffer. + // + + ResourceIdBuffer[1] = (WCHAR)((ResourceId % 10) + L'0'); + ResourceId /= 10; + ASSERT(ResourceId <= 9); + ResourceIdBuffer[0] = (WCHAR)((ResourceId % 10) + L'0'); + ResourceIdBuffer[2] = 0; + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 2 : 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + + RtlCopyMemory (StringLoc, ResourceIdBuffer, sizeof(ResourceIdBuffer)); + StringLoc += sizeof(ResourceIdBuffer); + + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteResourceErrorLog */ + + +VOID +NbfWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceContext - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PWSTR DriverName; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceContext->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)DeviceContext->DeviceNameLength; + } else { + DriverName = L"Nbf"; + EntrySize += 4 * sizeof(WCHAR); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceContext->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, 4 * sizeof(WCHAR)); + StringLoc += 4 * sizeof(WCHAR); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteGeneralErrorLog */ + + +VOID +NbfWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceContext - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + WCHAR OidBuffer[9]; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + DeviceContext->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + OidBuffer[8] = 0; + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteOidErrorLog */ + +ULONG +NbfInitializeOneDeviceContext( + OUT PNDIS_STATUS NdisStatus, + IN PDRIVER_OBJECT DriverObject, + IN PCONFIG_DATA NbfConfig, + IN INT AdapterIndex + ) +/*++ + +Routine Description: + + This routine creates and initializes one nbf device context. In order to + do this it must successfully open and bind to the adapter described by + nbfconfig->names[adapterindex]. + +Arguments: + + NdisStatus - The outputted status of the operations. + + DriverObject - the nbf driver object. + + NbfConfig - the transport configuration information from the registry. + + AdapterIndex - which adapter to bind with. + +Return Value: + + The number of successful binds. + +--*/ + +{ + ULONG i, j; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PTP_LINK Link; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PTP_UI_FRAME UIFrame; + PTP_PACKET Packet; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + NTSTATUS status; + UINT SuccessfulOpens; + UINT MaxUserData; + ULONG InitReceivePackets; + BOOLEAN UniProcessor; +#ifdef _PNP_POWER + PDEVICE_OBJECT DeviceObject; + UNICODE_STRING DeviceString; + UCHAR PermAddr[sizeof(TA_ADDRESS)+TDI_ADDRESS_LENGTH_NETBIOS]; + PTA_ADDRESS pAddress = (PTA_ADDRESS)PermAddr; + PTDI_ADDRESS_NETBIOS NetBIOSAddress = + (PTDI_ADDRESS_NETBIOS)pAddress->Address; + + pAddress->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + pAddress->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + NetBIOSAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; +#endif + // + // Determine if we are on a uniprocessor. + // + + if (*KeNumberProcessors == 1) { + UniProcessor = TRUE; + } else { + UniProcessor = FALSE; + } + + { + j = AdapterIndex; + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + + status = NbfCreateDeviceContext( + DriverObject, + &NbfConfig->Names[NbfConfig->DevicesOffset+j], + &DeviceContext + ); + + if (!NT_SUCCESS (status)) { + NbfWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_BINDING_FAILED, + 707, + status, + NbfConfig->Names[j].Buffer, + 0, + NULL); + *NdisStatus = status; + return(0); + } + + DeviceContext->UniProcessor = UniProcessor; + + // + // Initialize the timer and retry values (note that the link timeouts + // are converted from NT ticks to NBF ticks). These values may + // be modified by NbfInitializeNdis. + // + + DeviceContext->DefaultT1Timeout = NbfConfig->DefaultT1Timeout / SHORT_TIMER_DELTA; + DeviceContext->DefaultT2Timeout = NbfConfig->DefaultT2Timeout / SHORT_TIMER_DELTA; + DeviceContext->DefaultTiTimeout = NbfConfig->DefaultTiTimeout / LONG_TIMER_DELTA; + DeviceContext->LlcRetries = NbfConfig->LlcRetries; + DeviceContext->LlcMaxWindowSize = NbfConfig->LlcMaxWindowSize; + DeviceContext->MaxConsecutiveIFrames = (UCHAR)NbfConfig->MaximumIncomingFrames; + DeviceContext->NameQueryRetries = NbfConfig->NameQueryRetries; + DeviceContext->NameQueryTimeout = NbfConfig->NameQueryTimeout; + DeviceContext->AddNameQueryRetries = NbfConfig->AddNameQueryRetries; + DeviceContext->AddNameQueryTimeout = NbfConfig->AddNameQueryTimeout; + DeviceContext->GeneralRetries = NbfConfig->GeneralRetries; + DeviceContext->GeneralTimeout = NbfConfig->GeneralTimeout; + DeviceContext->MinimumSendWindowLimit = NbfConfig->MinimumSendWindowLimit; + + // + // Initialize our counter that records memory usage. + // + + DeviceContext->MemoryUsage = 0; + DeviceContext->MemoryLimit = NbfConfig->MaxMemoryUsage; + + DeviceContext->MaxRequests = NbfConfig->MaxRequests; + DeviceContext->MaxLinks = NbfConfig->MaxLinks; + DeviceContext->MaxConnections = NbfConfig->MaxConnections; + DeviceContext->MaxAddressFiles = NbfConfig->MaxAddressFiles; + DeviceContext->MaxAddresses = NbfConfig->MaxAddresses; + + // + // Now fire up NDIS so this adapter talks + // + + status = NbfInitializeNdis (DeviceContext, + NbfConfig, + j); + + if (!NT_SUCCESS (status)) { + + // + // Log an error if we were failed to + // open this adapter. + // + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + NbfConfig->Names[j].Buffer, + 0, + NULL); + + NbfDereferenceDeviceContext ("Initialize NDIS failed", DeviceContext, DCREF_CREATION); + *NdisStatus = status; + return(0); + + } + +#if 0 + DbgPrint("Opened %S as %S\n", &NbfConfig->Names[j], &nameString); +#endif + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint6 ("NbfInitialize: NDIS returned: %x %x %x %x %x %x as local address.\n", + DeviceContext->LocalAddress.Address[0], + DeviceContext->LocalAddress.Address[1], + DeviceContext->LocalAddress.Address[2], + DeviceContext->LocalAddress.Address[3], + DeviceContext->LocalAddress.Address[4], + DeviceContext->LocalAddress.Address[5]); + } + + // + // Initialize our provider information structure; since it + // doesn't change, we just keep it around and copy it to + // whoever requests it. + // + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + TRUE, + &MaxUserData); + + DeviceContext->Information.Version = 0x0100; + DeviceContext->Information.MaxSendSize = 0x1fffe; // 128k - 2 + DeviceContext->Information.MaxConnectionUserData = 0; + DeviceContext->Information.MaxDatagramSize = + MaxUserData - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + DeviceContext->Information.ServiceFlags = NBF_SERVICE_FLAGS; + if (DeviceContext->MacInfo.MediumAsync) { + DeviceContext->Information.ServiceFlags |= TDI_SERVICE_POINT_TO_POINT; + } + DeviceContext->Information.MinimumLookaheadData = + 240 - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + DeviceContext->Information.MaximumLookaheadData = + DeviceContext->MaxReceivePacketSize - (sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + DeviceContext->Information.NumberOfResources = NBF_TDI_RESOURCES; + KeQuerySystemTime (&DeviceContext->Information.StartTime); + + + // + // Allocate various structures we will need. + // + + ENTER_NBF; + + // + // The TP_UI_FRAME structure has a CHAR[1] field at the end + // which we expand upon to include all the headers needed; + // the size of the MAC header depends on what the adapter + // told us about its max header size. + // + + DeviceContext->UIFrameHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof(DLC_FRAME) + + sizeof(NBF_HDR_CONNECTIONLESS); + + DeviceContext->UIFrameLength = + FIELD_OFFSET(TP_UI_FRAME, Header[0]) + + DeviceContext->UIFrameHeaderLength; + + + // + // The TP_PACKET structure has a CHAR[1] field at the end + // which we expand upon to include all the headers needed; + // the size of the MAC header depends on what the adapter + // told us about its max header size. TP_PACKETs are used + // for connection-oriented frame as well as for + // control frames, but since DLC_I_FRAME and DLC_S_FRAME + // are the same size, the header is the same size. + // + + ASSERT (sizeof(DLC_I_FRAME) == sizeof(DLC_S_FRAME)); + + DeviceContext->PacketHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof(DLC_I_FRAME) + + sizeof(NBF_HDR_CONNECTION); + + DeviceContext->PacketLength = + FIELD_OFFSET(TP_PACKET, Header[0]) + + DeviceContext->PacketHeaderLength; + + + // + // The BUFFER_TAG structure has a CHAR[1] field at the end + // which we expand upong to include all the frame data. + // + + DeviceContext->ReceiveBufferLength = + DeviceContext->MaxReceivePacketSize + + FIELD_OFFSET(BUFFER_TAG, Buffer[0]); + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: pre-allocating requests.\n"); + } + for (i=0; iInitRequests; i++) { + + NbfAllocateRequest (DeviceContext, &Request); + + if (Request == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate requests.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); +#if DBG + NbfRequestTable[i+1] = (PVOID)Request; +#endif + } +#if DBG + NbfRequestTable[0] = (PVOID)NbfConfig->InitRequests; + NbfRequestTable[NbfConfig->InitRequests + 1] = (PVOID) + ((NBF_REQUEST_SIGNATURE << 16) | sizeof (TP_REQUEST)); + InitializeListHead (&NbfGlobalRequestList); +#endif + + DeviceContext->RequestInitAllocated = NbfConfig->InitRequests; + DeviceContext->RequestMaxAllocated = NbfConfig->MaxRequests; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d requests, %ld\n", NbfConfig->InitRequests, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating links.\n"); + } + for (i=0; iInitLinks; i++) { + + NbfAllocateLink (DeviceContext, &Link); + + if (Link == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate links.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->LinkPool, &Link->Linkage); +#if DBG + NbfLinkTable[i+1] = (PVOID)Link; +#endif + } +#if DBG + NbfLinkTable[0] = (PVOID)NbfConfig->InitLinks; + NbfLinkTable[NbfConfig->InitLinks+1] = (PVOID) + ((NBF_LINK_SIGNATURE << 16) | sizeof (TP_LINK)); +#endif + + DeviceContext->LinkInitAllocated = NbfConfig->InitLinks; + DeviceContext->LinkMaxAllocated = NbfConfig->MaxLinks; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d links, %ld\n", NbfConfig->InitLinks, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating connections.\n"); + } + for (i=0; iInitConnections; i++) { + + NbfAllocateConnection (DeviceContext, &Connection); + + if (Connection == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate connections.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->ConnectionPool, &Connection->LinkList); +#if DBG + NbfConnectionTable[i+1] = (PVOID)Connection; +#endif + } +#if DBG + NbfConnectionTable[0] = (PVOID)NbfConfig->InitConnections; + NbfConnectionTable[NbfConfig->InitConnections+1] = (PVOID) + ((NBF_CONNECTION_SIGNATURE << 16) | sizeof (TP_CONNECTION)); +#endif + + DeviceContext->ConnectionInitAllocated = NbfConfig->InitConnections; + DeviceContext->ConnectionMaxAllocated = NbfConfig->MaxConnections; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d connections, %ld\n", NbfConfig->InitConnections, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating AddressFiles.\n"); + } + for (i=0; iInitAddressFiles; i++) { + + NbfAllocateAddressFile (DeviceContext, &AddressFile); + + if (AddressFile == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate Address Files.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); +#if DBG + NbfAddressFileTable[i+1] = (PVOID)AddressFile; +#endif + } +#if DBG + NbfAddressFileTable[0] = (PVOID)NbfConfig->InitAddressFiles; + NbfAddressFileTable[NbfConfig->InitAddressFiles + 1] = (PVOID) + ((NBF_ADDRESSFILE_SIGNATURE << 16) | + sizeof (TP_ADDRESS_FILE)); +#endif + + DeviceContext->AddressFileInitAllocated = NbfConfig->InitAddressFiles; + DeviceContext->AddressFileMaxAllocated = NbfConfig->MaxAddressFiles; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d address files, %ld\n", NbfConfig->InitAddressFiles, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating addresses.\n"); + } + for (i=0; iInitAddresses; i++) { + + NbfAllocateAddress (DeviceContext, &Address); + if (Address == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate addresses.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); +#if DBG + NbfAddressTable[i+1] = (PVOID)Address; +#endif + } +#if DBG + NbfAddressTable[0] = (PVOID)NbfConfig->InitAddresses; + NbfAddressTable[NbfConfig->InitAddresses + 1] = (PVOID) + ((NBF_ADDRESS_SIGNATURE << 16) | sizeof (TP_ADDRESS)); +#endif + + DeviceContext->AddressInitAllocated = NbfConfig->InitAddresses; + DeviceContext->AddressMaxAllocated = NbfConfig->MaxAddresses; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d addresses, %ld\n", NbfConfig->InitAddresses, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating UI frames.\n"); + } + + for (i=0; iInitUIFrames; i++) { + + NbfAllocateUIFrame (DeviceContext, &UIFrame); + + if (UIFrame == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate UI frames.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&(DeviceContext->UIFramePool), &UIFrame->Linkage); +#if DBG + NbfUiFrameTable[i+1] = UIFrame; +#endif + } +#if DBG + NbfUiFrameTable[0] = (PVOID)NbfConfig->InitUIFrames; +#endif + + DeviceContext->UIFrameInitAllocated = NbfConfig->InitUIFrames; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d UI frames, %ld\n", NbfConfig->InitUIFrames, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating I frames.\n"); + NbfPrint1 ("NBFDRVR: Packet pool header: %lx\n",&DeviceContext->PacketPool); + } + + for (i=0; iInitPackets; i++) { + + NbfAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->PacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); +#if DBG + NbfSendPacketTable[i+1] = Packet; +#endif + } +#if DBG + NbfSendPacketTable[0] = (PVOID)NbfConfig->InitPackets; + NbfSendPacketTable[NbfConfig->InitPackets+1] = (PVOID) + ((NBF_PACKET_SIGNATURE << 16) | sizeof (TP_PACKET)); +#endif + + DeviceContext->PacketInitAllocated = NbfConfig->InitPackets; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d send packets, %ld\n", NbfConfig->InitPackets, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating RR frames.\n"); + NbfPrint1 ("NBFDRVR: Packet pool header: %lx\n",&DeviceContext->RrPacketPool); + } + + for (i=0; i<10; i++) { + + NbfAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + Packet->Action = PACKET_ACTION_RR; + PushEntryList (&DeviceContext->RrPacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); + } + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d send packets, %ld\n", 10, DeviceContext->MemoryUsage); + } + + + // Allocate receive Ndis packets + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating Ndis Receive packets.\n"); + } + if (DeviceContext->MacInfo.SingleReceive) { + InitReceivePackets = 2; + } else { + InitReceivePackets = NbfConfig->InitReceivePackets; + } + for (i=0; iProtocolReserved; + PushEntryList (&DeviceContext->ReceivePacketPool, &ReceiveTag->Linkage); + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + PNDIS_BUFFER NdisBuffer; + NdisQueryPacket(NdisPacket, NULL, NULL, &NdisBuffer, NULL); + NbfPrint2 ("NbfInitialize: Created NDIS Pkt: %x Buffer: %x\n", + NdisPacket, NdisBuffer); + } + } + + DeviceContext->ReceivePacketInitAllocated = InitReceivePackets; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d receive packets, %ld\n", InitReceivePackets, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating Ndis Receive buffers.\n"); + } + + for (i=0; iInitReceiveBuffers; i++) { + + NbfAllocateReceiveBuffer (DeviceContext, &BufferTag); + + if (BufferTag == NULL) { + PANIC ("NbfInitialize: Unable to allocate receive packet.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->ReceiveBufferPool, (PSINGLE_LIST_ENTRY)&BufferTag->Linkage); + + } + + DeviceContext->ReceiveBufferInitAllocated = NbfConfig->InitReceiveBuffers; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d receive buffers, %ld\n", NbfConfig->InitReceiveBuffers, DeviceContext->MemoryUsage); + } + + DeviceContext->State = DEVICECONTEXT_STATE_OPEN; + + // + // Start the link-level timers running. + // + + NbfInitializeTimerSystem (DeviceContext); + + + // + // Now link the device into the global list. + // + + InsertTailList (&NbfDeviceList, &DeviceContext->Linkage); + + ++SuccessfulOpens; + +#ifdef _PNP_POWER + DeviceObject = (PDEVICE_OBJECT) DeviceContext; + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + RtlInitUnicodeString(&DeviceString, DeviceContext->DeviceName); + status = TdiRegisterDeviceObject(&DeviceString, + &DeviceContext->TdiDeviceHandle); + + if (!NT_SUCCESS (status)) { + --SuccessfulOpens; + RemoveEntryList(&DeviceContext->Linkage); + goto cleanup; + } + + RtlCopyMemory(NetBIOSAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, 16); + + + status = TdiRegisterNetAddress(pAddress, + &DeviceContext->ReservedAddressHandle); + + if (!NT_SUCCESS (status)) { + --SuccessfulOpens; + RemoveEntryList(&DeviceContext->Linkage); + goto cleanup; + } +#endif + + LEAVE_NBF; + *NdisStatus = NDIS_STATUS_SUCCESS; + + return(1);; + +cleanup: + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 501, + DeviceContext->MemoryUsage, + 0); + + // + // Cleanup whatever device context we were initializing + // when we failed. + // + *NdisStatus = status; + ASSERT(status != STATUS_SUCCESS); + + NbfFreeResources (DeviceContext); + NbfCloseNdis (DeviceContext); + NbfDereferenceDeviceContext ("Load failed", DeviceContext, DCREF_CREATION); + + + LEAVE_NBF; + } + return(0); +} diff --git a/private/ntos/tdi/nbf/nbfhdrs.h b/private/ntos/tdi/nbf/nbfhdrs.h new file mode 100644 index 000000000..9dfa5966f --- /dev/null +++ b/private/ntos/tdi/nbf/nbfhdrs.h @@ -0,0 +1,257 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbfhdrs.h + +Abstract: + + This module defines private structure definitions describing the layout + of the NetBIOS Frames Protocol headers for the NT NBF transport + provider. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + remove pc586 and PDI specific code; add NDIS support + +--*/ + +#ifndef _NBFHDRS_ +#define _NBFHDRS_ + +// +// Pack these headers, as they are sent fully packed on the network. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Off(Align_members) +#else +#pragma pack(1) +#endif // def __STDC__ + +#endif // def PACKING + +#define NETBIOS_SIGNATURE_1 0xef // signature in NetBIOS frames. +#define NETBIOS_SIGNATURE_0 0xff // 1st byte. +#define NETBIOS_SIGNATURE 0xefff + +// +// NetBIOS Frames Protocol Command Codes. +// + +#define NBF_CMD_ADD_GROUP_NAME_QUERY 0x00 +#define NBF_CMD_ADD_NAME_QUERY 0x01 +#define NBF_CMD_NAME_IN_CONFLICT 0x02 +#define NBF_CMD_STATUS_QUERY 0x03 +#define NBF_CMD_TERMINATE_TRACE 0x07 +#define NBF_CMD_DATAGRAM 0x08 +#define NBF_CMD_DATAGRAM_BROADCAST 0x09 +#define NBF_CMD_NAME_QUERY 0x0a +#define NBF_CMD_ADD_NAME_RESPONSE 0x0d +#define NBF_CMD_NAME_RECOGNIZED 0x0e +#define NBF_CMD_STATUS_RESPONSE 0x0f +#define NBF_CMD_TERMINATE_TRACE2 0x13 +#define NBF_CMD_DATA_ACK 0x14 +#define NBF_CMD_DATA_FIRST_MIDDLE 0x15 +#define NBF_CMD_DATA_ONLY_LAST 0x16 +#define NBF_CMD_SESSION_CONFIRM 0x17 +#define NBF_CMD_SESSION_END 0x18 +#define NBF_CMD_SESSION_INITIALIZE 0x19 +#define NBF_CMD_NO_RECEIVE 0x1a +#define NBF_CMD_RECEIVE_OUTSTANDING 0x1b +#define NBF_CMD_RECEIVE_CONTINUE 0x1c +#define NBF_CMD_SESSION_ALIVE 0x1f + +// +// NBF Transport Layer Header. +// + +typedef struct _NBF_HDR_GENERIC { + USHORT Length; // Length of this header in bytes. + UCHAR Signature [2]; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + USHORT Data2; // optional parameter. + USHORT TransmitCorrelator; // transmit correlator parameter. + USHORT ResponseCorrelator; // response correlator parameter. +} NBF_HDR_GENERIC; +typedef NBF_HDR_GENERIC UNALIGNED *PNBF_HDR_GENERIC; + +typedef struct _NBF_HDR_CONNECTION { + USHORT Length; // length of the header in bytes (14). + USHORT Signature; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + UCHAR Data2Low, Data2High; // Intel-formatted DW parameter. + USHORT TransmitCorrelator; // Intel-formatted DW param. (transmit correlator). + USHORT ResponseCorrelator; // Intel-formatted DW param. (response correlator). + UCHAR DestinationSessionNumber; // connection identifier of packet receiver. + UCHAR SourceSessionNumber; // connection identifier of packet sender. +} NBF_HDR_CONNECTION; +typedef NBF_HDR_CONNECTION UNALIGNED *PNBF_HDR_CONNECTION; + +typedef struct _NBF_HDR_CONNECTIONLESS { + USHORT Length; // length of the header in bytes (44). + USHORT Signature; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + UCHAR Data2Low, Data2High; // Intel-formatted DW parameter. + USHORT TransmitCorrelator; // Intel-formatted DW param. (transmit correlator). + USHORT ResponseCorrelator; // Intel-formatted DW param. (response correlator). + UCHAR DestinationName [NETBIOS_NAME_LENGTH]; // name of packet receiver. + UCHAR SourceName [NETBIOS_NAME_LENGTH]; // name of packet sender. +} NBF_HDR_CONNECTIONLESS; +typedef NBF_HDR_CONNECTIONLESS UNALIGNED *PNBF_HDR_CONNECTIONLESS; + +// +// These macros are used to retrieve the transmit and response +// correlators from an NBF_HDR_CONNECTION(LESS). The first two +// are general, the second two are used when the correlators +// are known to be WORD aligned. +// + +#define TRANSMIT_CORR(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->TransmitCorrelator)) +#define RESPONSE_CORR(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->ResponseCorrelator)) + +#define TRANSMIT_CORR_A(_Hdr) ((_Hdr)->TransmitCorrelator) +#define RESPONSE_CORR_A(_Hdr) ((_Hdr)->ResponseCorrelator) + +#define HEADER_LENGTH(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->Length)) +#define HEADER_SIGNATURE(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->Signature)) + +#define HEADER_LENGTH_A(_Hdr) ((_Hdr)->Length) +#define HEADER_SIGNATURE_A(_Hdr) ((_Hdr)->Signature) + +typedef union _NBF_HDR { + NBF_HDR_GENERIC Generic; + NBF_HDR_CONNECTION ConnectionOrientedFrame; + NBF_HDR_CONNECTIONLESS ConnectionlessFrame; +} NBF_HDR; +typedef NBF_HDR UNALIGNED *PNBF_HDR; + +// +// The following structures define I-frame, U-frame, and S-frame DLC headers. +// + +#define DLC_SSAP_RESPONSE 0x0001 // if (ssap & DLC_SSAP_RESP), it's a response. +#define DLC_SSAP_GLOBAL 0x00ff // the global SAP. +#define DLC_SSAP_NULL 0x0000 // the null SAP. +#define DLC_SSAP_MASK 0x00fe // mask to wipe out the response bit. +#define DLC_DSAP_MASK 0x00fe // mask to wipe out the group SAP bit. + +#define DLC_CMD_RR 0x01 // command code for RR. +#define DLC_CMD_RNR 0x05 // command code for RNR. +#define DLC_CMD_REJ 0x09 // command code for REJ. + +#define DLC_CMD_SABME 0x6f // command code for SABME. +#define DLC_CMD_DISC 0x43 // command code for DISC. +#define DLC_CMD_UA 0x63 // command code for UA. +#define DLC_CMD_DM 0x0f // command code for DM. +#define DLC_CMD_FRMR 0x87 // command code for FRMR. +#define DLC_CMD_UI 0x03 // command code for UI. +#define DLC_CMD_XID 0xaf // command code for XID. +#define DLC_CMD_TEST 0xe3 // command code for TEST. + +typedef struct _DLC_XID_INFORMATION { + UCHAR FormatId; // format of this XID frame. + UCHAR Info1; // first information byte. + UCHAR Info2; // second information byte. +} DLC_XID_INFORMATION; +typedef DLC_XID_INFORMATION UNALIGNED *PDLC_XID_INFORMATION; + +typedef struct _DLC_TEST_INFORMATION { + UCHAR Buffer [10]; // this buffer is actually arbitrarily large. +} DLC_TEST_INFORMATION; +typedef DLC_TEST_INFORMATION UNALIGNED *PDLC_TEST_INFORMATION; + +typedef struct _DLC_FRMR_INFORMATION { + UCHAR Command; // format: mmmpmm11, m=modifiers, p=poll/final. + UCHAR Ctrl; // control field of rejected frame. + UCHAR Vs; // our next send when error was detected. + UCHAR Vr; // our next receive when error was detected. + UCHAR Reason; // reason for sending FRMR: 000VZYXW. +} DLC_FRMR_INFORMATION; +typedef DLC_FRMR_INFORMATION UNALIGNED *PDLC_FRMR_INFORMATION; + +typedef struct _DLC_U_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Command; // command code. + union { // information field for FRMR, TEST, XID. + DLC_XID_INFORMATION XidInfo; // XID information. + DLC_TEST_INFORMATION TestInfo; // TEST information. + DLC_FRMR_INFORMATION FrmrInfo; // FRMR information. + NBF_HDR_CONNECTIONLESS NbfHeader; // UI frame contains NetBIOS header. + } Information; +} DLC_U_FRAME; +typedef DLC_U_FRAME UNALIGNED *PDLC_U_FRAME; + +#define DLC_U_INDICATOR 0x03 // (cmd & DLC_U_IND) == DLC_U_IND --> U-frame. +#define DLC_U_PF 0x10 // (cmd & DLC_U_PF) -> poll/final set. + +typedef struct _DLC_S_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Command; // RR, RNR, REJ command code. + UCHAR RcvSeq; // receive seq #, bottom bit is poll/final. +} DLC_S_FRAME; +typedef DLC_S_FRAME UNALIGNED *PDLC_S_FRAME; + +#define DLC_S_PF 0x01 // (rcvseq & DLC_S_PF) means poll/final set. + +typedef struct _DLC_I_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR SendSeq; // send sequence number, bottom bit 0. + UCHAR RcvSeq; // rcv sequence number, bottom bit p/f. +} DLC_I_FRAME; +typedef DLC_I_FRAME UNALIGNED *PDLC_I_FRAME; + +#define DLC_I_PF 0x01 // (rcvseq & DLC_I_PF) means poll/final set. +#define DLC_I_INDICATOR 0x01 // !(sndseq & DLC_I_INDICATOR) indicates I-frame. + +typedef struct _DLC_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Byte1; // command byte. +} DLC_FRAME; +typedef DLC_FRAME UNALIGNED *PDLC_FRAME; + + +// +// This macro builds a DLC UI-frame header. +// + +#define NbfBuildUIFrameHeader(_Header) \ +{ \ + PDLC_FRAME DlcHeader = (PDLC_FRAME)(_Header); \ + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; \ + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; \ + DlcHeader->Byte1 = DLC_CMD_UI; \ +} + + +// +// Resume previous structure packing method. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Pop(Align_members) +#else +#pragma pack() +#endif // def __STDC__ + +#endif // def PACKING + +#endif // def _NBFHDRS_ diff --git a/private/ntos/tdi/nbf/nbfmac.c b/private/ntos/tdi/nbf/nbfmac.c new file mode 100644 index 000000000..bbc418c19 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfmac.c @@ -0,0 +1,417 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfmac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the NBF transport. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR SingleRouteSourceRouting[] = { 0xc2, 0x70 }; +UCHAR GeneralRouteSourceRouting[] = { 0x82, 0x70 }; +ULONG DefaultSourceRoutingLength = 2; + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,MacInitializeMacInfo) +#pragma alloc_text(PAGE,MacSetNetBIOSMulticast) +#endif + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + IN BOOLEAN UseDix, + OUT PNBF_NDIS_IDENTIFICATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + UseDix - TRUE if we should use DIX encoding on 802.3. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->DestinationOffset = 0; + MacInfo->SourceOffset = 6; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + if (UseDix) { + MacInfo->TransferDataOffset = 3; + MacInfo->MaxHeaderLength = 17; + MacInfo->MediumType = NdisMediumDix; + } else { + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + } + MacInfo->MediumAsync = FALSE; + break; + case NdisMedium802_5: + MacInfo->DestinationOffset = 2; + MacInfo->SourceOffset = 8; + MacInfo->SourceRouting = TRUE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 32; + MacInfo->MediumType = NdisMedium802_5; + MacInfo->MediumAsync = FALSE; + break; + case NdisMediumFddi: + MacInfo->DestinationOffset = 1; + MacInfo->SourceOffset = 7; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + MacInfo->MediumAsync = FALSE; + break; + case NdisMediumWan: + MacInfo->DestinationOffset = 0; + MacInfo->SourceOffset = 6; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + MacInfo->MediumAsync = TRUE; + break; + default: + ASSERT(FALSE); + } +} + +VOID +MacConstructHeader ( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ) + +/*++ + +Routine Description: + + This routine is called to construct the Mac header for the particular + network type we're talking to. + +Arguments: + + MacInfo - Describes the mac we wish to build a header for. + + Buffer - Where to build the header. + + DestinationAddress - the address this packet is to be sent to. + + SourceAddress - Our address. Passing it in as a parameter allows us to play + games with source if we need to. + + PacketLength - The length of this packet. Note that this does not + includes the Mac header. + + SourceRouting - Optional source routing information. + + SourceRoutingLength - The length of SourceRouting. + + HeaderLength - Returns the length of the constructed header. + +Return Value: + + None. + +--*/ +{ + + // + // Note network order of bytes. + // + + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + *(ULONG UNALIGNED *)&Buffer[6] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[10] = SourceAddress[4]; + Buffer[11] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[0] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[4] = DestinationAddress[4]; + Buffer[5] = DestinationAddress[5]; + + Buffer[12] = (UCHAR)(PacketLength >> 8); + Buffer[13] = (UCHAR)PacketLength; + + *HeaderLength = 14; + + break; + + case NdisMediumDix: + + *(ULONG UNALIGNED *)&Buffer[6] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[10] = SourceAddress[4]; + Buffer[11] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[0] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[4] = DestinationAddress[4]; + Buffer[5] = DestinationAddress[5]; + + Buffer[12] = 0x80; + Buffer[13] = 0xd5; + + Buffer[14] = (UCHAR)(PacketLength >> 8); + Buffer[15] = (UCHAR)PacketLength; + + Buffer[16] = 0x00; + *HeaderLength = 17; + + break; + + case NdisMedium802_5: + + Buffer[0] = TR_HEADER_BYTE_0; + Buffer[1] = TR_HEADER_BYTE_1; + + ASSERT (TR_ADDRESS_LENGTH == 6); + + *(ULONG UNALIGNED *)&Buffer[8] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[12] = SourceAddress[4]; + Buffer[13] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[2] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[6] = DestinationAddress[4]; + Buffer[7] = DestinationAddress[5]; + + *HeaderLength = 14; + if (SourceRouting != NULL) { + RtlCopyMemory (&Buffer[14], SourceRouting, SourceRoutingLength); + Buffer[8] |= 0x80; // add SR bit in source address + *HeaderLength = 14 + SourceRoutingLength; + } + + break; + + case NdisMediumFddi: + + Buffer[0] = FDDI_HEADER_BYTE; + + *(ULONG UNALIGNED *)&Buffer[7] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[11] = SourceAddress[4]; + Buffer[12] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[1] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[5] = DestinationAddress[4]; + Buffer[6] = DestinationAddress[5]; + + *HeaderLength = 13; + + break; + + default: + PANIC ("MacConstructHeader: PANIC! called with unsupported Mac type.\n"); + } +} + + +VOID +MacReturnMaxDataSize( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + IN BOOLEAN AssumeWorstCase, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all LLC and NBF + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + AssumeWorstCase - TRUE if we should be pessimistic. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMediumDix: + + // + // For DIX, we have the 14-byte MAC header plus + // the three-byte DIX header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 17; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst if told to. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & 0x70) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + + if (!AssumeWorstCase) { + *MaxFrameSize = DeviceMaxFrameSize - 16; + } else if (DeviceMaxFrameSize < (544+sizeof(DLC_FRAME)+sizeof(NBF_HDR_CONNECTIONLESS))) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 512 + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + } + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + } +} + + + +VOID +MacSetNetBIOSMulticast ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine sets the NetBIOS broadcast address into a buffer provided + by the user. + +Arguments: + + Type the Mac Medium type. + + Buffer the buffer to put the multicast address in. + + +Return Value: + + none. + +--*/ +{ + switch (Type) { + case NdisMedium802_3: + case NdisMediumDix: + Buffer[0] = 0x03; + Buffer[ETHERNET_ADDRESS_LENGTH-1] = 0x01; + break; + + case NdisMedium802_5: + Buffer[0] = 0xc0; + Buffer[TR_ADDRESS_LENGTH-1] = 0x80; + break; + + case NdisMediumFddi: + Buffer[0] = 0x03; + Buffer[FDDI_ADDRESS_LENGTH-1] = 0x01; + break; + + default: + PANIC ("MacSetNetBIOSAddress: PANIC! called with unsupported Mac type.\n"); + } +} diff --git a/private/ntos/tdi/nbf/nbfmac.h b/private/ntos/tdi/nbf/nbfmac.h new file mode 100644 index 000000000..8710f1d20 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfmac.h @@ -0,0 +1,766 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + mac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Author: + + David Beaver (dbeaver) 02-Oct-1990 + +Revision History: + +--*/ + +#ifndef _MAC_ +#define _MAC_ + +// +// MAC-specific definitions, some of which get used below +// + +#define MAX_MAC_HEADER_LENGTH 32 +#define MAX_SOURCE_ROUTING 18 +#define MAX_DEFAULT_SR 2 + +#define ETHERNET_ADDRESS_LENGTH 6 +#define ETHERNET_PACKET_LENGTH 1514 // max size of an ethernet packet +#define ETHERNET_HEADER_LENGTH 14 // size of the ethernet MAC header +#define ETHERNET_DATA_LENGTH_OFFSET 12 +#define ETHERNET_DESTINATION_OFFSET 0 +#define ETHERNET_SOURCE_OFFSET 6 + +#define TR_ADDRESS_LENGTH 6 +#define TR_ADDRESS_OFFSET 2 +#define TR_SPECIFIC_OFFSET 0 +#define TR_PACKET_LENGTH 1514 // max size of a TR packet //BUGBUG +#define TR_HEADER_LENGTH 36 // size of the MAC header w/o source routing +#define TR_DATA_LENGTH_OFFSET 0 +#define TR_DESTINATION_OFFSET 2 +#define TR_SOURCE_OFFSET 8 +#define TR_ROUTING_OFFSET 14 // starts at the 14th byte +#define TR_GR_BCAST_LENGTH 2 +#define TR_GR_BROADCAST 0xC270 // what a general route b'cast looks like +#define TR_ROUTING_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit + +#define TR_PREAMBLE_AC 0x10 // how would these be specified? +#define TR_PREAMBLE_FC 0x40 + +#define TR_HEADER_BYTE_0 0x10 +#define TR_HEADER_BYTE_1 0x40 + +#define FDDI_ADDRESS_LENGTH 6 +#define FDDI_HEADER_BYTE 0x57 + + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the nbfmac routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _NBF_NDIS_IDENTIFICATION { + NDIS_MEDIUM MediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + BOOLEAN QueryWithoutSourceRouting; + BOOLEAN AllRoutesNameRecognized; + ULONG DestinationOffset; + ULONG SourceOffset; + ULONG AddressLength; + ULONG TransferDataOffset; + ULONG MaxHeaderLength; + BOOLEAN CopyLookahead; + BOOLEAN ReceiveSerialized; + BOOLEAN TransferSynchronous; + BOOLEAN SingleReceive; +} NBF_NDIS_IDENTIFICATION, *PNBF_NDIS_IDENTIFICATION; + + + +VOID +MacConstructHeader( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ); + +VOID +MacReturnMaxDataSize( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + IN BOOLEAN AssumeWorstCase, + OUT PUINT MaxFrameSize + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + IN BOOLEAN UseDix, + OUT PNBF_NDIS_IDENTIFICATION MacInfo + ); + + +extern UCHAR SingleRouteSourceRouting[]; +extern UCHAR GeneralRouteSourceRouting[]; +extern ULONG DefaultSourceRoutingLength; + + +//++ +// +// VOID +// MacReturnDestinationAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * DestinationAddress +// ); +// +// Routine Description: +// +// Returns the a pointer to the destination address in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// DestinationAddress - Returns the start of the destination address. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnDestinationAddress(_MacInfo, _Packet, _DestinationAddress) \ + *(_DestinationAddress) = ((PUCHAR)(_Packet)) + (_MacInfo)->DestinationOffset + + +//++ +// +// VOID +// MacReturnSourceAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PHARDWARE_ADDRESS SourceAddressBuffer, +// OUT PHARDWARE_ADDRESS * SourceAddress, +// OUT BOOLEAN * Multicast OPTIONAL +// ); +// +// Routine Description: +// +// Copies the source address in the packet into SourceAddress. +// NOTE THAT IT MAY COPY THE DATA, UNLIKE ReturnDestinationAddress +// AND ReturnSourceRouting. Optionally, indicates whether the +// destination address is a multicast address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceAddressBuffer - A buffer to hold the source address, +// if needed. +// +// SourceAddress - Returns a pointer to the source address. +// +// Multicast - Optional pointer to a BOOLEAN to receive indication +// of whether the destination was a multicast address. +// +// Return Value: +// +// None. +// +//-- + +// +// NOTE: The default case below handles Ethernet and FDDI. +// + +#define MacReturnSourceAddress(_MacInfo, _Packet, _SourceAddressBuffer, \ + _SourceAddress, _Multicast) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + PUCHAR SrcBuffer = (PUCHAR)(_SourceAddressBuffer); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + if (ARGUMENT_PRESENT(_Multicast)) { \ + *(PBOOLEAN)(_Multicast) = TmpPacket[2] & 0x80; \ + } \ + if (TmpPacket[8] & 0x80) { \ + *(PULONG)SrcBuffer = *(ULONG UNALIGNED *)(&TmpPacket[8]) & ~0x80;\ + SrcBuffer[4] = TmpPacket[12]; \ + SrcBuffer[5] = TmpPacket[13]; \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)SrcBuffer; \ + } else { \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + 8); \ + } \ + break; \ + default: \ + if (ARGUMENT_PRESENT(_Multicast)) { \ + *(PBOOLEAN)(_Multicast) = TmpPacket[0] & 0x01; \ + } \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + \ + (_MacInfo)->SourceOffset); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnSourceRouting( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * SourceRouting +// OUT PUINT SourceRoutingLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the source routing info in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceRouting - Returns the start of the source routing information, +// or NULL if none is present. +// +// SourceRoutingLength - Returns the length of the source routing +// information. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSourceRouting(_MacInfo, _Packet, _SourceRouting, _SourceRoutingLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + *(_SourceRoutingLength) = 0; \ + if ((_MacInfo)->SourceRouting) { \ + if (TmpPacket[8] & 0x80) { \ + *(_SourceRouting) = TmpPacket + 14; \ + *(_SourceRoutingLength) = TmpPacket[14] & 0x1f; \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ +} + +//++ +// +// VOID +// MacIsMulticast( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PBOOLEAN Multicast +// ); +// +// Routine Description: +// +// Returns TRUE if the packet is sent to the multicast address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// Multicast - Returns the result. +// +// Return Value: +// +// None. +// +//-- + +#define MacIsMulticast(_MacInfo, _Packet, _Multicast) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_Multicast) = ((TmpPacket[2] & 0x80) != 0); \ + break; \ + default: \ + *(_Multicast) = ((TmpPacket[0] & 0x01) != 0); \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnPacketLength( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Header, +// IN UINT PacketLength, +// OUT PUINT DataLength +// ); +// +// Routine Description: +// +// Returns the length of data in the packet given the header. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// PacketLength - The length of the data (not including header). +// +// DataLength - Returns the length of the data. Unchanged if the +// packet is not recognized. Should be initialized by caller to 0. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnPacketLength(_MacInfo, _Header, _HeaderLength, _PacketLength, _DataLength, _LookaheadBuffer, _LookaheadBufferLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + UINT TmpLength; \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + if ((_HeaderLength) >= 14) { \ + TmpLength = (TmpPacket[12] << 8) | TmpPacket[13]; \ + if (TmpLength <= 0x600) { \ + if (TmpLength <= (_PacketLength)) { \ + *(_DataLength) = TmpLength; \ + } \ + } \ + } \ + break; \ + case NdisMedium802_5: \ + if (((_HeaderLength) >= 14) && \ + (!(TmpPacket[8] & 0x80) || \ + ((_HeaderLength) >= \ + (UINT)(14 + (TmpPacket[14] & 0x1f))))) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + case NdisMediumFddi: \ + if ((_HeaderLength) >= 13) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + case NdisMediumDix: \ + if ((TmpPacket[12] == 0x80) && (TmpPacket[13] == 0xd5)) { \ + if (*(_LookaheadBufferLength) >= 3) { \ + TmpPacket = (PUCHAR)(*(_LookaheadBuffer)); \ + TmpLength = (TmpPacket[0] << 8) | TmpPacket[1]; \ + if (TmpLength <= (_PacketLength)-3) { \ + *(_DataLength) = TmpLength; \ + *(_LookaheadBuffer) = (PVOID)(TmpPacket + 3); \ + *(_LookaheadBufferLength) -= 3; \ + } \ + } \ + } \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnHeaderLength( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID HeaderLength, +// ); +// +// Routine Description: +// +// Returns the length of the MAC header in a packet (this +// is used for loopback indications to separate header +// and data). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// HeaderLength - Returns the length of the header. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnHeaderLength(_MacInfo, _Header, _HeaderLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + case NdisMediumDix: \ + *(_HeaderLength) = 14; \ + break; \ + case NdisMedium802_5: \ + if (TmpPacket[8] & 0x80) { \ + *(_HeaderLength) = (TmpPacket[14] & 0x1f) + 14; \ + } else { \ + *(_HeaderLength) = 14; \ + } \ + break; \ + case NdisMediumFddi: \ + *(_HeaderLength) = 13; \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnSingleRouteSR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * SingleRouteSR, +// OUT PUINT SingleRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard single route broadcast +// source routing information for the media type. This is used +// for ADD_NAME_QUERY, DATAGRAM, NAME_IN_CONFLICT, NAME_QUERY, +// and STATUS_QUERY frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// SingleRouteSR - Returns a pointer to the data. +// +// SingleRouteSRLength - The length of SingleRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSingleRouteSR(_MacInfo, _SingleRouteSR, _SingleRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_SingleRouteSR) = SingleRouteSourceRouting; \ + *(_SingleRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_SingleRouteSR) = NULL; \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnGeneralRouteSR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * GeneralRouteSR, +// OUT PUINT GeneralRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard general route broadcast +// source routing information for the media type. This is used +// for NAME_RECOGNIZED frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// GeneralRouteSR - Returns a pointer to the data. +// +// GeneralRouteSRLength - The length of GeneralRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnGeneralRouteSR(_MacInfo, _GeneralRouteSR, _GeneralRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_GeneralRouteSR) = GeneralRouteSourceRouting; \ + *(_GeneralRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_GeneralRouteSR) = NULL; \ + break; \ + } \ +} + +#if 0 + +//++ +// +// VOID +// MacCreateGeneralRouteReplySR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a general-route source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateGeneralRouteReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + TmpSR[0] = (TmpSR[0] & 0x1f) | 0x80; \ + TmpSR[1] = (TmpSR[1] ^ 0x80); \ + *(_NewSR) = (_ExistingSR); \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} +#endif + + +//++ +// +// VOID +// MacCreateNonBroadcastReplySR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a non-broadcast source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateNonBroadcastReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + if ((_ExistingSRLength) == 2) { \ + *(_NewSR) = NULL; \ + } else { \ + TmpSR[0] = (TmpSR[0] & 0x1f); \ + TmpSR[1] = (TmpSR[1] ^ 0x80); \ + *(_NewSR) = (_ExistingSR); \ + } \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} + + +//++ +// +// VOID +// MacModifyHeader( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR Header, +// IN UINT PacketLength +// ); +// +// Routine Description: +// +// Modifies a pre-built packet header to include the +// packet length. Used for connection-oriented traffic +// where the header is pre-built. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The header to modify. +// +// PacketLength - Packet length (not including the header). +// Currently this is the only field that cannot be pre-built. +// +// Return Value: +// +// None. +// +//-- + +#define MacModifyHeader(_MacInfo, _Header, _PacketLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + (_Header)[12] = (UCHAR)((_PacketLength) >> 8); \ + (_Header)[13] = (UCHAR)((_PacketLength) & 0xff); \ + break; \ + case NdisMediumDix: \ + (_Header)[14] = (UCHAR)((_PacketLength) >> 8); \ + (_Header)[15] = (UCHAR)((_PacketLength) & 0xff); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnMagicAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Address, +// OUT PULARGE_INTEGER Magic +// ); +// +// Routine Description: +// +// MacReturnMagicAddress returns the link as a 64 bit number. +// We then find the link in the link trees by doing a large +// integer comparison. +// +// The number is constructed by assigning the last four bytes of +// the address as the low longword, and the first two bytes as +// the high one. For 802_5 we need to mask off the source routing +// bit in byte 0 of the address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Address - The address we are encoding. +// +// Magic - Returns the magic number for this address. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnMagicAddress(_MacInfo, _Address, _Magic) \ +{ \ + PUCHAR TempAddr = (PUCHAR)(_Address); \ + \ + (_Magic)->LowPart = *((LONG UNALIGNED *)(TempAddr + 2)); \ + if ((_MacInfo)->MediumType == NdisMedium802_5) { \ + (_Magic)->HighPart = ((TempAddr[0] & 0x7f) << 8) + TempAddr[1]; \ + } else { \ + (_Magic)->HighPart = (TempAddr[0] << 8) + TempAddr[1]; \ + } \ +} + + +VOID +MacSetNetBIOSMulticast ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ); + + + +// VOID +// NbfSetNdisPacketLength ( +// IN NDIS_PACKET Packet, +// IN ULONG Length +// ); +// +// NB: This is not a general purpose macro; it assumes that we are setting the +// length of an NDIS packet with only one NDIS_BUFFER chained. We do +// this to save time during the sending of short control packets. +// + +#define NbfSetNdisPacketLength(_packet,_length) { \ + PNDIS_BUFFER NdisBuffer; \ + NdisQueryPacket((_packet), NULL, NULL, &NdisBuffer, NULL); \ + NdisAdjustBufferLength(NdisBuffer, (_length)); \ + NdisRecalculatePacketCounts(_packet); \ +} + +#endif // ifdef _MAC_ + diff --git a/private/ntos/tdi/nbf/nbfndis.c b/private/ntos/tdi/nbf/nbfndis.c new file mode 100644 index 000000000..9dd7902bd --- /dev/null +++ b/private/ntos/tdi/nbf/nbfndis.c @@ -0,0 +1,1956 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfndis.c + +Abstract: + + This module contains code which implements the routines used to interface + NBF and NDIS. All callback routines (except for Transfer Data, + Send Complete, and ReceiveIndication) are here, as well as those routines + called to initialize NDIS. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Environment: + + Kernel mode + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modify to use new TDI interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef NBF_LOCKS // see spnlckdb.c + +VOID +NbfFakeSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +VOID +NbfFakeTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ); + +#endif + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE NbfNdisProtocolHandle = (NDIS_HANDLE)NULL; + + +NDIS_STATUS +NbfSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterName + ); + +VOID +NbfOpenAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +NbfCloseAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +NbfResetComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +NbfRequestComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ); + +VOID +NbfStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbfProcessStatusClosing( + IN PVOID Parameter + ); + +VOID +NbfStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ); + + +#ifdef _PNP_POWER +VOID +NbfProtocolBindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ); +VOID +NbfProtocolUnbindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE ProtocolBindContext, + IN PNDIS_HANDLE UnbindContext + ); +#endif + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfRegisterProtocol) +#pragma alloc_text(INIT,NbfSubmitNdisRequest) +#pragma alloc_text(INIT,NbfInitializeNdis) +#else // PNP_POWER +#pragma alloc_text(PAGE,NbfProtocolBindAdapter) +#pragma alloc_text(PAGE,NbfProtocolUnbindAdapter) +#pragma alloc_text(PAGE,NbfRegisterProtocol) +#pragma alloc_text(PAGE,NbfSubmitNdisRequest) +#pragma alloc_text(PAGE,NbfInitializeNdis) +#endif +#endif + + +NTSTATUS +NbfRegisterProtocol ( + IN PUNICODE_STRING NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + PNDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + ProtChars = ExAllocatePoolWithTag( + NonPagedPool, +#ifndef _PNP_POWER + sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + +#else + sizeof(NDIS40_PROTOCOL_CHARACTERISTICS) + +#endif + NameString->MaximumLength, + ' FBN'); + + if (ProtChars == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Set up the characteristics of this protocol + // +#ifndef _PNP_POWER + ProtChars->MajorNdisVersion = 3; +#else + ProtChars->MajorNdisVersion = 4; + ProtChars->ReceivePacketHandler = NULL; + ProtChars->TranslateHandler = NULL; + ProtChars->BindAdapterHandler = NbfProtocolBindAdapter; // FIX ME!!! + ProtChars->UnbindAdapterHandler = NbfProtocolUnbindAdapter; // FIX ME!!! +#endif + ProtChars->MinorNdisVersion = 0; + + ProtChars->Name.Length = NameString->Length; + ProtChars->Name.Buffer = (PWCHAR)(ProtChars+1); + RtlCopyMemory (ProtChars->Name.Buffer, NameString->Buffer, NameString->Length); + ProtChars->Name.Buffer[NameString->Length/sizeof(WCHAR)] = UNICODE_NULL; + + ProtChars->OpenAdapterCompleteHandler = NbfOpenAdapterComplete; + ProtChars->CloseAdapterCompleteHandler = NbfCloseAdapterComplete; + ProtChars->ResetCompleteHandler = NbfResetComplete; + ProtChars->RequestCompleteHandler = NbfRequestComplete; + +#ifdef NBF_LOCKS + ProtChars->SendCompleteHandler = NbfFakeSendCompletionHandler; + ProtChars->TransferDataCompleteHandler = NbfFakeTransferDataComplete; +#else + ProtChars->SendCompleteHandler = NbfSendCompletionHandler; + ProtChars->TransferDataCompleteHandler = NbfTransferDataComplete; +#endif + + ProtChars->ReceiveHandler = NbfReceiveIndication; + ProtChars->ReceiveCompleteHandler = NbfReceiveComplete; + ProtChars->StatusHandler = NbfStatusIndication; + ProtChars->StatusCompleteHandler = NbfStatusComplete; + + NdisRegisterProtocol ( + &ndisStatus, + &NbfNdisProtocolHandle, + ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->MaximumLength); + + ExFreePool (ProtChars); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1("NbfInitialize: NdisRegisterProtocol failed: %s\n", + NbfGetNdisStatus(ndisStatus)); + } +#endif + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; +} + + +VOID +NbfDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (NbfNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + NbfNdisProtocolHandle); + NbfNdisProtocolHandle = (NDIS_HANDLE)NULL; + } +} + + +NDIS_STATUS +NbfSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + DeviceContext - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + DeviceContext->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("OID %lx pended.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus == STATUS_SUCCESS) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + if (NdisRequest->RequestType == NdisRequestSetInformation) { + NbfPrint1 ("Nbfdrvr: Set OID %lx succeeded.\n", + NdisRequest->DATA.SET_INFORMATION.Oid); + } else { + NbfPrint1 ("Nbfdrvr: Query OID %lx succeeded.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + } + + } else { +#if DBG + if (NdisRequest->RequestType == NdisRequestSetInformation) { + NbfPrint2 ("Nbfdrvr: Set OID %lx failed: %s.\n", + NdisRequest->DATA.SET_INFORMATION.Oid, NbfGetNdisStatus(NdisStatus)); + } else { + NbfPrint2 ("Nbfdrvr: Query OID %lx failed: %s.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid, NbfGetNdisStatus(NdisStatus)); + } +#endif + NbfWriteOidErrorLog( + DeviceContext, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + + return NdisStatus; +} + + +NTSTATUS +NbfInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA NbfConfig, + IN UINT ConfigInfoNameIndex + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + ULONG SendPacketReservedLength; + ULONG ReceivePacketReservedLen; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM NbfSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan }; + UINT SelectedMedium; + NDIS_REQUEST NbfRequest; + UCHAR NbfDataBuffer[6]; + NDIS_OID NbfOid; + UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x80, 0xd5 }; + ULONG WanHeaderFormat = NdisWanHeaderEthernet; + ULONG MinimumLookahead = 128 + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + ULONG MacOptions; + PNDIS_STRING AdapterString; + + + // + // Initialize this adapter for NBF use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + DeviceContext->NdisBindingHandle = NULL; + AdapterString = (PNDIS_STRING)&NbfConfig->Names[ConfigInfoNameIndex]; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &DeviceContext->NdisBindingHandle, + &SelectedMedium, + NbfSupportedMedia, + sizeof (NbfSupportedMedia) / sizeof(NDIS_MEDIUM), + NbfNdisProtocolHandle, + (NDIS_HANDLE)DeviceContext, + AdapterString, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Adapter %S open pended.\n", AdapterString); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus == NDIS_STATUS_SUCCESS) { +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Adapter %S successfully opened.\n", AdapterString); + } +#endif + } else { +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint2 ("Adapter open %S failed, status: %s.\n", + AdapterString, + NbfGetNdisStatus (NdisStatus)); + } +#endif + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 0, + NULL); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + NbfSupportedMedia[SelectedMedium], + (BOOLEAN)(NbfConfig->UseDixOverEthernet != 0), + &DeviceContext->MacInfo); + DeviceContext->MacInfo.QueryWithoutSourceRouting = + NbfConfig->QueryWithoutSourceRouting ? TRUE : FALSE; + DeviceContext->MacInfo.AllRoutesNameRecognized = + NbfConfig->AllRoutesNameRecognized ? TRUE : FALSE; + + + // + // Set the multicast/functional addresses first so we avoid windows where we + // receive only part of the addresses. + // + + MacSetNetBIOSMulticast ( + DeviceContext->MacInfo.MediumType, + DeviceContext->NetBIOSAddress.Address); + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(NbfDataBuffer, DeviceContext->NetBIOSAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_802_3_MULTICAST_LIST; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + case NdisMedium802_5: + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + // + // Fill in the OVB for our functional address. + // + + RtlCopyMemory(NbfDataBuffer, ((PUCHAR)(DeviceContext->NetBIOSAddress.Address)) + 2, 4); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 4; + + break; + + case NdisMediumFddi: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(NbfDataBuffer, DeviceContext->NetBIOSAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_FDDI_LONG_MULTICAST_LIST; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + } + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + + if (DeviceContext->MacInfo.MediumAsync) { + NbfOid = OID_WAN_CURRENT_ADDRESS; + } else { + NbfOid = OID_802_3_CURRENT_ADDRESS; + } + break; + + case NdisMedium802_5: + + NbfOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + NbfOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = NbfOid; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = DeviceContext->LocalAddress.Address; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Set up the reserved Netbios address. + // + + RtlZeroMemory(DeviceContext->ReservedNetBIOSAddress, 10); + RtlCopyMemory(&DeviceContext->ReservedNetBIOSAddress[10], DeviceContext->LocalAddress.Address, 6); + + + + // + // Now query the maximum packet sizes. + // + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxReceivePacketSize); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxSendPacketSize); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->CurSendPacketSize = DeviceContext->MaxSendPacketSize; + + + // + // Now set the minimum lookahead size. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed for non-wan media + // + + if (!DeviceContext->MacInfo.MediumAsync) { + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MediumSpeed); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MediumSpeedAccurate = TRUE; + + DeviceContext->MinimumT1Timeout = 8; // == 400 ms + + } else { + + // + // On an wan media, this isn't valid until we get an + // WAN_LINE_UP indication. Set the timeouts to + // low values for now. + // + + DeviceContext->DefaultT1Timeout = 8; + DeviceContext->MinimumT1Timeout = 8; + + DeviceContext->MediumSpeedAccurate = FALSE; + + + // + // Back off our connectionless timeouts to 2 seconds. + // + + DeviceContext->NameQueryTimeout = 2 * SECONDS; + DeviceContext->AddNameQueryTimeout = 2 * SECONDS; + DeviceContext->GeneralTimeout = 2 * SECONDS; + + // + // Use the WAN parameter for name query retries. + // + + DeviceContext->NameQueryRetries = NbfConfig->WanNameQueryRetries; + + // + // Use this until we know better. + // + + DeviceContext->RecommendedSendWindow = 1; + + } + + // + // On media that use source routing, we double our name query + // retry count if we are configured to try both ways (with and + // without source routing). + // + + if ((DeviceContext->MacInfo.QueryWithoutSourceRouting) && + (DeviceContext->MacInfo.SourceRouting)) { + DeviceContext->NameQueryRetries *= 2; + } + + + // + // For wan, specify our protocol ID and header format. + // We don't query the medium subtype because we don't + // case (since we require ethernet emulation). + // + + if (DeviceContext->MacInfo.MediumAsync) { + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Now query the MAC's optional characteristics. + // + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if 1 + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; +#else + MacOptions = 0; +#endif + } + + DeviceContext->MacInfo.CopyLookahead = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0); + DeviceContext->MacInfo.ReceiveSerialized = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_RECEIVE_SERIALIZED) != 0); + DeviceContext->MacInfo.TransferSynchronous = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_TRANSFERS_NOT_PEND) != 0); + DeviceContext->MacInfo.SingleReceive = + (BOOLEAN)(DeviceContext->MacInfo.ReceiveSerialized && DeviceContext->MacInfo.TransferSynchronous); + + +#if 0 + // + // Now set our options if needed. + // + // Don't allow early indications because we can't determine + // if the CRC has been checked yet. + // + + if ((DeviceContext->MacInfo.MediumType == NdisMedium802_3) || + (DeviceContext->MacInfo.MediumType == NdisMediumDix)) { + + ULONG ProtocolOptions = NDIS_PROT_OPTION_ESTIMATED_LENGTH; + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_PROTOCOL_OPTIONS; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &ProtocolOptions; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } +#endif + + + // + // Calculate the NDIS-related stuff. + // + + SendPacketReservedLength = sizeof (SEND_PACKET_TAG); + ReceivePacketReservedLen = sizeof (RECEIVE_PACKET_TAG); + + + // + // The send packet pool is used for UI frames and regular packets. + // + + SendPacketPoolSize = NbfConfig->SendPacketPoolSize; + + // + // The receive packet pool is used in transfer data. + // + // For a MAC that will only have one receive active, we + // don't need multiple receive packets. Allow an extra + // one for loopback. + // + + if (DeviceContext->MacInfo.SingleReceive) { + ReceivePacketPoolSize = 2; + } else { + ReceivePacketPoolSize = NbfConfig->ReceivePacketPoolSize; + } + + + // Allocate Packet pool descriptors for dynamic packet allocation. + + DeviceContext->SendPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->SendPacketPoolDesc == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(DeviceContext->SendPacketPoolDesc, + sizeof(NBF_POOL_LIST_DESC)); + + DeviceContext->SendPacketPoolDesc->NumElements = + DeviceContext->SendPacketPoolDesc->TotalElements = (USHORT)SendPacketPoolSize; + + NdisAllocatePacketPool ( + &NdisStatus, + &DeviceContext->SendPacketPoolDesc->PoolHandle, + SendPacketPoolSize, + SendPacketReservedLength); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NdisInitializePacketPool successful.\n"); + } + + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 109, + SendPacketPoolSize, + 0); + ExFreePool (DeviceContext->SendPacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->SendPacketPoolSize = SendPacketPoolSize; + + DeviceContext->MemoryUsage += + (SendPacketPoolSize * + (sizeof(NDIS_PACKET) + SendPacketReservedLength)); + +#if DBG + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + DbgPrint ("send pool %d hdr %d, %ld\n", + SendPacketPoolSize, + SendPacketReservedLength, + DeviceContext->MemoryUsage); + } +#endif + + + // Allocate Packet pool descriptors for dynamic packet allocation. + + DeviceContext->ReceivePacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->ReceivePacketPoolDesc == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(DeviceContext->ReceivePacketPoolDesc, + sizeof(NBF_POOL_LIST_DESC)); + + DeviceContext->ReceivePacketPoolDesc->NumElements = + DeviceContext->ReceivePacketPoolDesc->TotalElements = (USHORT)ReceivePacketPoolSize; + + NdisAllocatePacketPool( + &NdisStatus, + &DeviceContext->ReceivePacketPoolDesc->PoolHandle, + ReceivePacketPoolSize, + ReceivePacketReservedLen); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NdisInitializePacketPool successful, Pool: %lx\n", + DeviceContext->ReceivePacketPoolDesc->PoolHandle); + } + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ExFreePool (DeviceContext->SendPacketPoolDesc); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + DeviceContext->ReceivePacketPoolDesc = NULL; + NbfCloseNdis (DeviceContext); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 209, + ReceivePacketPoolSize, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->ReceivePacketPoolSize = ReceivePacketPoolSize; + + DeviceContext->MemoryUsage += + (ReceivePacketPoolSize * + (sizeof(NDIS_PACKET) + ReceivePacketReservedLen)); + +#if DBG + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + DbgPrint ("receive pool %d hdr %d, %ld\n", + ReceivePacketPoolSize, + ReceivePacketReservedLen, + DeviceContext->MemoryUsage); + } +#endif + + + // + // Allocate the buffer pool; as an estimate, allocate + // one per send or receive packet. + // + + NdisAllocateBufferPool ( + &NdisStatus, + &DeviceContext->NdisBufferPool, + SendPacketPoolSize + ReceivePacketPoolSize); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NdisAllocateBufferPool successful.\n"); + } + + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisAllocateBufferPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ExFreePool(DeviceContext->SendPacketPoolDesc); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + DeviceContext->ReceivePacketPoolDesc = NULL; + DeviceContext->NdisBufferPool = NULL; + NbfCloseNdis (DeviceContext); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 309, + SendPacketPoolSize + ReceivePacketPoolSize, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + // + // Fill in the OVB for packet filter. + // + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + case NdisMediumFddi: + + RtlStoreUlong((PULONG)NbfDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST)); + break; + + case NdisMedium802_5: + + RtlStoreUlong((PULONG)NbfDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_FUNCTIONAL)); + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* NbfInitializeNdis */ + + +VOID +NbfCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in NbfInitializeNdis. + It is written so that it can be called from within NbfInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (DeviceContext->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + DeviceContext->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("Adapter close pended.\n"); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + + if (DeviceContext->SendPacketPoolDesc != NULL && + DeviceContext->SendPacketPoolDesc->PoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->SendPacketPoolDesc->PoolHandle); + ExFreePool(DeviceContext->SendPacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + } + + if (DeviceContext->ReceivePacketPoolDesc != NULL && + DeviceContext->ReceivePacketPoolDesc->PoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->ReceivePacketPoolDesc->PoolHandle); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->ReceivePacketPoolDesc = NULL; + } + + if (DeviceContext->NdisBufferPool != NULL) { + NdisFreeBufferPool (DeviceContext->NdisBufferPool); + } + +} /* NbfCloseNdis */ + + +VOID +NbfOpenAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfOpenAdapterCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfCloseAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfCloseAdapterCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfResetComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfResetCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + return; +} + +VOID +NbfRequestComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint2 ("Nbfdrvr: NbfRequestComplete request: %i, NDIS Status: %s\n", + NdisRequest->RequestType,NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PDEVICE_CONTEXT DeviceContext; + PNDIS_WAN_LINE_UP LineUp; + KIRQL oldirql; + PTP_LINK Link; + + DeviceContext = (PDEVICE_CONTEXT)NdisBindingContext; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + switch (NdisStatus) { + + case NDIS_STATUS_WAN_LINE_UP: + + // + // A wan line is connected. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + // + // If this happens before we are ready, then make + // a note of it, otherwise make the device ready. + // + + DeviceContext->MediumSpeedAccurate = TRUE; + + LineUp = (PNDIS_WAN_LINE_UP)StatusBuffer; + + // + // See if this is a new lineup for this protocol type + // + if (LineUp->ProtocolType == 0x80D5) { + NDIS_HANDLE TransportHandle; + + *((ULONG UNALIGNED *)(&TransportHandle)) = + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])); + + // + // See if this is a new lineup + // + if (TransportHandle == NULL) { + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])) = *((ULONG UNALIGNED *)(&DeviceContext)); +// ETH_COPY_NETWORK_ADDRESS(DeviceContext->LocalAddress.Address, LineUp->LocalAddress); +// ETH_COPY_NETWORK_ADDRESS(&DeviceContext->ReservedNetBIOSAddress[10], DeviceContext->LocalAddress.Address); + } + + // + // Calculate minimum link timeouts based on the speed, + // which is passed in StatusBuffer. + // + // The formula is (max_frame_size * 2) / speed + 0.4 sec. + // This expands to + // + // MFS (bytes) * 2 8 bits + // ------------------- x ------ == timeout (sec), + // speed (100 bits/sec) byte + // + // which is (MFS * 16 / 100) / speed. We then convert it into + // the 50 ms units that NBF uses and add 8 (which is + // 0.4 seconds in 50 ms units). + // + // As a default timeout we use the min + 0.2 seconds + // unless the configured default is more. + // + + if (LineUp->LinkSpeed > 0) { + DeviceContext->MediumSpeed = LineUp->LinkSpeed; + } + + if (LineUp->MaximumTotalSize > 0) { +#if DBG + if (LineUp->MaximumTotalSize > DeviceContext->MaxSendPacketSize) { + DbgPrint ("Nbf: Bad LINE_UP size, %d (> %d)\n", + LineUp->MaximumTotalSize, DeviceContext->MaxSendPacketSize); + } + if (LineUp->MaximumTotalSize < 128) { + DbgPrint ("NBF: Bad LINE_UP size, %d (< 128)\n", + LineUp->MaximumTotalSize); + } +#endif + DeviceContext->CurSendPacketSize = LineUp->MaximumTotalSize; + } + + if (LineUp->SendWindow == 0) { + DeviceContext->RecommendedSendWindow = 3; + } else { + DeviceContext->RecommendedSendWindow = LineUp->SendWindow + 1; + } + + DeviceContext->MinimumT1Timeout = + ((((DeviceContext->CurSendPacketSize * 16) / 100) / DeviceContext->MediumSpeed) * + ((1 * SECONDS) / (50 * MILLISECONDS))) + 8; + + if (DeviceContext->DefaultT1Timeout < DeviceContext->MinimumT1Timeout) { + DeviceContext->DefaultT1Timeout = DeviceContext->MinimumT1Timeout + 4; + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + break; + + case NDIS_STATUS_WAN_LINE_DOWN: + + // + // An wan line is disconnected. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + DeviceContext->MediumSpeedAccurate = FALSE; + + // + // Set the timeouts to small values (0.4 seconds) + // + + DeviceContext->DefaultT1Timeout = 8; + DeviceContext->MinimumT1Timeout = 8; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + + // + // Stop the link on this device context (there + // will only be one). + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + if (DeviceContext->LinkTreeElements > 0) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) == 0) { + + NbfReferenceLink ("Wan line down", Link, LREF_TREE); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + // + // Put the link in ADM to shut it down. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->State != LINK_STATE_ADM) { + Link->State = LINK_STATE_ADM; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfDereferenceLinkSpecial ("Wan line down", Link, LREF_NOT_ADM); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + // + // Now stop it to destroy all connections on it. + // + + NbfStopLink (Link); + + NbfDereferenceLink ("Wan line down", Link, LREF_TREE); + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + break; + + case NDIS_STATUS_WAN_FRAGMENT: + + // + // A fragment has been received on the wan line. + // Send a reject back to him. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + if (DeviceContext->LinkTreeElements > 0) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + NbfReferenceLink ("Async line down", Link, LREF_TREE); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfSendRej (Link, FALSE, FALSE); // release lock + NbfDereferenceLink ("Async line down", Link, LREF_TREE); + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + break; + + case NDIS_STATUS_CLOSING: + + // + // The adapter is shutting down. We queue a worker + // thread to handle this. + // + + ExInitializeWorkItem( + &DeviceContext->StatusClosingQueueItem, + NbfProcessStatusClosing, + (PVOID)DeviceContext); + ExQueueWorkItem(&DeviceContext->StatusClosingQueueItem, DelayedWorkQueue); + + break; + + default: + break; + + } + + KeLowerIrql (oldirql); + +} + + +VOID +NbfProcessStatusClosing( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the thread routine which restarts packetizing + that has been delayed on WAN to allow RRs to come in. + This is very similar to PacketizeConnections. + +Arguments: + + Parameter - A pointer to the device context. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; +#if 0 + PTP_ADDRESS Address; +#endif + PTP_LINK Link; + PTP_REQUEST Request; + NDIS_STATUS ndisStatus; + KIRQL oldirql; + + DeviceContext = (PDEVICE_CONTEXT)Parameter; + + // + // Prevent new activity on the connection. + // + + DeviceContext->State = DEVICECONTEXT_STATE_DOWN; + + +#if 0 + // + // Stop all the addresses. + // + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->AddressDatabase, + &DeviceContext->SpinLock)) != NULL) { + + Address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + InitializeListHead(p); + + NbfStopAddress (Address); + + } +#endif + + // + // To speed things along, stop all the links too. + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + DeviceContext->LastLink = NULL; + + while (DeviceContext->LinkTreeRoot != NULL) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + DeviceContext->LinkTreeRoot = RtlDelete ((PRTL_SPLAY_LINKS)Link); + DeviceContext->LinkTreeElements--; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if (Link->OnShortList) { + RemoveEntryList (&Link->ShortList); + } + if (Link->OnLongList) { + RemoveEntryList (&Link->LongList); + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if (Link->State != LINK_STATE_ADM) { + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in CONNECTING mode", Link, LREF_NOT_ADM); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + NbfStopLink (Link); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + KeLowerIrql (oldirql); + + + // + // Shutdown the control channel. + // + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->QueryIndicationQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->DatagramIndicationQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->StatusQueryQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->FindNameQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + + // + // Close the NDIS binding. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + DeviceContext->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("Adapter close pended.\n"); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + DeviceContext->NdisBindingHandle = NULL; + + // + // We ignore ndisStatus. + // + +#if 0 + // + // Remove all the storage associated with the device. + // + + NbfFreeResources (DeviceContext); + + NdisFreePacketPool (DeviceContext->SendPacketPoolHandle); + NdisFreePacketPool (DeviceContext->ReceivePacketPoolHandle); + NdisFreeBufferPool (DeviceContext->NdisBufferPoolHandle); +#endif + + // + // And remove the creation reference from the device + // context. + // + + NbfDereferenceDeviceContext ("Unload", DeviceContext, DCREF_CREATION); + +} /* NbfProcessStatusClosing */ + + +VOID +NbfStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} + +#if DBG + +PUCHAR +NbfGetNdisStatus( + NDIS_STATUS GeneralStatus + ) +/*++ + +Routine Description: + + This routine returns a pointer to the string describing the NDIS error + denoted by GeneralStatus. + +Arguments: + + GeneralStatus - the status you wish to make readable. + +Return Value: + + None. + +--*/ +{ + static NDIS_STATUS Status[] = { + NDIS_STATUS_SUCCESS, + NDIS_STATUS_PENDING, + + NDIS_STATUS_ADAPTER_NOT_FOUND, + NDIS_STATUS_ADAPTER_NOT_OPEN, + NDIS_STATUS_ADAPTER_NOT_READY, + NDIS_STATUS_ADAPTER_REMOVED, + NDIS_STATUS_BAD_CHARACTERISTICS, + NDIS_STATUS_BAD_VERSION, + NDIS_STATUS_CLOSING, + NDIS_STATUS_DEVICE_FAILED, + NDIS_STATUS_FAILURE, + NDIS_STATUS_INVALID_DATA, + NDIS_STATUS_INVALID_LENGTH, + NDIS_STATUS_INVALID_OID, + NDIS_STATUS_INVALID_PACKET, + NDIS_STATUS_MULTICAST_FULL, + NDIS_STATUS_NOT_INDICATING, + NDIS_STATUS_NOT_RECOGNIZED, + NDIS_STATUS_NOT_RESETTABLE, + NDIS_STATUS_NOT_SUPPORTED, + NDIS_STATUS_OPEN_FAILED, + NDIS_STATUS_OPEN_LIST_FULL, + NDIS_STATUS_REQUEST_ABORTED, + NDIS_STATUS_RESET_IN_PROGRESS, + NDIS_STATUS_RESOURCES, + NDIS_STATUS_UNSUPPORTED_MEDIA + }; + static PUCHAR String[] = { + "SUCCESS", + "PENDING", + + "ADAPTER_NOT_FOUND", + "ADAPTER_NOT_OPEN", + "ADAPTER_NOT_READY", + "ADAPTER_REMOVED", + "BAD_CHARACTERISTICS", + "BAD_VERSION", + "CLOSING", + "DEVICE_FAILED", + "FAILURE", + "INVALID_DATA", + "INVALID_LENGTH", + "INVALID_OID", + "INVALID_PACKET", + "MULTICAST_FULL", + "NOT_INDICATING", + "NOT_RECOGNIZED", + "NOT_RESETTABLE", + "NOT_SUPPORTED", + "OPEN_FAILED", + "OPEN_LIST_FULL", + "REQUEST_ABORTED", + "RESET_IN_PROGRESS", + "RESOURCES", + "UNSUPPORTED_MEDIA" + }; + + static UCHAR BadStatus[] = "UNDEFINED"; +#define StatusCount (sizeof(Status)/sizeof(NDIS_STATUS)) + INT i; + + for (i=0; iInitConnections + 2 + + NbfConfig->InitRequests + 2 + + NbfConfig->InitUIFrames + 2 + + NbfConfig->InitPackets + 2 + + NbfConfig->InitLinks + 2 + + NbfConfig->InitAddressFiles + 2 + + NbfConfig->InitAddresses + 2), + ' FBN'); + + ASSERT (NbfConnectionTable); + +#if 0 + if (NbfConnectionTable == NULL) { + *NdisStatus = NDIS_STATUS_RESOURCES; + return; + } +#endif + + + NbfRequestTable = NbfConnectionTable + (NbfConfig->InitConnections + 2); + NbfUiFrameTable = NbfRequestTable + (NbfConfig->InitRequests + 2); + NbfSendPacketTable = NbfUiFrameTable + (NbfConfig->InitUIFrames + 2); + NbfLinkTable = NbfSendPacketTable + (NbfConfig->InitPackets + 2); + NbfAddressFileTable = NbfLinkTable + (NbfConfig->InitLinks + 2); + NbfAddressTable = NbfAddressFileTable + + (NbfConfig->InitAddressFiles + 2); +#endif + } + + for (j=0;jNumAdapters;j++ ) { + + // + // Loop through all the adapters that are in the configuration + // information structure until we find the one that ndis is calling + // Protocol bind adapter for. + // + if (NdisEqualString(DeviceName, &NbfConfig->Names[j], TRUE)) { + break; + } + + } + + SuccessfulOpens += NbfInitializeOneDeviceContext(NdisStatus, + NbfDriverObject, + NbfConfig, j + ); + + if (SuccessfulOpens == 1 && *NdisStatus == NDIS_STATUS_SUCCESS) { + +#if DBG + DbgPrint("Calling NbfAcdBind()\n"); +#endif + // + // If this is the first successful open. + // +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbfAcdBind(); +#endif // RASAUTODIAL + } + return; +} +VOID +NbfProtocolUnbindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE ProtocolBindContext, + IN PNDIS_HANDLE UnbindContext + ) +/*++ + +Routine Description: + + This routine deactivates a transport binding. Currently unimplemented. + +Arguments: + + NdisStatus - The status of the bind. + + ProtocolBindContext - the context from the openadapter call + + UnbindContext - A context for async unbinds. + + +Return Value: + + None. + +--*/ + +{ + + *NdisStatus = STATUS_NOT_IMPLEMENTED; + return; +} + +#endif // _PNP_POWER diff --git a/private/ntos/tdi/nbf/nbfprocs.h b/private/ntos/tdi/nbf/nbfprocs.h new file mode 100644 index 000000000..9449f0563 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfprocs.h @@ -0,0 +1,2322 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfprocs.h + +Abstract: + + This header file defines private functions for the NT NBF transport + provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Revision History: + +--*/ + +#ifndef _NBFPROCS_ +#define _NBFPROCS_ + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// IF_NBFDBG( +// IN PSZ Message +// ); +// + +#if DBG +#define IF_NBFDBG(flags) \ + if (NbfDebug & (flags)) +#else +#define IF_NBFDBG(flags) \ + if (0) +#endif + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + DbgPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow DbgPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbfPrint0(fmt) DbgPrint(fmt) +#define NbfPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbfPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbfPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbfPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbfPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbfPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbfPrint0(fmt) +#define NbfPrint1(fmt,v0) +#define NbfPrint2(fmt,v0,v1) +#define NbfPrint3(fmt,v0,v1,v2) +#define NbfPrint4(fmt,v0,v1,v2,v3) +#define NbfPrint5(fmt,v0,v1,v2,v3,v4) +#define NbfPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + +// +// The REFCOUNTS message take up a lot of room, so make +// removing them easy. +// + +#if 1 +#define IF_REFDBG IF_NBFDBG (NBF_DEBUG_REFCOUNTS) +#else +#define IF_REFDBG if (0) +#endif + +#if DBG +#define NbfReferenceLink( Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefL %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->ReferenceCount);\ + }\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefLink (Link) + +#define NbfDereferenceLink(Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefL %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefLink (Link) + +#define NbfDereferenceLinkMacro(Reason, Link, Type)\ + NbfDereferenceLink(Reason, Link, Type) + +#define NbfReferenceLinkSpecial( Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to special reference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefLS %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->SpecialRefCount);\ + }\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefLinkSpecial (Link) + +#define NbfDereferenceLinkSpecial(Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to special dereference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefLS %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->SpecialRefCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefLinkSpecial (Link) + +#define NbfReferenceConnection(Reason, Connection, Type)\ + if ((Connection)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed conn %lx\n", Connection); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefC %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Connection)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefConnection (Connection) + +#define NbfDereferenceConnection(Reason, Connection, Type)\ + if ((Connection)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed conn %lx\n", Connection); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefC %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)&((Connection)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefConnection (Connection) + +#define NbfDereferenceConnectionMacro(Reason, Connection, Type)\ + NbfDereferenceConnection(Reason, Connection, Type) + +#define NbfDereferenceConnectionSpecial(Reason, Connection, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefCL %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)&((Connection)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefConnectionSpecial (Connection) + +#define NbfReferenceRequest( Reason, Request, Type)\ + if ((Request)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed req %lx\n", Request); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefR %x: %s %s, %ld : %ld\n", Request, Reason, __FILE__, __LINE__, (Request)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Request)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefRequest (Request) + +#define NbfDereferenceRequest(Reason, Request, Type)\ + if ((Request)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed req %lx\n", Request); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefR %x: %s %s, %ld : %ld\n", Request, Reason, __FILE__, __LINE__, (Request)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Request)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefRequest (Request) + +#define NbfReferenceSendIrp( Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("RefSI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_SEND_REFCOUNT(IrpSp));}\ + NbfRefSendIrp (IrpSp) + +#define NbfDereferenceSendIrp(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefSI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_SEND_REFCOUNT(IrpSp));\ + } \ + NbfDerefSendIrp (IrpSp) + +#define NbfReferenceReceiveIrpLocked( Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("RefRI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));}\ + NbfRefReceiveIrpLocked (IrpSp) + +#define NbfDereferenceReceiveIrp(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefRI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));\ + } \ + NbfDerefReceiveIrp (IrpSp) + +#define NbfDereferenceReceiveIrpLocked(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefRILocked %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));\ + } \ + NbfDerefReceiveIrpLocked (IrpSp) + +#define NbfReferenceAddress( Reason, Address, Type)\ + IF_REFDBG { \ + DbgPrint ("RefA %x: %s %s, %ld : %ld\n", Address, Reason, __FILE__, __LINE__, (Address)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Address)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefAddress (Address) + +#define NbfDereferenceAddress(Reason, Address, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefA %x: %s %s, %ld : %ld\n", Address, Reason, __FILE__, __LINE__, (Address)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Address)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefAddress (Address) + +#define NbfReferenceDeviceContext( Reason, DeviceContext, Type)\ + IF_REFDBG { \ + DbgPrint ("RefDC %x: %s %s, %ld : %ld\n", DeviceContext, Reason, __FILE__, __LINE__, (DeviceContext)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(DeviceContext)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefDeviceContext (DeviceContext) + +#define NbfDereferenceDeviceContext(Reason, DeviceContext, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefDC %x: %s %s, %ld : %ld\n", DeviceContext, Reason, __FILE__, __LINE__, (DeviceContext)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(DeviceContext)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefDeviceContext (DeviceContext) + +#else +#if defined(NBF_UP) +#define NbfReferenceLink(Reason, Link, Type) \ + { \ + ULONG _ref; \ + _ref = ++(Link)->ReferenceCount; \ + if ( _ref == 0 ) { \ + NbfReferenceLinkSpecial ("first ref", (Link), LREF_SPECIAL_TEMP); \ + } \ + } +#else +#define NbfReferenceLink(Reason, Link, Type) \ + if (InterlockedIncrement( \ + &(Link)->ReferenceCount) == 0) { \ + NbfReferenceLinkSpecial ("first ref", (Link), LREF_SPECIAL_TEMP); \ + } +#endif + +#define NbfDereferenceLink(Reason, Link, Type)\ + NbfDereferenceLinkMacro(Reason,Link,Type) + +#if defined(NBF_UP) +#define NbfDereferenceLinkMacro(Reason, Link, Type){ \ + ULONG _ref; \ + _ref = --(Link)->ReferenceCount; \ + if (_ref < 0) { \ + NbfDisconnectLink (Link); \ + NbfDerefLinkSpecial (Link); \ + } \ +} +#else +#define NbfDereferenceLinkMacro(Reason, Link, Type){ \ + if (InterlockedDecrement( \ + &(Link)->ReferenceCount) < 0) { \ + NbfDisconnectLink (Link); \ + NbfDerefLinkSpecial (Link); \ + } \ +} +#endif + +#define NbfReferenceLinkSpecial(Reason, Link, Type)\ + NbfRefLinkSpecial (Link) + +#define NbfDereferenceLinkSpecial(Reason, Link, Type)\ + NbfDerefLinkSpecial (Link) + +#define NbfReferenceConnection(Reason, Connection, Type)\ + if (InterlockedIncrement( \ + &(Connection)->ReferenceCount) == 0) { \ + ExInterlockedAddUlong( \ + (PULONG)(&(Connection)->SpecialRefCount), \ + 1, \ + (Connection)->ProviderInterlock); \ + } + +#define NbfDereferenceConnection(Reason, Connection, Type)\ + NbfDerefConnection (Connection) + +#define NbfDereferenceConnectionMacro(Reason, Connection, Type){ \ + if (InterlockedDecrement( \ + &(Connection)->ReferenceCount) < 0) { \ + if (NbfDisconnectFromLink (Connection, TRUE)) { \ + NbfIndicateDisconnect (Connection); \ + } \ + NbfDerefConnectionSpecial (Connection); \ + } \ +} + +#define NbfDereferenceConnectionSpecial(Reason, Connection, Type)\ + NbfDerefConnectionSpecial (Connection) + +#define NbfReferenceRequest(Reason, Request, Type)\ + (VOID)InterlockedIncrement( \ + &(Request)->ReferenceCount) + +#define NbfDereferenceRequest(Reason, Request, Type)\ + NbfDerefRequest (Request) + +#define NbfReferenceSendIrp(Reason, IrpSp, Type)\ + (VOID)InterlockedIncrement( \ + &IRP_SEND_REFCOUNT(IrpSp)) + +#define NbfDereferenceSendIrp(Reason, IrpSp, Type) {\ + PIO_STACK_LOCATION _IrpSp = (IrpSp); \ + if (InterlockedDecrement( \ + &IRP_SEND_REFCOUNT(_IrpSp)) == 0) { \ + PIRP _Irp = IRP_SEND_IRP(_IrpSp); \ + IRP_SEND_REFCOUNT(_IrpSp) = 0; \ + IRP_SEND_IRP (_IrpSp) = NULL; \ + IoCompleteRequest (_Irp, IO_NETWORK_INCREMENT); \ + } \ +} + +#define NbfReferenceReceiveIrpLocked(Reason, IrpSp, Type)\ + ++IRP_RECEIVE_REFCOUNT(IrpSp) + +#define NbfDereferenceReceiveIrp(Reason, IrpSp, Type)\ + NbfDerefReceiveIrp (IrpSp) + +#define NbfDereferenceReceiveIrpLocked(Reason, IrpSp, Type) { \ + if (--IRP_RECEIVE_REFCOUNT(IrpSp) == 0) { \ + ExInterlockedInsertTailList( \ + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), \ + &(IRP_RECEIVE_IRP(IrpSp))->Tail.Overlay.ListEntry, \ + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); \ + } \ +} + +#define NbfReferenceAddress(Reason, Address, Type)\ + (VOID)InterlockedIncrement(&(Address)->ReferenceCount) + +#define NbfDereferenceAddress(Reason, Address, Type)\ + NbfDerefAddress (Address) + +#define NbfReferenceDeviceContext(Reason, DeviceContext, Type)\ + NbfRefDeviceContext (DeviceContext) + +#define NbfDereferenceDeviceContext(Reason, DeviceContext, Type)\ + NbfDerefDeviceContext (DeviceContext) + +#define NbfReferencePacket(Packet) \ + (VOID)InterlockedIncrement(&(Packet)->ReferenceCount) + +#define NbfDereferencePacket(Packet){ \ + if (InterlockedDecrement ( \ + &(Packet)->ReferenceCount) == 0) { \ + NbfDestroyPacket (Packet); \ + } \ +} + +#endif + + +// +// Error and statistics Macros +// + + +// VOID +// LogErrorToSystem( +// NTSTATUS ErrorType, +// PUCHAR ErrorDescription +// ) + +/*++ + +Routine Description: + + This routine is called to log an error from the transport to the system. + Errors that are of system interest should be logged using this interface. + For now, this macro is defined trivially. (BUGBUG) + +Arguments: + + ErrorType - The error type, a conventional NT status + + ErrorDescription - A pointer to a string describing the error. + +Return Value: + + none. + +--*/ + +#if DBG +#define LogErrorToSystem( ErrorType, ErrorDescription) \ + DbgPrint ("Logging error: File: %s Line: %ld \n Description: %s\n",__FILE__, __LINE__, ErrorDescription) +#else +#define LogErrorToSystem( ErrorType, ErrorDescription) +#endif + + +// +// Routines in TIMER.C (lightweight timer system package). +// Note that all the start and stop routines for the timers assume that you +// have the link spinlock when you call them! +// Note also that, with the latest revisions, the timer system now works by +// putting those links that have timers running on a list of links to be looked +// at for each clock tick. This list is ordered, with the most recently inserted +// elements at the tail of the list. Note further that anything already on the +// is moved to the end of the list if the timer is restarted; thus, the list +// order is preserved. +// + +VOID +NbfStartShortTimer( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfInitializeTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfStopTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ); + + +VOID +StartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ); + +VOID +StartT2( + IN PTP_LINK Link + ); + +VOID +StartTi( + IN PTP_LINK Link + ); + +#if DBG + +VOID +StopT1( + IN PTP_LINK Link + ); + +VOID +StopT2( + IN PTP_LINK Link + ); + +VOID +StopTi( + IN PTP_LINK Link + ); + +#else + +#define StopT1(_Link) \ + { \ + (_Link)->CurrentPollOutstanding = FALSE; \ + (_Link)->T1 = 0; \ + } + +#define StopT2(_Link) \ + { \ + (_Link)->ConsecutiveIFrames = 0; \ + (_Link)->T2 = 0; \ + } + +#define StopTi(_Link) \ + (_Link)->Ti = 0; + +#endif + + +// +// These functions may become macros once they are finished. +// + +ULONG +GetTimerInterval( + IN PTP_LINK Link + ); + +VOID +BackoffCurrentT1Timeout( + IN PTP_LINK Link + ); + +VOID +UpdateBaseT1Timeout( + IN PTP_LINK Link + ); + +VOID +CancelT1Timeout( + IN PTP_LINK Link + ); + +VOID +UpdateDelayAndThroughput( + IN PTP_LINK Link, + IN ULONG TimerInterval + ); + +VOID +FakeStartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ); + +VOID +FakeUpdateBaseT1Timeout( + IN PTP_LINK Link + ); + + +// +// These macros are used to create and destroy packets, due +// to the allocation or deallocation of structure which +// need them. +// + +#define NbfAddUIFrame(DeviceContext) { \ + PTP_UI_FRAME _UIFrame; \ + NbfAllocateUIFrame ((DeviceContext), &_UIFrame); \ + if (_UIFrame != NULL) { \ + ExInterlockedInsertTailList( \ + &(DeviceContext)->UIFramePool, \ + &_UIFrame->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define NbfRemoveUIFrame(DeviceContext) { \ + PLIST_ENTRY p; \ + if (DeviceContext->UIFrameAllocated > DeviceContext->UIFrameInitAllocated) { \ + p = ExInterlockedRemoveHeadList( \ + &(DeviceContext)->UIFramePool, \ + &(DeviceContext)->Interlock); \ + if (p != NULL) { \ + NbfDeallocateUIFrame((DeviceContext), \ + (PTP_UI_FRAME)CONTAINING_RECORD(p, TP_UI_FRAME, Linkage)); \ + } \ + } \ +} + + +#define NbfAddSendPacket(DeviceContext) { \ + PTP_PACKET _SendPacket; \ + NbfAllocateSendPacket ((DeviceContext), &_SendPacket); \ + if (_SendPacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->PacketPool, \ + (PSINGLE_LIST_ENTRY)&_SendPacket->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define NbfRemoveSendPacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->PacketAllocated > DeviceContext->PacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->PacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateSendPacket((DeviceContext), \ + (PTP_PACKET)CONTAINING_RECORD(s, TP_PACKET, Linkage)); \ + } \ + } \ +} + + +#define NbfAddReceivePacket(DeviceContext) { \ + if (!(DeviceContext)->MacInfo.SingleReceive) { \ + PNDIS_PACKET _ReceivePacket; \ + NbfAllocateReceivePacket ((DeviceContext), &_ReceivePacket); \ + if (_ReceivePacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &((PRECEIVE_PACKET_TAG)_ReceivePacket->ProtocolReserved)->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ + } \ +} + +#define NbfRemoveReceivePacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceivePacketAllocated > DeviceContext->ReceivePacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateReceivePacket((DeviceContext), \ + (PNDIS_PACKET)CONTAINING_RECORD(s, NDIS_PACKET, ProtocolReserved[0])); \ + } \ + } \ +} + + +#define NbfAddReceiveBuffer(DeviceContext) { \ + if (!(DeviceContext)->MacInfo.SingleReceive) { \ + PBUFFER_TAG _ReceiveBuffer; \ + NbfAllocateReceiveBuffer ((DeviceContext), &_ReceiveBuffer); \ + if (_ReceiveBuffer != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + (PSINGLE_LIST_ENTRY)&_ReceiveBuffer->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ + } \ +} + +#define NbfRemoveReceiveBuffer(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceiveBufferAllocated > DeviceContext->ReceiveBufferInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateReceiveBuffer(DeviceContext, \ + (PBUFFER_TAG)CONTAINING_RECORD(s, BUFFER_TAG, Linkage)); \ + } \ + } \ +} + + +// +// These routines are used to maintain counters. +// + +#define INCREMENT_COUNTER(_DeviceContext,_Field) \ + ++(_DeviceContext)->Statistics._Field + +#define DECREMENT_COUNTER(_DeviceContext,_Field) \ + --(_DeviceContext)->Statistics._Field + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger), (ULONG)(_Ulong)) + + + +// +// Routines in PACKET.C (TP_PACKET object manager). +// + +VOID +NbfAllocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *TransportUIFrame + ); + +VOID +NbfAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ); + +VOID +NbfAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ); + +VOID +NbfAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ); + +VOID +NbfDeallocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME TransportUIFrame + ); + +VOID +NbfDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ); + +VOID +NbfDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ); + +VOID +NbfDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ); + +NTSTATUS +NbfCreatePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link, + OUT PTP_PACKET *Packet + ); + +NTSTATUS +NbfCreateRrPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link, + OUT PTP_PACKET *Packet + ); + +VOID +NbfDestroyPacket( + IN PTP_PACKET Packet + ); +VOID +NbfGrowSendPacketPool( + IN PDEVICE_CONTEXT DeviceContext + ); + +#if DBG +VOID +NbfReferencePacket( + IN PTP_PACKET Packet + ); + +VOID +NbfDereferencePacket( + IN PTP_PACKET Packet + ); +#endif + +VOID +NbfWaitPacket( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); + +#if DBG +#define MAGIC 1 +extern BOOLEAN NbfEnableMagic; +#else +#define MAGIC 0 +#endif + +#if MAGIC +VOID +NbfSendMagicBullet ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); +#endif + +// +// Routines in RCVENG.C (Receive engine). +// + +VOID +AwakenReceive( + IN PTP_CONNECTION Connection + ); + +VOID +ActivateReceive( + IN PTP_CONNECTION Connection + ); + +VOID +CompleteReceive ( + IN PTP_CONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN ULONG BytesTransferred + ); + +VOID +NbfCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbfCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routines in SEND.C (Receive engine). +// + +NTSTATUS +NbfTdiSend( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiSendDatagram( + IN PIRP Irp + ); + +// +// Routines in SENDENG.C (Send engine). +// + +#if DBG + +VOID +InitializeSend( + PTP_CONNECTION Connection + ); + +#else + +// See SENDENG.C for the fully-commented description of InitializeSend. + +#define InitializeSend(_conn_) { \ + PIRP _irp_; \ + (_conn_)->SendState = CONNECTION_SENDSTATE_PACKETIZE; \ + _irp_ = CONTAINING_RECORD ((_conn_)->SendQueue.Flink, \ + IRP, \ + Tail.Overlay.ListEntry); \ + (_conn_)->FirstSendIrp = (_conn_)->sp.CurrentSendIrp = _irp_; \ + (_conn_)->FirstSendMdl = (_conn_)->sp.CurrentSendMdl = \ + _irp_->MdlAddress; \ + (_conn_)->FirstSendByteOffset = (_conn_)->sp.SendByteOffset = 0; \ + (_conn_)->sp.MessageBytesSent = 0; \ + (_conn_)->CurrentSendLength = \ + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(_irp_)); \ + (_conn_)->StallCount = 0; \ + (_conn_)->StallBytesSent = 0; \ + if ((_conn_)->NetbiosHeader.ResponseCorrelator == 0xffff) { \ + (_conn_)->NetbiosHeader.ResponseCorrelator = 1; \ + } else { \ + ++((_conn_)->NetbiosHeader.ResponseCorrelator); \ + } \ +} + +#endif + +// See SENDENG.C for the fully-commented description of +// StartPacketizingConnection. On a free build this is a +// macro for speed. + +#if DBG + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate + ); + +#else + +#define StartPacketizingConnection(_conn_,_immed_) { \ + PDEVICE_CONTEXT _devctx_; \ + _devctx_ = (_conn_)->Provider; \ + if (((_conn_)->SendState == CONNECTION_SENDSTATE_PACKETIZE) && \ + !((_conn_)->Flags & CONNECTION_FLAGS_PACKETIZE)) { \ + (_conn_)->Flags |= CONNECTION_FLAGS_PACKETIZE; \ + if (!(_immed_)) { \ + NbfReferenceConnection("Packetize", \ + (_conn_), \ + CREF_PACKETIZE_QUEUE); \ + } \ + ExInterlockedInsertTailList (&_devctx_->PacketizeQueue, \ + &(_conn_)->PacketizeLinkage, \ + &_devctx_->SpinLock); \ + RELEASE_DPC_SPIN_LOCK ((_conn_)->LinkSpinLock); \ + } else { \ + RELEASE_DPC_SPIN_LOCK ((_conn_)->LinkSpinLock); \ + if (_immed_) { \ + NbfDereferenceConnection("temp TdiSend", (_conn_), CREF_BY_ID); \ + } \ + } \ + if (_immed_) { \ + PacketizeConnections (_devctx_); \ + } \ +} + +#endif + +VOID +PacketizeConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection, + IN BOOLEAN Direct + ); + +BOOLEAN +ResendLlcPackets( + IN PTP_LINK Link, + IN UCHAR AckSequenceNumber, + IN BOOLEAN Resend + ); + +VOID +CompleteSend( + IN PTP_CONNECTION Connection, + IN USHORT Correlator + ); + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ); + +VOID +ReframeSend( + IN PTP_CONNECTION Connection, + IN ULONG BytesReceived + ); + +VOID +NbfCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +SendOnePacket( + IN PTP_CONNECTION Connection, + IN PTP_PACKET Packet, + IN BOOLEAN ForceAck, + OUT PBOOLEAN LinkCheckpoint OPTIONAL + ); + +VOID +SendControlPacket( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ); + +VOID +NbfNdisSend( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ); + +VOID +RestartLinkTraffic( + IN PTP_LINK Link + ); + +VOID +NbfSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN PDEVICE_CONTEXT DeviceContext, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ); + +// +// Routines in DEVCTX.C (TP_DEVCTX object manager). +// + +VOID +NbfRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +NbfCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ); + +VOID +NbfDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in ADDRESS.C (TP_ADDRESS object manager). +// + +#if DBG +VOID +NbfRefAddress( + IN PTP_ADDRESS Address + ); +#endif + +VOID +NbfDerefAddress( + IN PTP_ADDRESS Address + ); + +VOID +NbfAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ); + +VOID +NbfDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ); + +NTSTATUS +NbfCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ); + +VOID +NbfReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +NbfDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +NbfDestroyAddress( + IN PVOID Parameter + ); + +NTSTATUS +NbfOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +NbfCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfStopAddress( + IN PTP_ADDRESS Address + ); + +VOID +NbfRegisterAddress( + IN PTP_ADDRESS Address + ); + +BOOLEAN +NbfMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN UCHAR NameType, + IN PUCHAR NetBIOSName + ); + +VOID +NbfAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ); + +VOID +NbfDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ); + +NTSTATUS +NbfCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ); + +PTP_ADDRESS +NbfLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName + ); + +PTP_CONNECTION +NbfLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName, + IN UCHAR RemoteSessionNumber + ); + +NTSTATUS +NbfStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ); + +VOID +AddressTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbfParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk +); + +BOOLEAN +NbfValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength +); + +NTSTATUS +NbfVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ); + +NTSTATUS +NbfSendDatagramsOnAddress( + PTP_ADDRESS Address + ); + +// +// Routines in CONNECT.C. +// + +NTSTATUS +NbfTdiAccept( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiConnect( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiDisconnect( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiDisassociateAddress ( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiAssociateAddress( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiListen( + IN PIRP Irp + ); + +NTSTATUS +NbfOpenConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +NbfCloseConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +// +// +// Routines in CONNOBJ.C (TP_CONNECTION object manager). +// + +#if DBG +VOID +NbfRefConnection( + IN PTP_CONNECTION TransportConnection + ); +#endif + +VOID +NbfDerefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfClearConnectionLsn( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfStopConnection( + IN PTP_CONNECTION TransportConnection, + IN NTSTATUS Status + ); + +VOID +NbfCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbfStartConnectionTimer( + IN PTP_CONNECTION TransportConnection, + IN PKDEFERRED_ROUTINE TimeoutFunction, + IN ULONG WaitTime + ); + +PTP_CONNECTION +NbfLookupListeningConnection( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ); + +PTP_CONNECTION +NbfLookupConnectingConnection( + IN PTP_ADDRESS Address + ); + +VOID +NbfAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +VOID +NbfDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +NbfCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +PTP_CONNECTION +NbfLookupConnectionById( + IN PTP_ADDRESS Address, + IN USHORT ConnectionId + ); + +PTP_CONNECTION +NbfLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ); + +#if 0 +VOID +NbfWaitConnectionOnLink( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); +#endif + +VOID +ConnectionEstablishmentTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +NbfVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +NbfIndicateDisconnect( + IN PTP_CONNECTION TransportConnection + ); + +// +// Routines in INFO.C (QUERY_INFO manager). +// + +NTSTATUS +NbfTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +NTSTATUS +NbfTdiSetInformation( + IN PIRP Irp + ); + +VOID +NbfSendQueryFindName( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request + ); + +NTSTATUS +NbfProcessQueryNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Packet, + PNBF_HDR_CONNECTIONLESS UiFrame + ); + +VOID +NbfSendStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request, + IN PHARDWARE_ADDRESS DestinationAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +NTSTATUS +NbfProcessStatusResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +NTSTATUS +NbfProcessStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address OPTIONAL, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +// +// Routines in EVENT.C. +// + +NTSTATUS +NbfTdiSetEventHandler( + IN PIRP Irp + ); + +// +// Routines in REQUEST.C (TP_REQUEST object manager). +// + + +VOID +TdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +#if DBG +VOID +NbfRefRequest( + IN PTP_REQUEST Request + ); +#endif + +VOID +NbfDerefRequest( + IN PTP_REQUEST Request + ); + +VOID +NbfCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ); + +#if DBG +VOID +NbfRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +#if DBG +VOID +NbfRefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfDerefReceiveIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +#if DBG +VOID +NbfDerefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfCompleteReceiveIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +NbfAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ); + +VOID +NbfDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ); + +NTSTATUS +NbfCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ); + +// +// Routines in LINK.C (TP_LINK object manager). +// + +NTSTATUS +NbfDestroyLink( + IN PTP_LINK TransportLink + ); + +VOID +NbfDisconnectLink( + IN PTP_LINK Link + ); + +#if DBG +VOID +NbfRefLink( + IN PTP_LINK TransportLink + ); +#endif + +VOID +NbfDerefLink( + IN PTP_LINK TransportLink + ); + +VOID +NbfRefLinkSpecial( + IN PTP_LINK TransportLink + ); + +VOID +NbfDerefLinkSpecial( + IN PTP_LINK TransportLink + ); + +VOID +NbfResetLink( + IN PTP_LINK Link + ); + +VOID +NbfStopLink( + IN PTP_LINK Link + ); + +VOID +NbfCompleteLink( + IN PTP_LINK Link + ); + +VOID +NbfActivateLink( + IN PTP_LINK Link + ); + +VOID +NbfWaitLink( + IN PTP_LINK Link + ); + +BOOLEAN +NbfDisconnectFromLink( + IN PTP_CONNECTION TransportConnection, + IN BOOLEAN VerifyReferenceCount + ); + +NTSTATUS +NbfAssignGroupLsn( + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +NbfConnectToLink( + IN PTP_LINK Link, + IN PTP_CONNECTION TransportConnection + ); + +PTP_CONNECTION +NbfLookupPendingConnectOnLink( + IN PTP_LINK Link + ); + +PTP_CONNECTION +NbfLookupPendingListenOnLink( + IN PTP_LINK Link + ); + +VOID +NbfAllocateLink( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_LINK *TransportLink + ); + +VOID +NbfDeallocateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK TransportLink + ); + +NTSTATUS +NbfCreateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS HardwareAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN USHORT LoopbackLinkIndex, + OUT PTP_LINK *TransportLink + ); + +VOID +NbfDumpLinkInfo ( + IN PTP_LINK Link + ); + +// +// routines in linktree.c +// + + +NTSTATUS +NbfAddLinkToTree ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); + +NTSTATUS +NbfRemoveLinkFromTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); + +PTP_LINK +NbfFindLinkInTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ); + +PTP_LINK +NbfFindLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ); + +// +// Routines in DLC.C (LLC frame cracker, entrypoints from NDIS interface). +// + +VOID +NbfInsertInLoopbackQueue ( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET NdisPacket, + IN UCHAR LinkIndex + ); + +VOID +NbfProcessLoopbackQueue ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NDIS_STATUS +NbfReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +VOID +NbfGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PTP_LINK Link, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PDLC_FRAME DlcHeader, + IN UINT DlcSize, + IN BOOLEAN Loopback + ); + +VOID +NbfReceiveComplete ( + IN NDIS_HANDLE BindingContext + ); + +VOID +NbfProcessWanDelayedQueue( + IN PVOID Parameter + ); + +VOID +NbfTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + + +VOID +NbfTransferLoopbackData ( + OUT PNDIS_STATUS NdisStatus, + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + + +// +// Routines in UFRAMES.C, the UI-frame NBF frame processor. +// + +NTSTATUS +NbfIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Dsdu, + IN ULONG Length + ); + +NTSTATUS +NbfProcessUi( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR Header, + IN PUCHAR DlcHeader, + IN ULONG DlcLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ); + +// +// Routines in IFRAMES.C, the I-frame NBF frame processor. +// + +VOID +NbfAcknowledgeDataOnlyLast( + IN PTP_CONNECTION Connection, + IN ULONG MessageLength + ); + +VOID +NbfProcessIIndicate( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN UINT DlcTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Loopback + ); + +NTSTATUS +ProcessIndicateData( + IN PTP_CONNECTION Connection, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN PUCHAR DataHeader, + IN UINT DataTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last, + IN BOOLEAN Loopback + ); + +// +// Routines in RCV.C (data copying routines for receives). +// + +NTSTATUS +NbfTdiReceive( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiReceiveDatagram( + IN PIRP Irp + ); + +// +// Routines in FRAMESND.C, the UI-frame (non-link) shipper. +// + +VOID +NbfSendNameQuery( + IN PTP_CONNECTION Connection, + IN BOOLEAN SourceRoutingOptional + ); + +VOID +NbfSendNameRecognized( + IN PTP_ADDRESS Address, + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +VOID +NbfSendNameInConflict( + IN PTP_ADDRESS Address, + IN PUCHAR ConflictingName + ); + +NTSTATUS +NbfSendAddNameQuery( + IN PTP_ADDRESS Address + ); + +VOID +NbfSendSessionInitialize( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSessionConfirm( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSessionEnd( + IN PTP_CONNECTION Connection, + IN BOOLEAN Abort + ); + +VOID +NbfSendNoReceive( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendReceiveContinue( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendReceiveOutstanding( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendDataAck( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSabme( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendDisc( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendUa( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendDm( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendRr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +#if 0 + +// +// These functions are not currently called, so they are commented +// out. +// + +VOID +NbfSendRnr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendTest( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PMDL Psdu + ); + +VOID +NbfSendFrmr( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +#endif + +VOID +NbfSendXid( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendRej( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +NTSTATUS +NbfCreateConnectionlessFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *OuterFrame + ); + +VOID +NbfDestroyConnectionlessFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME RawFrame + ); + +VOID +NbfSendUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME RawFrame, + IN BOOLEAN Loopback + ); + +VOID +NbfSendUIMdlFrame( + IN PTP_ADDRESS Address + ); + +VOID +NbfSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +// +// Routines in FRAMECON.C, the NetBIOS Frames Protocol Frame Constructors. +// To understand the various constant parameters to these functions (such +// as special data1 & data2 values, see NBFCONST.H for details. +// + +VOID +ConstructAddGroupNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME GroupName // NetBIOS group name to be added. + ); + +VOID +ConstructAddNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME Name // NetBIOS name to be added. + ); + +VOID +ConstructNameInConflict( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ConflictingName, // NetBIOS name that is conflicting. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ); + +VOID +ConstructStatusQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN USHORT BufferLength, // length of user's status buffer. + IN USHORT Correlator, // correlator for STATUS_RESPONSE. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ); + +VOID +ConstructTerminateTrace( + IN PNBF_HDR_CONNECTIONLESS RawFrame // frame buffer to format. + ); + +VOID +ConstructDatagram( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructDatagramBroadcast( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session (0=FIND_NAME). + IN USHORT Correlator, // correlator in NAME_RECOGNIZED. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of sender. + ); + +VOID +ConstructAddNameResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN USHORT Correlator, // correlator from ADD_[GROUP_]NAME_QUERY. + IN PNAME Name // NetBIOS name being responded to. + ); + +VOID +ConstructNameRecognized( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN USHORT NameQueryCorrelator, // correlator from NAME_QUERY. + IN USHORT Correlator, // correlator expected from next response. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ); + +VOID +ConstructStatusResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN BOOLEAN Truncated, // data is truncated. + IN BOOLEAN DataOverflow, // too much data for user's buffer. + IN USHORT DataLength, // length of data sent. + IN USHORT Correlator, // correlator from STATUS_QUERY. + IN PNAME ReceivingPermanentName, // NetBIOS permanent node name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructDataAck( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_ONLY_LAST. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructDataOnlyLast( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN BOOLEAN Resynched, // TRUE if we are resynching. + IN USHORT Correlator, // correlator for RECEIVE_CONTINUE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionConfirm( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT Correlator, // correlator from SESSION_INITIALIZE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionEnd( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Reason, // reason for termination, defined below. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionInitialize( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT NameRecognizedCorrelator, // correlator from NAME_RECOGNIZED. + IN USHORT Correlator, // correlator for SESSION_CONFIRM. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructNoReceive( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Options, // option bitflags, defined below. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructReceiveOutstanding( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructReceiveContinue( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_FIRST_MIDDLE + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +#if 0 +VOID +ConstructSessionAlive( + IN PNBF_HDR_CONNECTION RawFrame // frame buffer to format. + ); +#endif + +// +// Routines in nbfndis.c. +// + +#if DBG +PUCHAR +NbfGetNdisStatus ( + IN NDIS_STATUS NdisStatus + ); +#endif + +// +// Routines in nbfdrvr.c +// + +VOID +NbfWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN ULONG BytesNeeded, + IN ULONG ResourceId + ); + +VOID +NbfWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbfWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +VOID +NbfFreeResources( + IN PDEVICE_CONTEXT DeviceContext + ); + + +extern +ULONG +NbfInitializeOneDeviceContext( + OUT PNDIS_STATUS NdisStatus, + IN PDRIVER_OBJECT DriverObject, + IN PCONFIG_DATA NbfConfig, + IN INT AdapterIndex + ); +// +// routines in nbfcnfg.c +// + +NTSTATUS +NbfConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigData + ); + +// +// Routines in nbfndis.c +// + +NTSTATUS +NbfRegisterProtocol ( + IN PUNICODE_STRING NameString + ); + +VOID +NbfDeregisterProtocol ( + VOID + ); + + +NTSTATUS +NbfInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA ConfigInfo, + IN UINT ConfigInfoNameIndex + ); + +VOID +NbfCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in action.c +// + +NTSTATUS +NbfTdiAction( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +VOID +NbfActionQueryIndication( + PDEVICE_CONTEXT DeviceContext, + PNBF_HDR_CONNECTIONLESS UiFrame + ); + +VOID +NbfActionDatagramIndication( + PDEVICE_CONTEXT DeviceContext, + PNBF_HDR_CONNECTIONLESS UiFrame, + ULONG Length + ); + +VOID +NbfStopControlChannel( + IN PDEVICE_CONTEXT DeviceContext, + IN USHORT ChannelIdentifier + ); + + +// +// Routines in nbfdebug.c +// + +#if DBG + +VOID +DisplayOneFrame( + PTP_PACKET Packet + ); + +VOID +NbfDisplayUIFrame( + PTP_UI_FRAME OuterFrame + ); + +VOID +NbfFormattedDump( + PCHAR far_p, + ULONG len + ); + +#endif + +#endif // def _NBFPROCS_ diff --git a/private/ntos/tdi/nbf/nbftypes.h b/private/ntos/tdi/nbf/nbftypes.h new file mode 100644 index 000000000..a72e9fe1b --- /dev/null +++ b/private/ntos/tdi/nbf/nbftypes.h @@ -0,0 +1,2202 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbftypes.h + +Abstract: + + This module defines private data structures and types for the NT + NBF transport provider. + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Revision History: + +--*/ + +#ifndef _NBFTYPES_ +#define _NBFTYPES_ + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +#define NETBIOS_NAME_SIZE 16 + +typedef struct _NBF_NETBIOS_ADDRESS { + UCHAR NetbiosName[NETBIOS_NAME_SIZE]; + USHORT NetbiosNameType; +} NBF_NETBIOS_ADDRESS, *PNBF_NETBIOS_ADDRESS; + +typedef UCHAR NAME; +typedef NAME UNALIGNED *PNAME; + + +// +// This structure defines things associated with a TP_REQUEST, or outstanding +// TDI request, maintained on a queue somewhere in the transport. All +// requests other than open/close require that a TP_REQUEST block be built. +// + +#if DBG +#define REQUEST_HISTORY_LENGTH 20 +extern KSPIN_LOCK NbfGlobalInterlock; +#endif + +// +// the types of potential owners of requests +// + +typedef enum _REQUEST_OWNER { + ConnectionType, + AddressType, + DeviceContextType +} REQUEST_OWNER; + +//typedef +//NTSTATUS +//(*PTDI_TIMEOUT_ACTION)( +// IN PTP_REQUEST Request +// ); + +// +// The request itself +// + +#if DBG +#define RREF_CREATION 0 +#define RREF_PACKET 1 +#define RREF_TIMER 2 +#define RREF_RECEIVE 3 +#define RREF_FIND_NAME 4 +#define RREF_STATUS 5 + +#define NUMBER_OF_RREFS 8 +#endif + +typedef struct _TP_REQUEST { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + LIST_ENTRY Linkage; // used by ExInterlocked routines. + KSPIN_LOCK SpinLock; // spinlock for other fields. + // (used in KeAcquireSpinLock calls) +#if DBG + LONG RefTypes[NUMBER_OF_RREFS]; +#endif + LONG ReferenceCount; // reasons why we can't destroy this req. + + struct _DEVICE_CONTEXT *Provider; // pointer to the device context. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock. + + PIRP IoRequestPacket; // pointer to IRP for this request. + + // + // The following two fields are used to quickly reference the basic + // components of the requests without worming through the IRP's stack. + // + + PVOID Buffer2; // second buffer in the request. + ULONG Buffer2Length; // length of the second buffer. + + // + // The following two fields (Flags and Context) are used to clean up + // queued requests which must be canceled or abnormally completed. + // The Flags field contains bitflags indicating the state of the request, + // and the specific queue type that the request is located on. The + // Context field contains a pointer to the owning structure (TP_CONNECTION + // or TP_ADDRESS) so that the cleanup routines can perform post-cleanup + // operations on the owning structure, such as dereferencing, etc. + // + + ULONG Flags; // disposition of this request. + PVOID Context; // context of this request. + REQUEST_OWNER Owner; // what type of owner this request has. + +#if DBG + LARGE_INTEGER Time; // time when request created +#endif + + KTIMER Timer; // kernel timer for this request. + KDPC Dpc; // DPC object for timeouts. + + // + // These fields are used for FIND.NAME and STATUS.QUERY requests. + // + + ULONG Retries; // timeouts remaining. + USHORT BytesWritten; // usage varies. + USHORT FrameContext; // identifies request. + PVOID ResponseBuffer; // temp alloc to hold data. + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[REQUEST_HISTORY_LENGTH]; + BOOLEAN Completed; + BOOLEAN Destroyed; +#endif + +} TP_REQUEST, *PTP_REQUEST; + +#ifdef _PNP_POWER +// +// in nbfdrvr.c +// + +extern UNICODE_STRING NbfRegistryPath; + +// +// We need the driver object to create device context structures. +// + +extern PDRIVER_OBJECT NbfDriverObject; + +#endif // _PNP_POWER +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalRequestList; +#define StoreRequestHistory(_req,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_req)->Destroyed) { \ + DbgPrint ("request touched after being destroyed 0x%lx\n", \ + (_req)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_req)->History[(_req)->NextRefLoc].Caller, \ + &(_req)->History[(_req)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_req)->TotalReferences++; \ + } else { \ + (_req)->TotalDereferences++; \ + (_req)->History[(_req)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_req)->History[(_req)->NextRefLoc].Caller & \ + ~0x80000000); \ + } \ + if (++(_req)->NextRefLoc == REQUEST_HISTORY_LENGTH) { \ + (_req)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define NBF_ALLOCATION_TYPE_REQUEST 1 + +#define REQUEST_FLAGS_TIMER 0x0001 // a timer is active for this request. +#define REQUEST_FLAGS_TIMED_OUT 0x0002 // a timer expiration occured on this request. +#define REQUEST_FLAGS_ADDRESS 0x0004 // request is attached to a TP_ADDRESS. +#define REQUEST_FLAGS_CONNECTION 0x0008 // request is attached to a TP_CONNECTION. +#define REQUEST_FLAGS_STOPPING 0x0010 // request is being killed. +#define REQUEST_FLAGS_EOR 0x0020 // TdiSend request has END_OF_RECORD mark. +#define REQUEST_FLAGS_PIGGYBACK 0x0040 // TdiSend that can be piggyback ack'ed. +#define REQUEST_FLAGS_DC 0x0080 // request is attached to a TP_DEVICE_CONTEXT + +// +// This defines the TP_SEND_IRP_PARAMETERS, which is masked onto the +// Parameters section of a send IRP's stack location. +// + +typedef struct _TP_SEND_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_SEND Request; + LONG ReferenceCount; + PVOID Irp; +} TP_SEND_IRP_PARAMETERS, *PTP_SEND_IRP_PARAMETERS; + +#define IRP_SEND_LENGTH(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendLength) + +#define IRP_SEND_FLAGS(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendFlags) + +#define IRP_SEND_REFCOUNT(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_SEND_IRP(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Irp) + +#define IRP_SEND_CONNECTION(_IrpSp) \ + ((PTP_CONNECTION)((_IrpSp)->FileObject->FsContext)) + +#define IRP_DEVICE_CONTEXT(_IrpSp) \ + ((PDEVICE_CONTEXT)((_IrpSp)->DeviceObject)) + + +// +// This defines the TP_RECEIVE_IRP_PARAMETERS, which is masked onto the +// Parameters section of a receive IRP's stack location. +// + +typedef struct _TP_RECEIVE_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_RECEIVE Request; + LONG ReferenceCount; + PIRP Irp; +} TP_RECEIVE_IRP_PARAMETERS, *PTP_RECEIVE_IRP_PARAMETERS; + +#define IRP_RECEIVE_LENGTH(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.ReceiveLength) + +#define IRP_RECEIVE_FLAGS(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.ReceiveFlags) + +#define IRP_RECEIVE_REFCOUNT(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_RECEIVE_IRP(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Irp) + +#define IRP_RECEIVE_CONNECTION(_IrpSp) \ + ((PTP_CONNECTION)((_IrpSp)->FileObject->FsContext)) + + + +// +// This structure defines a TP_UI_FRAME, or connectionless frame header, +// that is manipulated by the FRAME.C routines. +// + +typedef struct _TP_UI_FRAME { + PNDIS_PACKET NdisPacket; + LIST_ENTRY Linkage; // used by ExInterLocked routines. + PVOID DataBuffer; // for transport-created data. + UCHAR Header[1]; // the header in the frame (MAC + DLC + NBF) +} TP_UI_FRAME, *PTP_UI_FRAME; + + +// +// This structure defines a TP_VARIABLE, or network managable variable, +// maintained in a linked list on the device context. +// + +typedef struct _TP_VARIABLE { + + struct _TP_VARIABLE *Fwdlink; // next variable in provider's chain. + + ULONG VariableSerialNumber; // identifier for this variable. + ULONG VariableType; // type of this variable (see TDI.H). + STRING VariableName; // allocated variable name. + + union { + ULONG LongValue; + HARDWARE_ADDRESS HardwareAddressValue; + STRING StringValue; // allocated string value, if of that type. + } Value; + +} TP_VARIABLE, *PTP_VARIABLE; + + +// +// This structure defines a TP_CONNECTION, or active transport connection, +// maintained on a transport address. +// + +#if DBG +#define CONNECTION_HISTORY_LENGTH 50 + +#define CREF_SPECIAL_CREATION 0 +#define CREF_SPECIAL_TEMP 1 +#define CREF_COMPLETE_SEND 2 +#define CREF_SEND_IRP 3 +#define CREF_ADM_SESS 4 +#define CREF_TRANSFER_DATA 5 +#define CREF_FRAME_SEND 6 +#define CREF_TIMER 7 +#define CREF_BY_ID 8 +#define CREF_LINK 9 +#define CREF_SESSION_END 10 +#define CREF_LISTENING 11 +#define CREF_P_LINK 12 +#define CREF_P_CONNECT 13 +#define CREF_PACKETIZE 14 +#define CREF_RECEIVE_IRP 15 +#define CREF_PROCESS_DATA 16 +#define CREF_REQUEST 17 +#define CREF_TEMP 18 +#define CREF_DATA_ACK_QUEUE 19 +#define CREF_ASSOCIATE 20 +#define CREF_STOP_ADDRESS 21 +#define CREF_PACKETIZE_QUEUE 22 +#define CREF_STALLED 23 + +#define NUMBER_OF_CREFS 24 +#endif + +// +// This structure holds our "complex send pointer" indicating +// where we are in the packetization of a send. +// + +typedef struct _TP_SEND_POINTER { + ULONG MessageBytesSent; // up count, bytes sent/this msg. + PIRP CurrentSendIrp; // ptr, current send request in chain. + PMDL CurrentSendMdl; // ptr, current MDL in send chain. + ULONG SendByteOffset; // current byte offset in current MDL. +} TP_SEND_POINTER, *PTP_SEND_POINTER; + +typedef struct _TP_CONNECTION { + +#if DBG + ULONG RefTypes[NUMBER_OF_CREFS]; +#endif + +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + ULONG Padding; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif + + CSHORT Type; + USHORT Size; + + LIST_ENTRY LinkList; // used for link thread or for free + // resource list + KSPIN_LOCK SpinLock; // spinlock for connection protection. + PKSPIN_LOCK LinkSpinLock; // pointer to link's spinlock + + LONG ReferenceCount; // number of references to this object. + LONG SpecialRefCount; // controls freeing of connection. + + // + // The following lists are used to associate this connection with a + // particular address. + // + + LIST_ENTRY AddressList; // list of connections for given address + LIST_ENTRY AddressFileList; // list for connections bound to a + // given address reference + + // + // The following field is used as linkage in the device context's + // PacketizeQueue + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device context's + // PacketWaitQueue. + // + + LIST_ENTRY PacketWaitLinkage; + + // + // The following field points to the TP_LINK object that describes the + // (active) data link connection for this transport connection. To be + // valid, this field is non-NULL. + // + + struct _TP_LINK *Link; // pointer to transport link object. + struct _TP_ADDRESS_FILE *AddressFile; // pointer to owning Address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock + PFILE_OBJECT FileObject; // easy backlink to file object. + + // + // The following field contains the actual ID we expose to the TDI client + // to represent this connection. A unique one is created from the address. + // + + USHORT ConnectionId; // unique identifier. + UCHAR SessionNumber; // the session number used in the packet header + + // + // This field is used to keep the reason for the connection disconnect + // around until connection deletion time. + // + + BOOLEAN RemoteDisconnect; // was this connection remotely disonnected? + + // + // The following field is specified by the user at connection open time. + // It is the context that the user associates with the connection so that + // indications to and from the client can be associated with a particular + // connection. + // + + CONNECTION_CONTEXT Context; // client-specified value. + + // + // The following two queues are used to associate TdiSend and TdiReceive + // IRPs with this connection. New arrivals are placed at the end of + // the queues (really a linked list) and IRPs are processed at the + // front of the queues. The first TdiSend IRP on the SendQueue is + // the current TdiSend being processed, and the first TdiReceive IRP + // on the ReceiveQueue is the first TdiReceive being processed, PROVIDED + // the CONNECTION_FLAGS_ACTIVE_RECEIVE flag is set. If this flag is not + // set, then the first TdiReceive IRP on the ReceiveQueue is not active. + // These queues are managed by the EXECUTIVE interlocked list manipuation + // routines. + // + + LIST_ENTRY SendQueue; // FIFO of outstanding TdiSends. + LIST_ENTRY ReceiveQueue; // FIFO of outstanding TdiReceives. + + // + // The following fields are used to maintain state for the current receive. + // + + ULONG MessageBytesReceived; // up count, bytes recd/this msg. + ULONG MessageBytesAcked; // bytes acked (NR or RO) this msg. + ULONG MessageInitAccepted; // bytes accepted during indication. + + // + // These fields are only valid if the CONNECTION_FLAGS_ACTIVE_RECEIVE + // flag is set. + // + + PIRP SpecialReceiveIrp; // a "no-request" receive IRP exists. + PIRP CurrentReceiveIrp; // ptr, current receive IRP. + PMDL CurrentReceiveMdl; // ptr, current MDL in receive chain. + ULONG ReceiveByteOffset; // current byte offset in current MDL. + ULONG ReceiveLength; // current receive length, in bytes (total) + ULONG ReceiveBytesUnaccepted; // by client...only indicate when == 0 + + // + // The following fields are used to maintain state for the active send. + // They only have meaning if the connection's SendState is not IDLE. + // Because the TDI client may submit multiple TdiSend requests to comprise + // a full message, we have to keep a complex pointer to the first byte of + // unACKed data (hence the first three fields). We also have a complex + // pointer to the first byte of unsent data (hence the last three fields). + // + + ULONG SendState; // send state machine variable. + + PIRP FirstSendIrp; // ptr, 1st TdiSend's IRP. + PMDL FirstSendMdl; // ptr, 1st unacked MDL in chain/this msg. + ULONG FirstSendByteOffset; // pre-acked bytes in that MDL. + + TP_SEND_POINTER sp; // current send loc, defined above. + ULONG CurrentSendLength; // how long is this send (total) + ULONG StallCount; // times in a row we looked stalled. + ULONG StallBytesSent; // bytes sent last time we checked. + + // + // This is TRUE if we need don't need to reference the current + // receive IRP during transfers (because it is a special + // receive or the driver doesn't pend transfers). + // + + BOOLEAN CurrentReceiveSynchronous; + + // + // This field will be TRUE if the last DOL received allowed + // piggyback acks. + // + + BOOLEAN CurrentReceiveAckQueueable; + + // + // + // This field will be TRUE if the last DOL received was + // sent NO.ACK. + // + + BOOLEAN CurrentReceiveNoAck; + + // + // These fields handle asynchronous TransferData calls. + // + + ULONG TransferBytesPending; // bytes pending in current transfers + ULONG TotalTransferBytesPending; // bytes since TransferBytesPending was 0; + // how much we back off if a transfer fails + PMDL SavedCurrentReceiveMdl; // used to back off by TotalTransferPending bytes + ULONG SavedReceiveByteOffset; // used to back off by TotalTransferPending bytes + + // + // This field will be TRUE if we are in the middle of + // processing a receive indication on this connection and + // we are not yet in a state where another indication + // can be handled. + // + // It is stored as a INT since access to it is guarded + // by the connection's link spinlock, unlike the variables + // around it (BUGBUG: What about Alpha?) + // + + UINT IndicationInProgress; + + // + // The following field is used as a linkage when on the device + // context's DataAckQueue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if the connection is on the data ack queue. + // Also an INT so access can be non-guarded. + // + + UINT OnDataAckQueue; + + // + // These keep track of the number of consecutive sends or + // receives on this connection. This is used in determining when + // to queue a data ack. + // + + ULONG ConsecutiveSends; + ULONG ConsecutiveReceives; + + // + // The following list head is used as a pointer to a TdiListen/TdiConnect + // request which is in progress. Although manipulated + // with queue instructions, there will only be one request in the queue. + // This is done for consistency with respect to TpCreateRequest, which + // does a great job of creating a request and associating it atomically + // with a supervisory object. + // + + LIST_ENTRY InProgressRequest; // TdiListen/TdiConnect + + // + // If the connection is being disconnected as a result of + // a TdiDisconnect call (RemoteDisconnect is FALSE) then this + // will hold the IRP passed to TdiDisconnect. It is needed + // when the TdiDisconnect request is completed. + // + + PIRP DisconnectIrp; + + // + // If the connection is being closed, this will hold + // the IRP passed to TdiCloseConnection. It is needed + // when the request is completed. + // + + PIRP CloseIrp; + + // + // These fields are used for deferred operations on connections; the only + // deferred operation currently supported is piggyback ACK + // + + ULONG DeferredFlags; +#if DBG + ULONG DeferredPasses; +#endif + LIST_ENTRY DeferredQueue; + + // + // The following fields are used for connection housekeeping. + // + + ULONG Flags; // attributes guarded by LinkSpinLock + ULONG Flags2; // attributes guarded by SpinLock + UINT OnPacketWaitQueue; // TRUE if on PacketWaitQueue + UCHAR Lsn; // local session number (1-254). + UCHAR Rsn; // remote session number (1-254). + USHORT Retries; // retry limit for NAME_QUERY shipments. + KTIMER Timer; // kernel timer for timeouts on NQ/NR. + LARGE_INTEGER ConnectStartTime; // when we sent the committed NQ. + KDPC Dpc; // DPC object for timeouts. + NTSTATUS Status; // status code for connection rundown. + ULONG LastPacketsSent; // The value that was in Link->XXX the + ULONG LastPacketsResent; // last time we calculated the throughput. + NBF_NETBIOS_ADDRESS CalledAddress; // TdiConnect request's T.A. + USHORT MaximumDataSize; // maximum I-frame data size for NBF. + + NBF_HDR_CONNECTION NetbiosHeader; // pre-built Netbios header; we store + // the current send and reply correlators + // in the appropriate spots in this. + + // + // These are for CONNECTION_INFO queries. + // + + ULONG TransmittedTsdus; // TSDUs sent on this connection. + ULONG ReceivedTsdus; // TSDUs received on this connection. + ULONG TransmissionErrors; // TSDUs transmitted in error/this connection. + ULONG ReceiveErrors; // TSDUs received in error/this connection. + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + // TDI_CONNECTION_INFO Information; // information about this connection. + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[CONNECTION_HISTORY_LENGTH]; + BOOLEAN Destroyed; +#endif + CHAR RemoteName[16]; + +} TP_CONNECTION, *PTP_CONNECTION; + +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalConnectionList; +#define StoreConnectionHistory(_conn,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_conn)->Destroyed) { \ + DbgPrint ("connection touched after being destroyed 0x%lx\n", \ + (_conn)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_conn)->History[(_conn)->NextRefLoc].Caller, \ + &(_conn)->History[(_conn)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_conn)->TotalReferences++; \ + } else { \ + (_conn)->TotalDereferences++; \ + (_conn)->History[(_conn)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_conn)->History[(_conn)->NextRefLoc].Caller | 1); \ + } \ + if (++(_conn)->NextRefLoc == CONNECTION_HISTORY_LENGTH) { \ + (_conn)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define CONNECTION_FLAGS_VERSION2 0x00000001 // remote netbios is version 2.0. +#define CONNECTION_FLAGS_RECEIVE_WAKEUP 0x00000002 // send a RECEIVE_OUTSTANDING when a receive arrives. +#define CONNECTION_FLAGS_ACTIVE_RECEIVE 0x00000004 // a receive is active. +#define CONNECTION_FLAGS_WAIT_SI 0x00000020 // waiting for a SESSION_INITIALIZE. +#define CONNECTION_FLAGS_WAIT_SC 0x00000040 // waiting for a SESSION_CONFIRM. +#define CONNECTION_FLAGS_WAIT_LINK_UP 0x00000080 // waiting for DDI to est. connection. +#define CONNECTION_FLAGS_READY 0x00000200 // sends/rcvs/discons valid. +#define CONNECTION_FLAGS_RC_PENDING 0x00001000 // a receive is pending completion +#define CONNECTION_FLAGS_W_PACKETIZE 0x00002000 // w/for a packet to packetize. +#define CONNECTION_FLAGS_PACKETIZE 0x00004000 // we're on the PacketizeQueue. +#define CONNECTION_FLAGS_W_RESYNCH 0x00008000 // waiting for resynch indicator. (receive) +#define CONNECTION_FLAGS_SEND_SI 0x00010000 // w/for a packet to send SI. +#define CONNECTION_FLAGS_SEND_SC 0x00020000 // w/for a packet to send SC. +#define CONNECTION_FLAGS_SEND_DA 0x00040000 // w/for a packet to send DA. +#define CONNECTION_FLAGS_SEND_RO 0x00080000 // w/for a packet to send RO. +#define CONNECTION_FLAGS_SEND_RC 0x00100000 // w/for a packet to send RC. +#define CONNECTION_FLAGS_SEND_SE 0x00200000 // w/for a packet to send SE. +#define CONNECTION_FLAGS_SEND_NR 0x00400000 // w/for a packet to send NR. +#define CONNECTION_FLAGS_NO_INDICATE 0x00800000 // don't take packets at indication time +#define CONNECTION_FLAGS_FAILING_TO_EOR 0x01000000 // wait for an EOF in an incoming request before sending +#define CONNECTION_FLAGS_RESYNCHING 0x02000000 // engaged send side resynch +#define CONNECTION_FLAGS_RCV_CANCELLED 0x10000000 // current receive was cancelled +#define CONNECTION_FLAGS_PEND_INDICATE 0x20000000 // new data received during RC_PENDING +#define CONNECTION_FLAGS_TRANSFER_FAIL 0x40000000 // a transfer data call failed + +#define CONNECTION_FLAGS2_STOPPING 0x00000001 // connection is running down. +#define CONNECTION_FLAGS2_WAIT_NR 0x00000002 // waiting for NAME_RECOGNIZED. +#define CONNECTION_FLAGS2_WAIT_NQ 0x00000004 // waiting for NAME_QUERY. +#define CONNECTION_FLAGS2_WAIT_NR_FN 0x00000008 // waiting for FIND NAME response. +#define CONNECTION_FLAGS2_CLOSING 0x00000010 // connection is closing +#define CONNECTION_FLAGS2_ASSOCIATED 0x00000020 // associated with address +#define CONNECTION_FLAGS2_DISCONNECT 0x00000040 // disconnect done on connection +#define CONNECTION_FLAGS2_ACCEPTED 0x00000080 // accept done on connection +#define CONNECTION_FLAGS2_REQ_COMPLETED 0x00000100 // Listen/Connect request completed. +#define CONNECTION_FLAGS2_DISASSOCIATED 0x00000200 // associate CRef has been removed +#define CONNECTION_FLAGS2_DISCONNECTED 0x00000400 // disconnect has been indicated +#define CONNECTION_FLAGS2_NO_LISTEN 0x00000800 // no_listen received during setup +#define CONNECTION_FLAGS2_REMOTE_VALID 0x00001000 // Connection->RemoteName is valid +#define CONNECTION_FLAGS2_GROUP_LSN 0x00002000 // connection LSN is globally assigned +#define CONNECTION_FLAGS2_W_ADDRESS 0x00004000 // waiting for address reregistration. +#define CONNECTION_FLAGS2_PRE_ACCEPT 0x00008000 // no TdiAccept after listen completes +#define CONNECTION_FLAGS2_ABORT 0x00010000 // abort this connection. +#define CONNECTION_FLAGS2_ORDREL 0x00020000 // we're in orderly release. +#define CONNECTION_FLAGS2_DESTROY 0x00040000 // destroy this connection. +#define CONNECTION_FLAGS2_LISTENER 0x00100000 // we were the passive listener. +#define CONNECTION_FLAGS2_CONNECTOR 0x00200000 // we were the active connector. +#define CONNECTION_FLAGS2_WAITING_SC 0x00400000 // the connection is waiting for + // and accept to send the + // session confirm +#define CONNECTION_FLAGS2_INDICATING 0x00800000 // connection was manipulated while + // indication was in progress + +#define CONNECTION_FLAGS2_LDISC 0x01000000 // Local disconnect req. +#ifdef RASAUTODIAL +#define CONNECTION_FLAGS2_AUTOCONNECTING 0x02000000 // RAS autodial in progress +#define CONNECTION_FLAGS2_AUTOCONNECTED 0x04000000 // RAS autodial done +#endif // RASAUTODIAL + +#define CONNECTION_FLAGS_STARVED ( \ + CONNECTION_FLAGS_SEND_SI | \ + CONNECTION_FLAGS_SEND_SC | \ + CONNECTION_FLAGS_SEND_DA | \ + CONNECTION_FLAGS_SEND_RO | \ + CONNECTION_FLAGS_SEND_RC | \ + CONNECTION_FLAGS_SEND_NR | \ + CONNECTION_FLAGS_SEND_SE \ + ) + +#define CONNECTION_FLAGS_DEFERRED_ACK 0x00000001 // send piggyback ack first opportunity +#define CONNECTION_FLAGS_DEFERRED_ACK_2 0x00000002 // deferred ack wasn't sent +#define CONNECTION_FLAGS_DEFERRED_NOT_Q 0x00000004 // DEFERRED_ACK set, but not on DataAckQueue +#define CONNECTION_FLAGS_DEFERRED_SENDS 0x80000000 // print completed sends + +#define CONNECTION_SENDSTATE_IDLE 0 // no sends being processed. +#define CONNECTION_SENDSTATE_PACKETIZE 1 // send being packetized. +#define CONNECTION_SENDSTATE_W_PACKET 2 // waiting for free packet. +#define CONNECTION_SENDSTATE_W_LINK 3 // waiting for good link conditions. +#define CONNECTION_SENDSTATE_W_EOR 4 // waiting for TdiSend(EOR). +#define CONNECTION_SENDSTATE_W_ACK 5 // waiting for DATA_ACK. +#define CONNECTION_SENDSTATE_W_RCVCONT 6 // waiting for RECEIVE_CONTINUE. + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to a TP_ADDRESS +// structure, which describes the address that it is bound to. Thus, a +// connection will point to this structure, which describes the address the +// connection was associated with. When the address file closes, all connections +// opened on this address file get closed, too. Note that this may leave an +// address hanging around, with other references. +// + +typedef struct _TP_ADDRESS_FILE { + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + LONG ReferenceCount; // number of references to this object. + + // + // This structure is edited after taking the Address spinlock for the + // owning address. This ensures that the address and this structure + // will never get out of syncronization with each other. + // + + // + // The following field points to a list of TP_CONNECTION structures, + // one per connection open on this address. This list of connections + // is used to help the cleanup process if a process closes an address + // before disassociating all connections on it. By design, connections + // will stay around until they are explicitly + // closed; we use this database to ensure that we clean up properly. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + // + // The following fields are kept for housekeeping purposes. + // + + PIRP Irp; // the irp used for open or close + struct _TP_ADDRESS *Address; // address to which we are bound. + PFILE_OBJECT FileObject; // easy backlink to file object. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the Irp used to close this address file, + // for pended completion. + // + + PIRP CloseIrp; + + // + // is this address file currently indicating a connection request? if yes, we + // need to mark connections that are manipulated during this time. + // + + BOOLEAN ConnectIndicationInProgress; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredConnectionHandler; + BOOLEAN RegisteredDisconnectHandler; + BOOLEAN RegisteredReceiveHandler; + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredExpeditedDataHandler; + BOOLEAN RegisteredErrorHandler; + + // + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + // + // + + PTDI_IND_CONNECT ConnectionHandler; + PVOID ConnectionHandlerContext; + + // + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_DISCONNECT DisconnectHandler; + PVOID DisconnectHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. If the NULL handler + // is specified in a TdiSetEventHandler, then this points to an internal + // routine which does not accept the incoming data. + // + + PTDI_IND_RECEIVE ReceiveHandler; + PVOID ReceiveHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + + // + // An expedited data handler. This handler is used if expedited data is + // expected; it never is in NBF, thus this handler should always point to + // the default handler. + // + + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + PVOID ExpeditedDataHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + PVOID ErrorHandlerOwner; + + +} TP_ADDRESS_FILE, *PTP_ADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a TP_ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#if DBG +#define AREF_TIMER 0 +#define AREF_TEMP_CREATE 1 +#define AREF_OPEN 2 +#define AREF_VERIFY 3 +#define AREF_LOOKUP 4 +#define AREF_FRAME_SEND 5 +#define AREF_CONNECTION 6 +#define AREF_TEMP_STOP 7 +#define AREF_REQUEST 8 +#define AREF_PROCESS_UI 9 +#define AREF_PROCESS_DATAGRAM 10 +#define AREF_TIMER_SCAN 11 + +#define NUMBER_OF_AREFS 12 +#endif + +typedef struct _TP_ADDRESS { + +#if DBG + ULONG RefTypes[NUMBER_OF_AREFS]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + LONG ReferenceCount; // number of references to this object. + + // + // The following spin lock is acquired to edit this TP_ADDRESS structure + // or to scan down or edit the list of address files. + // + + KSPIN_LOCK SpinLock; // lock to manipulate this structure. + + // + // The following fields comprise the actual address itself. + // + + PIRP Irp; // pointer to address creation IRP. + PNBF_NETBIOS_ADDRESS NetworkName; // this address + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY SendDatagramQueue; // FIFO of outstanding TdiSendDatagrams. + + // + // The following field points to a list of TP_CONNECTION structures, + // one per active, connecting, or disconnecting connections on this + // address. By definition, if a connection is on this list, then + // it is visible to the client in terms of receiving events and being + // able to post requests by naming the ConnectionId. If the connection + // is not on this list, then it is not valid, and it is guaranteed that + // no indications to the client will be made with reference to it, and + // no requests specifying its ConnectionId will be accepted by the transport. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // The packet pool of size 1 that holds the UI frame, and the + // frame that is allocated out of it. + // + + NDIS_HANDLE UIFramePoolHandle; + PTP_UI_FRAME UIFrame; // DLC-UI/NBF header for datagram sends. + + // + // The following fields are used to register this address on the network. + // + + ULONG Retries; // retries of ADD_NAME_QUERY left to go. + KTIMER Timer; // kernel timer for timeouts on ANQ/ANR. + KDPC Dpc; // DPC object for timeout. + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + + // + // If we get an ADD_NAME_RESPONSE frame, this holds the address + // of the remote we got it from (used to check for duplicate names). + // + + UCHAR UniqueResponseAddress[6]; + + // + // Set to TRUE once we send a name in conflict frame, so that + // we don't flood the network with them on every response. + // + + BOOLEAN NameInConflictSent; + +} TP_ADDRESS, *PTP_ADDRESS; + +#define ADDRESS_FLAGS_GROUP 0x00000001 // set if group, otherwise unique. +#define ADDRESS_FLAGS_CONFLICT 0x00000002 // address in conflict detected. +#define ADDRESS_FLAGS_REGISTERING 0x00000004 // registration in progress. +#define ADDRESS_FLAGS_DEREGISTERING 0x00000008 // deregistration in progress. +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000010 // duplicate name was found on net. +#define ADDRESS_FLAGS_NEEDS_REG 0x00000020 // address must be registered. +#define ADDRESS_FLAGS_STOPPING 0x00000040 // TpStopAddress is in progress. +#define ADDRESS_FLAGS_BAD_ADDRESS 0x00000080 // name in conflict on associated address. +#define ADDRESS_FLAGS_SEND_IN_PROGRESS 0x00000100 // send datagram process active. +#define ADDRESS_FLAGS_CLOSED 0x00000200 // address has been closed; + // existing activity can + // complete, nothing new can start +#define ADDRESS_FLAGS_NEED_REREGISTER 0x00000400 // quick-reregister on next connect. +#define ADDRESS_FLAGS_QUICK_REREGISTER 0x00000800 // address is quick-reregistering. + + +// +// This structure defines a TP_LINK, or established data link object, +// maintained by the transport provider. Each data link connection with +// a remote machine is represented by this object. Zero, one, or several +// transport connections can be multiplexed over the same data link connection. +// This object is managed by routines in LINK.C. +// + +#if DBG +#define LREF_SPECIAL_CONN 0 +#define LREF_SPECIAL_TEMP 1 +#define LREF_CONNECTION 2 +#define LREF_STOPPING 3 +#define LREF_START_T1 4 +#define LREF_TREE 5 +#define LREF_NOT_ADM 6 +#define LREF_NDIS_SEND 7 + +#define NUMBER_OF_LREFS 8 +#endif + +#if DBG +#define LINK_HISTORY_LENGTH 20 +#endif + +typedef struct _TP_LINK { + + RTL_SPLAY_LINKS SplayLinks; // for the link splay tree + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + ULONG RefTypes[NUMBER_OF_LREFS]; +#endif + + LIST_ENTRY Linkage; // for list of free links or deferred + // operation queue + KSPIN_LOCK SpinLock; // lock to manipulate this structure. + + LONG ReferenceCount; // number of references to this object. + LONG SpecialRefCount; // controls freeing of the link. + + // + // information about the remote hardware this link is talking to. + // + + BOOLEAN Loopback; // TRUE if this is a loopback link. + UCHAR LoopbackDestinationIndex; // if Loopback, the index. + + HARDWARE_ADDRESS HardwareAddress; // hardware address of remote. + ULARGE_INTEGER MagicAddress; // numerical representation of the + // hardware address used for quick + // comparisons + UCHAR Header[MAX_MAC_HEADER_LENGTH]; // a place to stick a prebuilt packet + // header. + ULONG HeaderLength; // length of Header for this link + + // + // Vital conditions surrounding the data link connnection. + // + + ULONG MaxFrameSize; // maximum size of NetBIOS frame, MAC + // dependent. + + // + // Connections associated with this link. We keep a simple list of + // connections because it's unlikely we'll get more than a few connections + // on a given link (we're assuming that the server or redir will be the + // biggest user of the net in the vast majority of environments). We've + // made the link lookup be via a splay tree, which vastly speeds the + // process of getting to the proper link; as long as there are only a few + // connections, the connection lookup will be fast. If this becomes a + // problem down the road, we can make this connection list be a splay tree + // also. + // + + LIST_ENTRY ConnectionDatabase; + ULONG ActiveConnectionCount; // # connections in above list. + + // + // The following fields are used to maintain state about this link. + // One other field is implicit-- the address of this object is the + // ConnectionContext value as described in the PDI spec. + // + + ULONG Flags; // attributes of the link. + ULONG DeferredFlags; // when on the deferred queue. + ULONG State; // link state variable. + + // + // Send-side state. + // + + ULONG PacketsSent; // number of packets sent. + ULONG PacketsResent; // number of packets resent. + UCHAR SendState; // send-side state variable. + UCHAR NextSend; // next N(S) we should send. + UCHAR LastAckReceived; // last N(R) we received. + UCHAR SendWindowSize; // current send window size. + UCHAR PrevWindowSize; // size last time we dropped a frame. + UCHAR WindowsUntilIncrease; // how many windows until size increases. + UCHAR SendRetries; // number of retries left/this checkpoint. + UCHAR ConsecutiveLastPacketLost; // consecutive windows with last packet dropped. + ULONG NdisSendsInProgress; // >0 if sends queued to NdisSendQueue. + LIST_ENTRY NdisSendQueue; // queue of sends to pass to NdisSend. + LIST_ENTRY WackQ; // sent packets waiting LLC acks. + + BOOLEAN OnDeferredRrQueue; + LIST_ENTRY DeferredRrLinkage; + + // + // Receive-side state. + // + + ULONG PacketsReceived; // number of packets received. + UCHAR ReceiveState; // receive-side state variable. + UCHAR NextReceive; // next expected N(S) we should receive. + UCHAR LastAckSent; // last N(R) we sent. + UCHAR ReceiveWindowSize; // current receive window size. + BOOLEAN RespondToPoll; // remote guy is polling-- we must final. + BOOLEAN ResendingPackets; // ResendLlcPackets in progress + BOOLEAN LinkBusy; // received RNR (really send-side state). + + // + // Timer, used to determine delay and throughput. + // + + ULONG Delay; // an NT time, but only LowPart is saved. + LARGE_INTEGER Throughput; + + // + // These are counters needed by ADAPTER_STATUS queries. + // + + USHORT FrmrsReceived; + USHORT FrmrsTransmitted; + USHORT ErrorIFramesReceived; + USHORT ErrorIFramesTransmitted; + USHORT AbortedTransmissions; + USHORT BuffersNotAvailable; + ULONG SuccessfulTransmits; + ULONG SuccessfulReceives; + USHORT T1Expirations; + USHORT TiExpirations; + + // + // Timeout state. There is one kernel timer for this transport that is set + // to go off at regular intervals. This timer increments the current time, + // which is then used to compare against the timer queues. The timer queues + // are ordered, so whenever the first element is not expired, the rest of + // the queue is not expired. This allows us to have hundreds of timers + // running with very little system overhead. + // A value of 0 indicates that the timer is not active. + // + + ULONG T1; // retry timer. + ULONG T2; // delayed ack timer. + ULONG Ti; // inactivity timer. + BOOLEAN OnShortList; // TRUE if link is in ShortList + BOOLEAN OnLongList; // TRUE if link is in LongList + LIST_ENTRY ShortList; // list of links waiting t1 or t2 + LIST_ENTRY LongList; // list of links waiting ti + + LIST_ENTRY PurgeList; + + // + // This counter is used to keep track of whether there are + // any "connectors" (connections initiated by this side) on + // this link. If there are none, and we are on an easily + // disconnected link, then we handle the inactivity timeout + // differently. + // + + LONG NumberOfConnectors; + + // + // BaseT1Timeout is the current T1 timeout computed based on + // the response to previous poll frames. T1Timeout is the + // value to be used for the next T1, and will generally be + // based on BaseT1Timeout but may be more if T1 is backing + // off. T2Timeout and TiTimeout are independent of these. + // + + ULONG BaseT1Timeout; // Timeout value for T1, << 16. + ULONG CurrentT1Timeout; // Current backed-off T1 timeout. + ULONG MinimumBaseT1Timeout; // Minimum value, based on link speed. + ULONG BaseT1RecalcThreshhold; // Only recalc BaseT1 on frames > this. + ULONG CurrentPollRetransmits; // Current retransmits waiting for final. + BOOLEAN ThroughputAccurate; // Is the throughput on this link accurate? + BOOLEAN CurrentT1Backoff; // the last poll frame had retransmits + BOOLEAN CurrentPollOutstanding; // Check that we have a poll outstanding. + LARGE_INTEGER CurrentTimerStart; // Time that current timing was begun. + ULONG CurrentPollSize; // Size of current poll packet. + ULONG T2Timeout; // Timeout value for T2. + ULONG TiTimeout; // Timeout value for Ti. + ULONG LlcRetries; // total retry count for this link. + ULONG MaxWindowSize; // maximum send window size. + ULONG TiStartPacketsReceived; // PacketsReceived when Ti was started. + + // + // Adaptive window algorithm state. + // + + ULONG WindowErrors; // # retransmissions/this adaptive run. + UCHAR BestWindowSize; // our best window from experience. + UCHAR WorstWindowSize; // our worst window from experience. + + // + // Keep track of remotes that never poll so we can send + // an RR every two frames. + // + + BOOLEAN RemoteNoPoll; // We think remote doesn't poll + UCHAR ConsecutiveIFrames; // number received since polling + +#if DBG + UCHAR CreatePacketFailures; // consecutive failures +#endif + + LIST_ENTRY DeferredList; // for threading on deferred list + + struct _DEVICE_CONTEXT *Provider; + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[LINK_HISTORY_LENGTH]; + BOOLEAN Destroyed; +#endif + +} TP_LINK, *PTP_LINK; + +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalLinkList; +#define StoreLinkHistory(_link,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_link)->Destroyed) { \ + DbgPrint ("link touched after being destroyed 0x%lx\n", (_link)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_link)->History[(_link)->NextRefLoc].Caller, \ + &(_link)->History[(_link)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_link)->TotalReferences++; \ + } else { \ + (_link)->TotalDereferences++; \ + (_link)->History[(_link)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_link)->History[(_link)->NextRefLoc].Caller & \ + ~0x80000000); \ + } \ + if (++(_link)->NextRefLoc == LINK_HISTORY_LENGTH) { \ + (_link)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define LINK_FLAGS_JUMP_START 0x00000040 // run adaptive alg/every sent window. +#define LINK_FLAGS_LOCAL_DISC 0x00000080 // link was stopped locally. + +// +// deferred flags, used for processing at timer tick if needed +// + +#define LINK_FLAGS_DEFERRED_DELETE 0x00010000 // delete at next opportunity +#define LINK_FLAGS_DEFERRED_ADD 0x00020000 // add to splay tree, next opportunity +#define LINK_FLAGS_DEFERRED_MASK 0x00030000 // (LINK_FLAGS_DEFERRED_DELETE | LINK_FLAGS_DEFERRED_ADD) + +#define LINK_STATE_ADM 1 // asynchronous disconnected mode. +#define LINK_STATE_READY 2 // asynchronous balanced mode extended. +#define LINK_STATE_BUSY 3 // all link buffers are busy, sent RNR +#define LINK_STATE_CONNECTING 4 // waiting SABME response (UA-r/f). +#define LINK_STATE_W_POLL 5 // waiting initial checkpoint. +#define LINK_STATE_W_FINAL 6 // waiting final from initial checkpoint. +#define LINK_STATE_W_DISC_RSP 7 // waiting disconnect response. + +#define SEND_STATE_DOWN 0 // asynchronous disconnected mode. +#define SEND_STATE_READY 1 // completely ready to send. +#define SEND_STATE_REJECTING 2 // other guy is rejecting. +#define SEND_STATE_CHECKPOINTING 3 // we're checkpointing (can't send data). + +#define RECEIVE_STATE_DOWN 0 // asynchronous disconnected mode. +#define RECEIVE_STATE_READY 1 // we're ready to receive. +#define RECEIVE_STATE_REJECTING 2 // we're rejecting. + + +// +// This structure defines the DEVICE_OBJECT and its extension allocated at +// the time the transport provider creates its device object. +// + +#if DBG +#define DCREF_CREATION 0 +#define DCREF_ADDRESS 1 +#define DCREF_CONNECTION 2 +#define DCREF_LINK 3 +#define DCREF_QUERY_INFO 4 +#define DCREF_SCAN_TIMER 5 +#define DCREF_REQUEST 6 + +#define NUMBER_OF_DCREFS 8 +#endif + + +typedef struct _NBF_POOL_LIST_DESC { + NDIS_HANDLE PoolHandle; + USHORT NumElements; + USHORT TotalElements; + struct _NBF_POOL_LIST_DESC *Next; +} NBF_POOL_LIST_DESC, *PNBF_POOL_LIST_DESC; + +typedef struct _DEVICE_CONTEXT { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[NUMBER_OF_DCREFS]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + + LIST_ENTRY Linkage; // links them on NbfDeviceList; + + KSPIN_LOCK Interlock; // GLOBAL spinlock for reference count. + // (used in ExInterlockedXxx calls) + LONG ReferenceCount; // activity count/this provider. + + + // + // This protects the LoopbackQueue. + // + + KSPIN_LOCK LoopbackSpinLock; + + // + // The queue of packets waiting to be looped back. + // + + LIST_ENTRY LoopbackQueue; + + // + // These two links are used for loopback. + // + + PTP_LINK LoopbackLinks[2]; + + // + // This buffer is used for loopback indications; a + // contiguous piece is copied into it. It is allocated + // (of size NBF_MAX_LOOPBACK_LOOKAHEAD) when one of + // the LoopbackLinks become non-NULL. + // + + PUCHAR LookaheadContiguous; + + // + // This holds the length of the header in the currently + // indicating loopback packet. + // + + ULONG LoopbackHeaderLength; + + // + // Used for processing the loopback queue. + // + + KDPC LoopbackDpc; + + // + // Determines if a LoopbackDpc is in progress. + // + + BOOLEAN LoopbackInProgress; + + // + // Determines if a WanDelayedDpc is in progress. + // + + BOOLEAN WanThreadQueued; + + // + // Used for momentarily delaying WAN packetizing to + // allow RR's to be received. + // + + WORK_QUEUE_ITEM WanDelayedQueueItem; + + // + // The queue of FIND.NAME requests waiting to be processed. + // + + LIST_ENTRY FindNameQueue; + + // + // The queue of STATUS.QUERY requests waiting to be processed. + // + + LIST_ENTRY StatusQueryQueue; + + // + // The queue of QUERY.INDICATION requests waiting to be completed. + // + + LIST_ENTRY QueryIndicationQueue; + + // + // The queue of DATAGRAM.INDICATION requests waiting to be completed. + // + + LIST_ENTRY DatagramIndicationQueue; + + // + // The queue of (currently receive only) IRPs waiting to complete. + // + + LIST_ENTRY IrpCompletionQueue; + + // + // This boolean is TRUE if either of the above two have ever + // had anything on them. + // + + BOOLEAN IndicationQueuesInUse; + + // + // Following are protected by Global Device Context SpinLock + // + + KSPIN_LOCK SpinLock; // lock to manipulate this object. + // (used in KeAcquireSpinLock calls) + + // + // the device context state, among open, closing + // + + UCHAR State; + + // + // Used when processing a STATUS_CLOSING indication. + // + + WORK_QUEUE_ITEM StatusClosingQueueItem; + + // + // The following queue holds free TP_LINK objects available for allocation. + // + + LIST_ENTRY LinkPool; + + // + // These counters keep track of resources uses by TP_LINK objects. + // + + ULONG LinkAllocated; + ULONG LinkInitAllocated; + ULONG LinkMaxAllocated; + ULONG LinkInUse; + ULONG LinkMaxInUse; + ULONG LinkExhausted; + ULONG LinkTotal; + ULONG LinkSamples; + + + // + // The following queue holds free TP_ADDRESS objects available for allocation. + // + + LIST_ENTRY AddressPool; + + // + // These counters keep track of resources uses by TP_ADDRESS objects. + // + + ULONG AddressAllocated; + ULONG AddressInitAllocated; + ULONG AddressMaxAllocated; + ULONG AddressInUse; + ULONG AddressMaxInUse; + ULONG AddressExhausted; + ULONG AddressTotal; + ULONG AddressSamples; + + + // + // The following queue holds free TP_ADDRESS_FILE objects available for allocation. + // + + LIST_ENTRY AddressFilePool; + + // + // These counters keep track of resources uses by TP_ADDRESS_FILE objects. + // + + ULONG AddressFileAllocated; + ULONG AddressFileInitAllocated; + ULONG AddressFileMaxAllocated; + ULONG AddressFileInUse; + ULONG AddressFileMaxInUse; + ULONG AddressFileExhausted; + ULONG AddressFileTotal; + ULONG AddressFileSamples; + + + // + // The following queue holds free TP_CONNECTION objects available for allocation. + // + + LIST_ENTRY ConnectionPool; + + // + // These counters keep track of resources uses by TP_CONNECTION objects. + // + + ULONG ConnectionAllocated; + ULONG ConnectionInitAllocated; + ULONG ConnectionMaxAllocated; + ULONG ConnectionInUse; + ULONG ConnectionMaxInUse; + ULONG ConnectionExhausted; + ULONG ConnectionTotal; + ULONG ConnectionSamples; + + + // + // The following is a free list of TP_REQUEST blocks which have been + // previously allocated and are available for use. + // + + LIST_ENTRY RequestPool; // free request block pool. + + // + // These counters keep track of resources uses by TP_REQUEST objects. + // + + ULONG RequestAllocated; + ULONG RequestInitAllocated; + ULONG RequestMaxAllocated; + ULONG RequestInUse; + ULONG RequestMaxInUse; + ULONG RequestExhausted; + ULONG RequestTotal; + ULONG RequestSamples; + + + // + // The following list comprises a pool of UI NetBIOS frame headers + // that are manipulated by the routines in FRAMESND.C. + // + + LIST_ENTRY UIFramePool; // free UI frames (TP_UI_FRAME objects). + + // + // These counters keep track of resources uses by TP_UI_FRAME objects. + // + + ULONG UIFrameLength; + ULONG UIFrameHeaderLength; + ULONG UIFrameAllocated; + ULONG UIFrameInitAllocated; + ULONG UIFrameExhausted; + + + // + // The following queue holds I-frame Send packets managed by PACKET.C. + // + + SINGLE_LIST_ENTRY PacketPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG PacketLength; + ULONG PacketHeaderLength; + ULONG PacketAllocated; + ULONG PacketInitAllocated; + ULONG PacketExhausted; + + + // + // The following queue holds RR-frame Send packets managed by PACKET.C. + // + + SINGLE_LIST_ENTRY RrPacketPool; + + + // + // The following queue contains Receive packets + // + + SINGLE_LIST_ENTRY ReceivePacketPool; + + // + // These counters keep track of resources uses by NDIS_PACKET objects. + // + + ULONG ReceivePacketAllocated; + ULONG ReceivePacketInitAllocated; + ULONG ReceivePacketExhausted; + + + // + // This queue contains pre-allocated receive buffers + // + + SINGLE_LIST_ENTRY ReceiveBufferPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG ReceiveBufferLength; + ULONG ReceiveBufferAllocated; + ULONG ReceiveBufferInitAllocated; + ULONG ReceiveBufferExhausted; + + + // + // This holds the total memory allocated for the above structures. + // + + ULONG MemoryUsage; + ULONG MemoryLimit; + + + // + // The following field is a head of a list of TP_ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. + + // + // The following field is the pointer to the root of the splay tree of + // links that are associated with this Device Context. You must hold the + // LinkSpinLock to modify this list. You must set the LinkTreeSemaphore + // to traverse this list without modifying it. Note that all modify + // operations are deferred to timer(DPC)-time operations. + // + + KSPIN_LOCK LinkSpinLock; // protects these values + PTP_LINK LastLink; // the last link found in the tree. + PRTL_SPLAY_LINKS LinkTreeRoot; // pointer to root of the tree. + ULONG LinkTreeElements; // how many elements in the tree + LIST_ENTRY LinkDeferred; // Deferred operations on links. + ULONG DeferredNotSatisfied; // how many times we've come to the + // deferred well and not gotten it clear. + + // + // The following queue holds connections which are waiting on available + // packets. As each new packet becomes available, a connection is removed + // from this queue and placed on the PacketizeQueue. + // + + LIST_ENTRY PacketWaitQueue; // queue of packet-starved connections. + LIST_ENTRY PacketizeQueue; // queue of ready-to-packetize connections. + + // + // The following queue holds connections which are waiting to send + // a piggyback ack. In that case the CONNECTION_FLAGS_DEFERRED_ACK + // bit in DeferredFlags will be set. + // + + LIST_ENTRY DataAckQueue; + + // + // The following queue holds links which are waiting to send an + // RR frame because the remote they are talking to never polls. + // + + LIST_ENTRY DeferredRrQueue; + + // + // Used to track when the queue has changed. + // + + BOOLEAN DataAckQueueChanged; + + // + // When this hits thirty seconds we checked for stalled connections. + // + + USHORT StalledConnectionCount; + + // + // This queue contains receives that are in progress + // + + LIST_ENTRY ReceiveInProgress; + + // + // NDIS fields + // + + // + // following is used to keep adapter information. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The following fields are used for talking to NDIS. They keep information + // for the NDIS wrapper to use when determining what pool to use for + // allocating storage. + // + + KSPIN_LOCK SendPoolListLock; // protects these values + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + KSPIN_LOCK RcvPoolListLock; // protects these values + PNBF_POOL_LIST_DESC ReceivePacketPoolDesc; + NDIS_HANDLE NdisBufferPool; + + // + // These are kept around for error logging. + // + + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxRequests; + ULONG MaxLinks; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + PWCHAR DeviceName; + ULONG DeviceNameLength; + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + NBF_NDIS_IDENTIFICATION MacInfo; // MAC type and other info + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + ULONG CurSendPacketSize; // may be smaller for async + USHORT RecommendedSendWindow; // used for Async lines + BOOLEAN EasilyDisconnected; // TRUE over wireless nets. + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalAddress; // our local hardware address. + HARDWARE_ADDRESS NetBIOSAddress; // NetBIOS functional address, used for TR + + // + // The reserved Netbios address; consists of 10 zeroes + // followed by LocalAddress; + // + + UCHAR ReservedNetBIOSAddress[NETBIOS_NAME_LENGTH]; +#ifdef _PNP_POWER + HANDLE TdiDeviceHandle; + HANDLE ReservedAddressHandle; +#endif + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + + // + // This next field maintains a unique number which can next be assigned + // as a connection identifier. It is incremented by one each time a + // value is allocated. + // + + USHORT UniqueIdentifier; // starts at 0, wraps around 2^16-1. + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // The following fields are used to implement the lightweight timer + // system in the protocol provider. Each TP_LINK object in the device + // context's LinkDatabase contains three lightweight timers that are + // serviced by a DPC routine, which receives control by kernel functions. + // There is one kernel timer for this transport that is set + // to go off at regular intervals. This timer increments the Absolute time, + // which is then used to compare against the timer queues. The timer queues + // are ordered, so whenever the first element is not expired, the rest of + // the queue is not expired. This allows us to have hundreds of timers + // running with very low system overhead. + // A value of -1 indicates that the timer is not active. + // + + LARGE_INTEGER ShortTimerStart; // when the short timer was set. + KDPC ShortTimerSystemDpc; // kernel DPC object, short timer. + KTIMER ShortSystemTimer; // kernel timer object, short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + ULONG AdaptivePurge; // absolute time of next purge (short timer). + KDPC LongTimerSystemDpc; // kernel DPC object, long timer. + KTIMER LongSystemTimer; // kernel timer object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + union _DC_ACTIVE { + struct _DC_INDIVIDUAL { + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckQueueActive; // DataAckQueue is not empty. + BOOLEAN LinkDeferredActive; // LinkDeferred is not empty. + } i; + ULONG AnyActive; // used to check all four at once. + } a; + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. + KSPIN_LOCK TimerSpinLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of links waiting T1 or T2 + LIST_ENTRY LongList; // list of links waiting Ti expire + LIST_ENTRY PurgeList; // list of links waiting LAT expire + + // + // These fields are used on "easily disconnected" adapters. + // Every time the long timer expires, it notes if there has + // been any multicast traffic received. If there has not been, + // it increments LongTimeoutsWithoutMulticast. Activity is + // recorded by incrementing MulticastPacket when MC + // packets are received, and zeroing it when the long timer + // expires. + // + + ULONG LongTimeoutsWithoutMulticast; // LongTimer timeouts since traffic. + ULONG MulticastPacketCount; // How many MC packets rcved, this timeout. + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + BOOLEAN MediumSpeedAccurate; // if FALSE, can't use the link. + + // + // This is TRUE if we are on a UP system. + // + + BOOLEAN UniProcessor; + + // + // Configuration information on how soon we should send + // an unasked for RR with a non-polling remote. + // + + UCHAR MaxConsecutiveIFrames; + + // + // This is configuration information controlling the default + // value of timers and retry counts. + // + + ULONG DefaultT1Timeout; + ULONG MinimumT1Timeout; + ULONG DefaultT2Timeout; + ULONG DefaultTiTimeout; + ULONG LlcRetries; + ULONG LlcMaxWindowSize; + ULONG NameQueryRetries; + ULONG NameQueryTimeout; + ULONG AddNameQueryRetries; + ULONG AddNameQueryTimeout; + ULONG GeneralRetries; + ULONG GeneralTimeout; + ULONG MinimumSendWindowLimit; // how low we can lock a connection's window + + // + // Counters for most of the statistics that NBF maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempIFrameBytesSent; + ULONG TempIFramesSent; + ULONG TempIFrameBytesReceived; + ULONG TempIFramesReceived; + + // + // Some counters needed for Netbios adapter status. + // + + ULONG TiExpirations; + ULONG FrmrReceived; + ULONG FrmrTransmitted; + + // + // These are used to compute AverageSendWindow. + // + + ULONG SendWindowTotal; + ULONG SendWindowSamples; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbfStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // This array is used to keep track of which LSNs are + // available for use by Netbios sessions. LSNs can be + // re-used for sessions to unique names if they are on + // different links, but must be committed beforehand + // for group names. The maximum value that can fit in + // an array element is defined by LSN_TABLE_MAX. + // + + UCHAR LsnTable[NETBIOS_SESSION_LIMIT+1]; + + // + // This is where we start looking in LsnTable for an + // unused LSN. We cycle from 0-63 to prevent quick + // down-and-up connections from getting funny data. + // + + ULONG NextLsnStart; + + // + // This array is used to quickly dismiss UI frames that + // are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + + PTP_VARIABLE NetmanVariables; // list of network managable variables. + + // + // The magic bullet is a packet that is sent under certain debugging + // conditions. This allows the transport to signal packet capture devices + // that a particular condiion has been met. This packet has the current + // devicecontext as the source, and 0x04 in every other byte of the packet. + // + + UCHAR MagicBullet[32]; // + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// device context state definitions +// + +#define DEVICECONTEXT_STATE_OPENING 0x00 +#define DEVICECONTEXT_STATE_OPEN 0x01 +#define DEVICECONTEXT_STATE_DOWN 0x02 +#define DEVICECONTEXT_STATE_STOPPING 0x03 + +// +// This is the maximum value that can go in an element +// of LsnTable (should be 0xff if they are UCHARs, +// 0xffff for USHORTs, etc.). +// + +#define LSN_TABLE_MAX 0xff + + +#define MAGIC_BULLET_FOOD 0x04 + + +// +// These are constants for the LoopbackLinks elements. +// The distinctions are arbitrary; the listener link +// is the one established from ProcessNameQuery, and +// the connector link is the one established from +// ProcessNameRecognized. +// + +#define LISTENER_LINK 0 +#define CONNECTOR_LINK 1 + + +// +// This structure defines the packet object, used to represent a DLC I-frame +// in some portion of its lifetime. The PACKET.C module contains routines +// to manage this object. +// + +typedef struct _TP_PACKET { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + PNDIS_PACKET NdisPacket; // ptr to owning Ndis Packet + ULONG NdisIFrameLength; // Length of NdisPacket + + LIST_ENTRY Linkage; // used to chain packets together. + LONG ReferenceCount; // activity count/this packet. + BOOLEAN PacketSent; // packet completed by NDIS. + BOOLEAN PacketNoNdisBuffer; // chain on this packet was not allocated. + + UCHAR Action; // what to do when we're acked. + BOOLEAN PacketizeConnection; // restart packetizing when completed. + + PVOID Owner; // ptr to owning connection or IrpSp. + PTP_LINK Link; // ptr to link it was sent on. + PDEVICE_CONTEXT Provider; // The owner of this packet. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock. + + UCHAR Header[1]; // the MAC, DLC, and NBF headers + +} TP_PACKET, *PTP_PACKET; + + +// +// The following values are placed in the Action field in the TP_PACKET +// object to indicate what action, if any, should be taken when the packet +// is destroyed. +// + +#define PACKET_ACTION_NULL 0 // no special action should be taken. +#define PACKET_ACTION_IRP_SP 1 // Owner is an IRP_SP, deref when done. +#define PACKET_ACTION_CONNECTION 2 // Owner is a TP_CONNECTION, deref when done. +#define PACKET_ACTION_END 3 // shutdown session (sent SESSION_END). +#define PACKET_ACTION_RR 5 // packet is an RR, put back in RR pool. + +// +// Types used to hold information in the send and receive NDIS packets +// + +typedef struct _SEND_PACKET_TAG { + LIST_ENTRY Linkage; // used for threading on loopback queue + BOOLEAN OnLoopbackQueue; // TRUE if the packet is on a loopback queue + UCHAR LoopbackLinkIndex; // index of other link for loopback packets + USHORT Type; // identifier for packet type + PVOID Frame; // backpointer to owning NBF structure + PVOID Owner; // backpointer for owning nbf construct + // (like address, devicecontext, etc) + } SEND_PACKET_TAG, *PSEND_PACKET_TAG; + +// +// Packet types used in send completion +// + +#define TYPE_I_FRAME 1 +#define TYPE_UI_FRAME 2 +#define TYPE_ADDRESS_FRAME 3 + +// +// LoopbackLinkIndex values. +// + +#define LOOPBACK_TO_LISTENER 0 +#define LOOPBACK_TO_CONNECTOR 1 +#define LOOPBACK_UI_FRAME 2 + +// +// receive packet used to hold information about this receive +// + +typedef struct _RECEIVE_PACKET_TAG { + SINGLE_LIST_ENTRY Linkage; // used for threading in pool + PTP_CONNECTION Connection; // connection this receive is occuring on + ULONG BytesToTransfer; // for I-frame, bytes in this transfer + UCHAR PacketType; // the type of packet we're processing + BOOLEAN AllocatedNdisBuffer; // did we allocate our own NDIS_BUFFERs + BOOLEAN EndOfMessage; // does this receive complete the message + BOOLEAN CompleteReceive; // complete the receive after TransferData? + BOOLEAN TransferDataPended; // TRUE if TransferData returned PENDING + } RECEIVE_PACKET_TAG, *PRECEIVE_PACKET_TAG; + +#define TYPE_AT_INDICATE 1 +#define TYPE_AT_COMPLETE 2 +#define TYPE_STATUS_RESPONSE 3 + +// +// receive buffer descriptor (built in memory at the beginning of the buffer) +// + +typedef struct _BUFFER_TAG { + LIST_ENTRY Linkage; // thread in pool and on receive queue + NDIS_STATUS NdisStatus; // completion status for send + PTP_ADDRESS Address; // the address this datagram is for. + PNDIS_BUFFER NdisBuffer; // describes the rest of the buffer + ULONG Length; // the length of the buffer + UCHAR Buffer[1]; // the actual storage (accessed through the NDIS_BUFFER) + } BUFFER_TAG, *PBUFFER_TAG; + +#endif // def _NBFTYPES_ + diff --git a/private/ntos/tdi/nbf/packet.c b/private/ntos/tdi/nbf/packet.c new file mode 100644 index 000000000..2b6de5b25 --- /dev/null +++ b/private/ntos/tdi/nbf/packet.c @@ -0,0 +1,1808 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the TP_PACKET object, which + describes a DLC I-frame at some point in its lifetime. Routines are + provided to allocate packets for shipment, to ship packets, to reference + packets, to dereference packets, to mark a connection as waiting for a + packet to become available, to satisfy the next waiting connection for + a packet, and to destroy packets (return them to the pool). + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// This is temporary; this is the quota that we charge for a receive +// packet for now, until we fix the problem with token-ring needing +// big packets and using all the memory. The number is the actual +// value for Ethernet. +// + +#if 1 +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) 1533 +#else +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) (_DeviceContext)->ReceiveBufferLength +#endif + +#define PACKET_POOL_GROW_COUNT 32 + +#if DBG +ULONG NbfCreatePacketThreshold = 5; +extern ULONG NbfPacketPanic; +#endif + +NDIS_STATUS +NbfAllocateNdisSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *NdisPacket + ) + +/*++ + +Routine Description: + + This routine allocates a recieve packet from the receive packet pool. + It Grows the packet pool if necessary. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + NDIS_STATUS NdisStatus; + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SendPoolListLock, &oldirql); + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + return(NdisStatus); + } + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + + PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))) > + DeviceContext->MemoryLimit)) { + + PANIC("NBF: Could not grow packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + return(NdisStatus); + } + } + + DeviceContext->MemoryUsage += + (PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))); + + // Allocate Packet pool descriptors for dynamic packet allocation. + + SendPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->SendPacketPoolDesc == NULL) { + return(NdisStatus); + } + + RtlZeroMemory(SendPacketPoolDesc, sizeof(NBF_POOL_LIST_DESC)); + + SendPacketPoolDesc->NumElements = + SendPacketPoolDesc->TotalElements = PACKET_POOL_GROW_COUNT; + + NdisAllocatePacketPool ( &NdisStatus, &SendPacketPoolDesc->PoolHandle, + PACKET_POOL_GROW_COUNT, sizeof (SEND_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 ("NbfGrowSendPacketPool: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + ExFreePool (SendPacketPoolDesc); + return(NdisStatus); + } + SendPacketPoolDesc->Next = DeviceContext->SendPacketPoolDesc; + DeviceContext->SendPacketPoolDesc = SendPacketPoolDesc; + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + NdisAllocatePacket ( &NdisStatus, NdisPacket, + SendPacketPoolDesc->PoolHandle); + + return(NdisStatus); +} + +NDIS_STATUS +NbfAllocateNdisRcvPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *NdisPacket + ) + +/*++ + +Routine Description: + + This routine allocates a recieve packet from the receive packet pool. + It Grows the packet pool if necessary. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PNBF_POOL_LIST_DESC RcvPacketPoolDesc; + NDIS_STATUS NdisStatus; + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, &oldirql); + for (RcvPacketPoolDesc = DeviceContext->ReceivePacketPoolDesc; + RcvPacketPoolDesc != NULL; + RcvPacketPoolDesc = RcvPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + NdisPacket, + RcvPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + + PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))) > + DeviceContext->MemoryLimit)) { + + PANIC("NBF: Could not grow packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + } + + DeviceContext->MemoryUsage += + (PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))); + + // Allocate Packet pool descriptors for dynamic packet allocation. + + RcvPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (RcvPacketPoolDesc == NULL) { + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + + RtlZeroMemory(RcvPacketPoolDesc, sizeof(NBF_POOL_LIST_DESC)); + + RcvPacketPoolDesc->NumElements = + RcvPacketPoolDesc->TotalElements = PACKET_POOL_GROW_COUNT; + + NdisAllocatePacketPool ( &NdisStatus, &RcvPacketPoolDesc->PoolHandle, + PACKET_POOL_GROW_COUNT, sizeof (RECEIVE_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 ("NbfGrowSendPacketPool: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + ExFreePool (RcvPacketPoolDesc); + return(NdisStatus); + } + RcvPacketPoolDesc->Next = DeviceContext->ReceivePacketPoolDesc; + DeviceContext->ReceivePacketPoolDesc = RcvPacketPoolDesc; + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + NdisAllocatePacket ( &NdisStatus, NdisPacket, + RcvPacketPoolDesc->PoolHandle); + + return(NdisStatus); +} + + +VOID +NbfAllocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *TransportUIFrame + ) + +/*++ + +Routine Description: + + This routine allocates storage for a UI frame. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PSEND_PACKET_TAG SendTag; + PTP_UI_FRAME UIFrame; + PNDIS_BUFFER NdisBuffer; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->UIFrameLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate UI frame: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + *TransportUIFrame = NULL; + return; + } + + UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->UIFrameLength, + 'uFBN'); + if (UIFrame == NULL) { + PANIC("NBF: Could not allocate UI frame: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 206, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + *TransportUIFrame = NULL; + return; + } + RtlZeroMemory (UIFrame, DeviceContext->UIFrameLength); + + DeviceContext->MemoryUsage += DeviceContext->UIFrameLength; + NdisStatus = NbfAllocateNdisSendPacket(DeviceContext, &NdisPacket); +#if 0 + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) + break; + } +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (UIFrame); +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 306, + 0, + UI_FRAME_RESOURCE_ID); +#endif + *TransportUIFrame = NULL; + return; + } + + + UIFrame->NdisPacket = NdisPacket; + UIFrame->DataBuffer = NULL; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_UI_FRAME; + SendTag->Frame = UIFrame; + SendTag->Owner = DeviceContext; + + // + // Make the packet header known to the packet descriptor + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + UIFrame->Header, + DeviceContext->UIFrameHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 406, + 0, + UI_FRAME_RESOURCE_ID); + NdisFreePacket (NdisPacket); + ExFreePool (UIFrame); + *TransportUIFrame = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + ++DeviceContext->UIFrameAllocated; + + *TransportUIFrame = UIFrame; + +} /* NbfAllocateUIFrame */ + + +VOID +NbfDeallocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME TransportUIFrame + ) + +/*++ + +Routine Description: + + This routine frees storage for a UI frame. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - A pointer to the frame. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET NdisPacket = TransportUIFrame->NdisPacket; + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + + NdisFreePacket (NdisPacket); + ExFreePool (TransportUIFrame); + --DeviceContext->UIFrameAllocated; + + DeviceContext->MemoryUsage -= DeviceContext->UIFrameLength; + +} /* NbfDeallocateUIFrame */ + + +VOID +NbfAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a send packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_PACKET Packet; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PSEND_PACKET_TAG SendTag; + PNDIS_BUFFER NdisBuffer; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate send packet: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 107, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + *TransportSendPacket = NULL; + return; + } + + Packet = (PTP_PACKET)ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->PacketLength, + 'pFBN'); + if (Packet == NULL) { + PANIC("NBF: Could not allocate send packet: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 207, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + *TransportSendPacket = NULL; + return; + } + RtlZeroMemory (Packet, DeviceContext->PacketLength); + + DeviceContext->MemoryUsage += DeviceContext->PacketLength; + + NdisStatus = NbfAllocateNdisSendPacket(DeviceContext, &NdisPacket); +#if 0 + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) + break; + } +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (Packet); +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 307, + 0, + PACKET_RESOURCE_ID); +#endif + *TransportSendPacket = NULL; + return; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + Packet->Header, + DeviceContext->PacketHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 407, + 0, + PACKET_RESOURCE_ID); + NdisFreePacket (NdisPacket); + ExFreePool (Packet); + *TransportSendPacket = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + Packet->NdisPacket = NdisPacket; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_I_FRAME; + SendTag->Frame = Packet; + SendTag->Owner = DeviceContext; + + Packet->Type = NBF_PACKET_SIGNATURE; + Packet->Size = sizeof (TP_PACKET); + Packet->Provider = DeviceContext; + Packet->Owner = NULL; // no connection/irpsp yet. + Packet->Action = PACKET_ACTION_IRP_SP; + Packet->PacketizeConnection = FALSE; + Packet->PacketNoNdisBuffer = FALSE; + Packet->ProviderInterlock = &DeviceContext->Interlock; +// KeInitializeSpinLock (&Packet->Interlock); + + ++DeviceContext->PacketAllocated; + + *TransportSendPacket = Packet; + +} /* NbfAllocateSendPacket */ + + +VOID +NbfDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a send packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - A pointer to the send packet. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET NdisPacket = TransportSendPacket->NdisPacket; + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + + NdisFreePacket (NdisPacket); + ExFreePool (TransportSendPacket); + + --DeviceContext->PacketAllocated; + DeviceContext->MemoryUsage -= DeviceContext->PacketLength; + +} /* NbfDeallocateSendPacket */ + + +VOID +NbfAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + + // + // This does not count in DeviceContext->MemoryUsage because + // the storage is allocated when we allocate the packet pool. + // + + NdisStatus = NbfAllocateNdisRcvPacket(DeviceContext, &NdisPacket); +#if 0 + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->ReceivePacketPoolDesc->PoolHandle); +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 309, + 0, + RECEIVE_PACKET_RESOURCE_ID); +#endif + *TransportReceivePacket = NULL; + return; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ++DeviceContext->ReceivePacketAllocated; + + *TransportReceivePacket = NdisPacket; + +} /* NbfAllocateReceivePacket */ + + +VOID +NbfDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - A pointer to the packet. + +Return Value: + + None. + +--*/ + +{ + + NdisFreePacket (TransportReceivePacket); + + --DeviceContext->ReceivePacketAllocated; + +} /* NbfDeallocateReceivePacket */ + + +VOID +NbfAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive buffer. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - Returns a pointer to the buffer, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PBUFFER_TAG BufferTag; + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + RECEIVE_BUFFER_QUOTA(DeviceContext)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate receive buffer: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 108, + RECEIVE_BUFFER_QUOTA(DeviceContext), + RECEIVE_BUFFER_RESOURCE_ID); + *TransportReceiveBuffer = NULL; + return; + } + + // + // BUGBUG: The Aligned doesn't help since the header makes it unaligned. + // + + BufferTag = (PBUFFER_TAG)ExAllocatePoolWithTag ( + NonPagedPoolCacheAligned, + DeviceContext->ReceiveBufferLength, + 'tFBN'); + + if (BufferTag == NULL) { + PANIC("NBF: Could not allocate receive buffer: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 208, + DeviceContext->ReceiveBufferLength, + RECEIVE_BUFFER_RESOURCE_ID); + + *TransportReceiveBuffer = NULL; + return; + } + + DeviceContext->MemoryUsage += RECEIVE_BUFFER_QUOTA(DeviceContext); + + // + // point to the buffer for NDIS + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + BufferTag->Buffer, + DeviceContext->MaxReceivePacketSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC("NBF: Could not allocate receive buffer: no buffer\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 308, + 0, + RECEIVE_BUFFER_RESOURCE_ID); + ExFreePool (BufferTag); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag->Length = DeviceContext->MaxReceivePacketSize; + BufferTag->NdisBuffer = NdisBuffer; + + ++DeviceContext->ReceiveBufferAllocated; + + *TransportReceiveBuffer = BufferTag; + +} /* NbfAllocateReceiveBuffer */ + + +VOID +NbfDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive buffer. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - A pointer to the buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisFreeBuffer (TransportReceiveBuffer->NdisBuffer); + ExFreePool (TransportReceiveBuffer); + + --DeviceContext->ReceiveBufferAllocated; + DeviceContext->MemoryUsage -= RECEIVE_BUFFER_QUOTA(DeviceContext); + +} /* NbfDeallocateReceiveBuffer */ + + +NTSTATUS +NbfCreatePacket( + PDEVICE_CONTEXT DeviceContext, + PTP_LINK Link, + PTP_PACKET *Packet + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool, + and prepares the MAC and DLC headers for use by the connection. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + Link - The link the packet will be sent over. + + Packet - Pointer to a place where we will return a pointer to the + allocated packet. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PSINGLE_LIST_ENTRY s; + PTP_PACKET ThePacket; + PDLC_I_FRAME DlcHdr; +#if DBG + PNBF_HDR_CONNECTION NbfHdr; +#endif + typedef struct _SIXTEEN_BYTES { + ULONG Data[4]; + } SIXTEEN_BYTES, *PSIXTEEN_BYTES; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NbfCreatePacket: Entered.\n"); + } + + // + // Make sure that structure packing hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTION) == 14); + +#if defined(NBF_UP) + s = DeviceContext->PacketPool.Next; + if (s != NULL) { + DeviceContext->PacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); +#endif + + if (s == NULL) { + NbfGrowSendPacketPool(DeviceContext); + +#if defined(NBF_UP) + s = DeviceContext->PacketPool.Next; + if (s != NULL) { + DeviceContext->PacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); +#endif + if (s == NULL) { +#if DBG + ++Link->CreatePacketFailures; + if ((ULONG)Link->CreatePacketFailures >= NbfCreatePacketThreshold) { + if (NbfPacketPanic) { + NbfPrint1 ("NbfCreatePacket: PANIC! no more packets in provider's pool (%d times).\n", + Link->CreatePacketFailures); + } + Link->CreatePacketFailures = 0; + } +#endif + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->PacketExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } +#if DBG + Link->CreatePacketFailures = 0; +#endif + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + // + // NOTE: ThePacket->Action and ThePacket->Owner are filled + // in by the caller of this function. + // + + ThePacket->ReferenceCount = 1; // automatic ref count of 1. + ThePacket->Link = NULL; // no link yet. + ThePacket->PacketSent = FALSE; + ASSERT (ThePacket->Action == PACKET_ACTION_IRP_SP); + ASSERT (ThePacket->PacketNoNdisBuffer == FALSE); + ASSERT (ThePacket->PacketizeConnection == FALSE); + + // + // Initialize the MAC header for this packet, using the connection's + // link pre-built header. + // + + if (Link->HeaderLength <= 14) { + + *(PSIXTEEN_BYTES)ThePacket->Header = *(PSIXTEEN_BYTES)Link->Header; + + } else { + + RtlCopyMemory( + ThePacket->Header, + Link->Header, + Link->HeaderLength); + + // + // Initialize the TP_FRAME_CONNECTION header for this packet. + // + + DlcHdr = (PDLC_I_FRAME)&(ThePacket->Header[Link->HeaderLength]); + DlcHdr->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHdr->Ssap = DSAP_NETBIOS_OVER_LLC; +#if DBG + DlcHdr->SendSeq = 0; // known values, will assist debugging. + DlcHdr->RcvSeq = 0; // these are assigned at shipment time. +#endif + + } + + +#if DBG + NbfHdr = (PNBF_HDR_CONNECTION)&(ThePacket->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]); + NbfHdr->Command = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data1 = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2Low = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2High = 0xff; // to assist debugging-- assigned later. + TRANSMIT_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + RESPONSE_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + NbfHdr->DestinationSessionNumber = 0xff; // to assist debugging-- assigned later. + NbfHdr->SourceSessionNumber = 0xff; // to assist debugging-- assigned later. +#endif + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* NbfCreatePacket */ + + +NTSTATUS +NbfCreateRrPacket( + PDEVICE_CONTEXT DeviceContext, + PTP_LINK Link, + PTP_PACKET *Packet + ) + +/*++ + +Routine Description: + + This routine allocates an RR packet from the device context's pool, + and prepares the MAC and DLC headers for use by the connection. + It first looks in the special RR packet pool, then in the regular + packet pool. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + Link - The link the packet will be sent over. + + Packet - Pointer to a place where we will return a pointer to the + allocated packet. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PTP_PACKET ThePacket; + PDLC_I_FRAME DlcHdr; + NTSTATUS Status; +#if DBG + PNBF_HDR_CONNECTION NbfHdr; +#endif + typedef struct _SIXTEEN_BYTES { + ULONG Data[4]; + } SIXTEEN_BYTES, *PSIXTEEN_BYTES; + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NbfCreateRrPacket: Entered.\n"); + } + + // + // Make sure that structure packing hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTION) == 14); + +#if defined(NBF_UP) + s = DeviceContext->RrPacketPool.Next; + if (s != NULL) { + DeviceContext->RrPacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->RrPacketPool, + &DeviceContext->Interlock); +#endif + + if (s == NULL) { +#if DBG + ++Link->CreatePacketFailures; + if ((ULONG)Link->CreatePacketFailures >= NbfCreatePacketThreshold) { + if (NbfPacketPanic) { + NbfPrint1 ("NbfCreateRrPacket: PANIC! no more packets in provider's pool (%d times).\n", + Link->CreatePacketFailures); + } + Link->CreatePacketFailures = 0; + } +#endif + // + // Try to get one from the regular pool, and mark it so + // it goes back there. + // + + Status = NbfCreatePacket( + DeviceContext, + Link, + Packet); + + if (Status == STATUS_SUCCESS) { + (*Packet)->Action = PACKET_ACTION_NULL; + } + return Status; + } +#if DBG + Link->CreatePacketFailures = 0; +#endif + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + // + // NOTE: ThePacket->Owner is filled in by the caller of this + // function. + // + + ThePacket->ReferenceCount = 1; // automatic ref count of 1. + ThePacket->Link = NULL; // no link yet. + ThePacket->PacketSent = FALSE; + ASSERT (ThePacket->Action == PACKET_ACTION_RR); + ASSERT (ThePacket->PacketNoNdisBuffer == FALSE); + + // + // Initialize the MAC header for this packet, using the connection's + // link pre-built header. + // + + if (Link->HeaderLength <= 14) { + + *(PSIXTEEN_BYTES)ThePacket->Header = *(PSIXTEEN_BYTES)Link->Header; + + } else { + + RtlCopyMemory( + ThePacket->Header, + Link->Header, + Link->HeaderLength); + + // + // Initialize the TP_FRAME_CONNECTION header for this packet. + // + + DlcHdr = (PDLC_I_FRAME)&(ThePacket->Header[Link->HeaderLength]); + DlcHdr->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHdr->Ssap = DSAP_NETBIOS_OVER_LLC; +#if DBG + DlcHdr->SendSeq = 0; // known values, will assist debugging. + DlcHdr->RcvSeq = 0; // these are assigned at shipment time. +#endif + + } + + +#if DBG + NbfHdr = (PNBF_HDR_CONNECTION)&(ThePacket->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]); + NbfHdr->Command = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data1 = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2Low = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2High = 0xff; // to assist debugging-- assigned later. + TRANSMIT_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + RESPONSE_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + NbfHdr->DestinationSessionNumber = 0xff; // to assist debugging-- assigned later. + NbfHdr->SourceSessionNumber = 0xff; // to assist debugging-- assigned later. +#endif + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* NbfCreateRrPacket */ + + +VOID +NbfDestroyPacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine destroys a packet, thereby returning it to the pool. If + it is determined that there is at least one connection waiting for a + packet to become available (and it just has), then the connection is + removed from the device context's list and AdvanceSend is called to + prep the connection further. + +Arguments: + + Packet - Pointer to a packet to be returned to the pool. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + ULONG Flags; + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint2 ("NbfDestroyPacket: Entered, Packet: %lx, NdisPacket: %lx\n", + Packet, Packet->NdisPacket); + } + + DeviceContext = Packet->Provider; + + // + // Strip off and unmap the buffers describing data and header. + // + + if (Packet->PacketNoNdisBuffer) { + + // + // If the NDIS_BUFFER chain is not ours, then we can't + // start unchaining since that would mess up the queue; + // instead we just drop the rest of the chain after the + // header. + // + + NdisQueryPacket (Packet->NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + ASSERT (HeaderBuffer != NULL); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisRecalculatePacketCounts (Packet->NdisPacket); + + Packet->PacketNoNdisBuffer = FALSE; + + } else { + + NdisUnchainBufferAtFront (Packet->NdisPacket, &HeaderBuffer); + ASSERT (HeaderBuffer != NULL); + + // + // Return all the NDIS_BUFFERs to the system. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + } + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Packet->NdisPacket, HeaderBuffer); + + } + + + // + // invoke the packet deallocate action specified in this packet. + // + + switch (Packet->Action) { + + case PACKET_ACTION_NULL: + // PANIC ("NbfDestroyPacket: no action.\n"); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_IRP_SP: + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfDestroyPacket: Packet %x deref IrpSp %x.\n", Packet, Packet->Owner); + } + NbfDereferenceSendIrp("Destroy packet", (PIO_STACK_LOCATION)(Packet->Owner), RREF_PACKET); + break; + + case PACKET_ACTION_CONNECTION: + NbfDereferenceConnection ("Destroy packet", (PTP_CONNECTION)(Packet->Owner), CREF_FRAME_SEND); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_END: + NbfDereferenceConnection ("SessionEnd destroyed", (PTP_CONNECTION)(Packet->Owner), CREF_FRAME_SEND); + NbfDereferenceConnection ("SessionEnd destroyed", (PTP_CONNECTION)(Packet->Owner), CREF_LINK); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_RR: +#if defined(NBF_UP) + ((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next = + DeviceContext->RrPacketPool.Next; + DeviceContext->RrPacketPool.Next = + &((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next; +#else + ExInterlockedPushEntryList ( + &DeviceContext->RrPacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); +#endif + return; + + default: + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("NbfDestroyPacket: invalid action (%ld).\n", Packet->Action); + } + ASSERT (FALSE); + } + + + // + // Put the packet back for use again. + // + +#if defined(NBF_UP) + ((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next = + DeviceContext->PacketPool.Next; + DeviceContext->PacketPool.Next = + &((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next; +#else + ExInterlockedPushEntryList ( + &DeviceContext->PacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); +#endif + + // + // If there is a connection waiting to ship out more packets, then + // wake it up and start packetizing again. + // + // We do a quick check without the lock; there is a small + // window where we may not take someone off, but this + // window exists anyway and we assume that more packets + // will be freed in the future. + // + + if (IsListEmpty (&DeviceContext->PacketWaitQueue)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (!(IsListEmpty(&DeviceContext->PacketWaitQueue))) { + + // + // Remove a connection from the "packet starved" queue. + // + + p = RemoveHeadList (&DeviceContext->PacketWaitQueue); + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketWaitLinkage); + Connection->OnPacketWaitQueue = FALSE; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If this connection is starved because it couldn't send a + // control packet (SI, SC, RO, RC, or DA) then start that + // operation up again. Otherwise, just start packetizing. + // + + if (Connection->Flags & CONNECTION_FLAGS_STARVED) { + + Flags = Connection->Flags & CONNECTION_FLAGS_STARVED; + + if ((Flags & (Flags-1)) != 0) { + + // + // More than one bit is on, use only the low one + // (an arbitrary choice). + // + +#if DBG + DbgPrint ("NBF: Connection %lx has two flag bits on %lx\n", Connection, Connection->Flags); +#endif + Flags &= ~(Flags-1); + + } + + Connection->Flags &= ~Flags; + + if ((Connection->Flags & CONNECTION_FLAGS_W_PACKETIZE) || + (Connection->Flags & CONNECTION_FLAGS_STARVED)) { + + // + // We are waiting for both a specific packet and + // to packetize, or for two specific packets, so + // put ourselves back on the queue. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + if (!Connection->OnPacketWaitQueue) { + Connection->OnPacketWaitQueue = TRUE; + InsertTailList( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (Flags & CONNECTION_FLAGS_SEND_SI) { + NbfSendSessionInitialize (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_SC) { + NbfSendSessionConfirm (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_RO) { + NbfSendReceiveOutstanding (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_RC) { + NbfSendReceiveContinue (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_SE) { + NbfSendSessionEnd ( + Connection, + FALSE); + } else if (Flags & CONNECTION_FLAGS_SEND_DA) { + NbfSendDataAck (Connection); + } else { + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint0 ("NbfDestroyPacket: connection flags mismanaged.\n"); + } + } + + } else { + + // + // Place the connection on the packetize queue and start + // packetizing the next connection to be serviced. If he + // is already on the packetize queue for some reason, then + // don't do this. + // + // We shouldn't be packetizing in this case!! - adb (7/3/91). + // This used to be a check that did nothing if FLAGS_PACKETIZE + // was set, but if that happens something is wrong... + // + + ASSERT (Connection->Flags & CONNECTION_FLAGS_W_PACKETIZE); + Connection->Flags &= ~CONNECTION_FLAGS_W_PACKETIZE; + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + if ((Connection->Flags & CONNECTION_FLAGS_READY) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packet available", Connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + PacketizeConnections (DeviceContext); + + } + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + +} /* NbfDestroyPacket */ + +VOID NbfGrowSendPacketPool(PDEVICE_CONTEXT DeviceContext) +{ + + NDIS_STATUS NdisStatus; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + PTP_PACKET TransportSendPacket; + UINT i; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not grow send packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 107, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + return; + } + + for (i = 0; i < PACKET_POOL_GROW_COUNT; i += 1) { + NbfAllocateSendPacket(DeviceContext, &TransportSendPacket); + + if (TransportSendPacket != NULL) { + ExInterlockedPushEntryList(&(DeviceContext)->PacketPool, + (PSINGLE_LIST_ENTRY)&TransportSendPacket->Linkage, + &(DeviceContext)->Interlock); + } + else { + break; + } + } + + if (i == PACKET_POOL_GROW_COUNT) { + return; + } + +#ifdef DBG + DbgBreakPoint(); +#endif // DBG + +} + +#if DBG +VOID +NbfReferencePacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine increases the number of reasons why a packet cannot be + discarded. + +Arguments: + + Packet - Pointer to a packet to be referenced. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("NbfReferencePacket: Entered, NdisPacket: %lx Packet: %lx Ref Count: %lx.\n", + Packet->NdisPacket, Packet, Packet->ReferenceCount); + } + + result = InterlockedIncrement (&Packet->ReferenceCount); + + ASSERT (result >= 0); + +} /* NbfReferencePacket */ + + +VOID +NbfDereferencePacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine dereferences a transport packet by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyPacket to remove it from the system. + +Arguments: + + Packet - Pointer to a packet object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Packet->ReferenceCount); + + // + // If we have deleted all references to this packet, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the packet any longer. + // + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint1 ("NbfDereferencePacket: Entered, result: %lx\n", result); + } + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyPacket (Packet); + } + +} /* NbfDereferencePacket */ +#endif + + +VOID +NbfWaitPacket( + PTP_CONNECTION Connection, + ULONG Flags + ) + +/*++ + +Routine Description: + + This routine causes the specified connection to be put into a wait + state pending the availability of a packet to send the specified + frame. + +Arguments: + + Connection - Pointer to the connection object to be paused. + + Flags - Bitflag indicating which specific frame should be resent. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint0 ("NbfWaitPacket: Entered.\n"); + } + + DeviceContext = Connection->Provider; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Now put this connection on the device context's PacketWaitQueue, + // but only if it isn't already queued there. This state is managed + // with the OnPacketWaitQueue variable. + // + // If the connection is stopping, don't queue him either. + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) || + (Flags == CONNECTION_FLAGS_SEND_SE)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + // + // Turn on the bitflag that indicates which frame we couldn't send. + // + +#if DBG + if (Flags == CONNECTION_FLAGS_SEND_SE) { + DbgPrint ("NBF: Inserting connection %lx on PacketWait for SESSION_END\n", Connection); + } +#endif + Connection->Flags |= Flags; + + if (!Connection->OnPacketWaitQueue) { + + Connection->OnPacketWaitQueue = TRUE; + InsertTailList ( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + +} /* NbfWaitPacket */ + + +#if MAGIC +VOID +NbfSendMagicBullet ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine sends a magic bullet on the net that can be used to trigger + sniffers or other such things. + +Arguments: + + DeviceContext - pointer to the device context + + Link - This is needed to call NbfCreatePacket + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PTP_UI_FRAME RawFrame; + PUCHAR Header; + PNDIS_BUFFER NdisBuffer; + UINT i; + + UNREFERENCED_PARAMETER (Link); // no longer needed + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. +#if DBG + DbgPrint ("NbfSendMagicBullet: Couldn't allocate frame!\n"); +#endif + return; + } + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + DeviceContext->MagicBullet, + 32); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + Header = (PUCHAR)&RawFrame->Header; + + for (i=0;i<6;i++) { + Header[i] = MAGIC_BULLET_FOOD; + Header[i+6] = DeviceContext->LocalAddress.Address[i]; + } + + Header[12] = 0; + Header[13] = (UCHAR)(DeviceContext->UIFrameHeaderLength + 18); + + for (i=14;iUIFrameHeaderLength;i++) { + Header[i] = MAGIC_BULLET_FOOD; + } + + NdisChainBufferAtBack (RawFrame->NdisPacket, NdisBuffer); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback + + } + + return; + +} +#endif + diff --git a/private/ntos/tdi/nbf/precomp.h b/private/ntos/tdi/nbf/precomp.h new file mode 100644 index 000000000..4b3c3c642 --- /dev/null +++ b/private/ntos/tdi/nbf/precomp.h @@ -0,0 +1,219 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbf.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport + provider subcomponent of the NTOS project. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove PDI and PC586-specific support; add NDIS support + +--*/ + +#include + +#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#ifdef BUILD_FOR_511 +#define ExAllocatePoolWithTag(a,b,c) ExAllocatePool(a,b) +#endif + +typedef struct _RTL_SPLAY_LINKS { + struct _RTL_SPLAY_LINKS *Parent; + struct _RTL_SPLAY_LINKS *LeftChild; + struct _RTL_SPLAY_LINKS *RightChild; +} RTL_SPLAY_LINKS; +typedef RTL_SPLAY_LINKS *PRTL_SPLAY_LINKS; + +#define RtlInitializeSplayLinks(Links) { \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ + } + +#define RtlLeftChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->LeftChild \ + ) + +#define RtlRightChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->RightChild \ + ) + +#define RtlInsertAsLeftChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +#define RtlInsertAsRightChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + + +PRTL_SPLAY_LINKS +NTAPI +RtlDelete ( + PRTL_SPLAY_LINKS Links + ); + + +#include // Transport Driver Interface. + +#include // Physical Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "nbfconst.h" // private NETBEUI constants. +#include "nbfmac.h" // mac-specific definitions +#include "nbfhdrs.h" // private NETBEUI protocol headers. +#include "nbftypes.h" // private NETBEUI types. +#include "nbfcnfg.h" // configuration information. +#include "nbfprocs.h" // private NETBEUI function prototypes. +#ifdef MEMPRINT +#include "memprint.h" // drt's memory debug print +#endif + +#if defined(NT_UP) && defined(DRIVERS_UP) +#define NBF_UP 1 +#endif + +#ifndef NBF_LOCKS + +#if !defined(NBF_UP) + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) +#define ACQUIRE_DPC_SPIN_LOCK(lock) KeAcquireSpinLockAtDpcLevel(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) KeReleaseSpinLockFromDpcLevel(lock) + +#else // NBF_UP + +#define ACQUIRE_SPIN_LOCK(lock,irql) ExAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) ExReleaseSpinLock(lock,irql) +#define ACQUIRE_DPC_SPIN_LOCK(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) + +#endif + +#if DBG + +#define ACQUIRE_C_SPIN_LOCK(lock,irql) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + KeAcquireSpinLock(lock,irql); \ + _conn->LockAcquired = TRUE; \ + strncpy(_conn->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastAcquireLine = __LINE__; \ +} +#define RELEASE_C_SPIN_LOCK(lock,irql) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + _conn->LockAcquired = FALSE; \ + strncpy(_conn->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastReleaseLine = __LINE__; \ + KeReleaseSpinLock(lock,irql); \ +} + +#define ACQUIRE_DPC_C_SPIN_LOCK(lock) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + KeAcquireSpinLockAtDpcLevel(lock); \ + _conn->LockAcquired = TRUE; \ + strncpy(_conn->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastAcquireLine = __LINE__; \ +} +#define RELEASE_DPC_C_SPIN_LOCK(lock) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + _conn->LockAcquired = FALSE; \ + strncpy(_conn->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastReleaseLine = __LINE__; \ + KeReleaseSpinLockFromDpcLevel(lock); \ +} + +#else // DBG + +#define ACQUIRE_C_SPIN_LOCK(lock,irql) ACQUIRE_SPIN_LOCK(lock,irql) +#define RELEASE_C_SPIN_LOCK(lock,irql) RELEASE_SPIN_LOCK(lock,irql) +#define ACQUIRE_DPC_C_SPIN_LOCK(lock) ACQUIRE_DPC_SPIN_LOCK(lock) +#define RELEASE_DPC_C_SPIN_LOCK(lock) RELEASE_DPC_SPIN_LOCK(lock) + +#endif // DBG + +#define ENTER_NBF +#define LEAVE_NBF + +#else + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +#define ACQUIRE_SPIN_LOCK(lock,irql) \ + NbfAcquireSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) +#define RELEASE_SPIN_LOCK(lock,irql) \ + NbfReleaseSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) + +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { \ + KIRQL OldIrql; \ + NbfAcquireSpinLock( lock, &OldIrql, #lock, __FILE__, __LINE__ ); \ + } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + NbfReleaseSpinLock( lock, DISPATCH_LEVEL, #lock, __FILE__, __LINE__ ) + +#define ENTER_NBF \ + NbfAcquireSpinLock( (PKSPIN_LOCK)NULL, (PKIRQL)NULL, "(Global)", __FILE__, __LINE__ ) +#define LEAVE_NBF \ + NbfReleaseSpinLock( (PKSPIN_LOCK)NULL, (KIRQL)-1, "(Global)", __FILE__, __LINE__ ) + +#endif + diff --git a/private/ntos/tdi/nbf/rcv.c b/private/ntos/tdi/nbf/rcv.c new file mode 100644 index 000000000..8fdc87af9 --- /dev/null +++ b/private/ntos/tdi/nbf/rcv.c @@ -0,0 +1,309 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + rcv.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceive + o TdiReceiveDatagram + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiReceive( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceive request for the transport provider. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // Check that this is really a connection. + // + + if ((connection->Size != sizeof (TP_CONNECTION)) || + (connection->Type != NBF_CONNECTION_SIGNATURE)) { +#if DBG + NbfPrint2 ("TdiReceive: Invalid Connection %lx Irp %lx\n", connection, Irp); +#endif + return STATUS_INVALID_CONNECTION; + } + + // + // Initialize bytes transferred here. + // + + Irp->IoStatus.Information = 0; // reset byte transfer count. + + // This reference is removed by NbfDestroyRequest. + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + Irp->IoStatus.Status = connection->Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + status = STATUS_PENDING; + + } else { + KIRQL cancelIrql; + + // + // Once the reference is in, LinkSpinLock will be valid. + // + + NbfReferenceConnection("TdiReceive request", connection, CREF_RECEIVE_IRP); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + IRP_RECEIVE_IRP(irpSp) = Irp; + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + +#if DBG + NbfReceives[NbfReceivesNext].Irp = Irp; + NbfReceives[NbfReceivesNext].Request = NULL; + NbfReceives[NbfReceivesNext].Connection = (PVOID)connection; + NbfReceivesNext = (NbfReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + // + // If this IRP has been cancelled, complete it now. + // + + if (Irp->Cancel) { + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = Irp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)0; + + i = 1; + while (iNext; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + // + // It is safe to do this with locks held. + // + NbfCompleteReceiveIrp (Irp, STATUS_CANCELLED, 0); + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + } else { + + // + // Insert onto the receive queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->ReceiveQueue,&Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NbfCancelReceive); + + // + // Release the cancel spinlock out of order. Since we were + // already at dpc level when it was acquired, we don't + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // This call releases the link spinlock, and references the + // connection first if it needs to access it after + // releasing the lock. + // + + AwakenReceive (connection); // awaken if sleeping. + + } + + status = STATUS_PENDING; + + } + + KeLowerIrql (oldirql); + + return status; +} /* TdiReceive */ + + +NTSTATUS +NbfTdiReceiveDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PIO_STACK_LOCATION irpSp; + KIRQL cancelIrql; + + // + // verify that the operation is taking place on an address. At the same + // time we do this, we reference the address. This ensures it does not + // get removed out from under us. Note also that we do the address + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject (addressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + +#if DBG + if (((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))->ReceiveLength > 0) { + ASSERT (Irp->MdlAddress != NULL); + } +#endif + + address = addressFile->Address; + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & (ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_CONFLICT)) != 0) { + + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelIrql); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = (address->Flags & ADDRESS_FLAGS_STOPPING) ? + STATUS_NETWORK_NAME_DELETED : STATUS_DUPLICATE_NAME; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelIrql); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + IoSetCancelRoutine(Irp, NbfCancelReceiveDatagram); + NbfReferenceAddress ("Receive datagram", address, AREF_REQUEST); + InsertTailList (&addressFile->ReceiveDatagramQueue,&Irp->Tail.Overlay.ListEntry); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelIrql); + } + + } + + NbfDereferenceAddress ("Temp rcv datagram", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* TdiReceiveDatagram */ + diff --git a/private/ntos/tdi/nbf/rcveng.c b/private/ntos/tdi/nbf/rcveng.c new file mode 100644 index 000000000..f3b3eab33 --- /dev/null +++ b/private/ntos/tdi/nbf/rcveng.c @@ -0,0 +1,823 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + rcveng.c + +Abstract: + + This module contains code that implements the receive engine for the + Jetbeui transport provider. This code is responsible for the following + basic activities: + + 1. Transitioning a TdiReceive request from an inactive state on the + connection's ReceiveQueue to the active state on that connection + (ActivateReceive). + + 2. Advancing the status of the active receive request by copying 0 or + more bytes of data from an incoming DATA_FIRST_MIDDLE or DATA_ONLY_LAST + NBF frame. + + 3. Completing receive requests. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +VOID +ActivateReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine activates the next TdiReceive request on the specified + connection object if there is no active request on that connection + already. This allows the request to accept data on the connection. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ActivateReceive: Entered.\n"); + } + + // + // The ACTIVE_RECEIVE bitflag will be set on the connection if + // the receive-fields in the CONNECTION object are valid. If + // this flag is cleared, then we try to make the next TdiReceive + // request in the ReceiveQueue the active request. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + if (!IsListEmpty (&Connection->ReceiveQueue)) { + + // + // Found a receive, so make it the active one. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + + Irp = CONTAINING_RECORD( + Connection->ReceiveQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = Irp; + Connection->CurrentReceiveSynchronous = + Connection->Provider->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = Irp->MdlAddress; + Connection->ReceiveLength = IRP_RECEIVE_LENGTH(IoGetCurrentIrpStackLocation(Irp)); + Connection->ReceiveByteOffset = 0; + } + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ActivateReceive: Exiting.\n"); + } +} /* ActivateReceive */ + + +VOID +AwakenReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to reactivate a sleeping connection with the + RECEIVE_WAKEUP bitflag set because data arrived for which no receive + was available. The caller has made a receive available at the connection, + so here we activate the next receive, and send the appropriate protocol + to restart the message at the first byte offset past the one received + by the last receive. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. IT IS CALLED + WITH CONNECTION->LINKSPINLOCK HELD. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" AwakenReceive: Entered.\n"); + } + + // + // If the RECEIVE_WAKEUP bitflag is set, then awaken the connection. + // + + if (Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) { + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + Connection->Flags &= ~CONNECTION_FLAGS_RECEIVE_WAKEUP; + + // + // Found a receive, so turn off the wakeup flag, activate + // the next receive, and send the protocol. + // + + // + // Quick fix: So there is no window where a receive + // is active but the bit is not on (otherwise we could + // accept whatever data happens to show up in the + // interim). + // + + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + NbfReferenceConnection ("temp AwakenReceive", Connection, CREF_BY_ID); // release lookup hold. + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ActivateReceive (Connection); + + // + // BUGBUG: What if this fails? The successful queueing + // of a RCV_O should cause ActivateReceive to be called. + // + // NOTE: Send this after ActivateReceive, since that + // is where the MessageBytesAcked/Received variables + // are initialized. + // + + NbfSendReceiveOutstanding (Connection); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" AwakenReceive: Returned from NbfSendReceive.\n"); + } + + NbfDereferenceConnection("temp AwakenReceive", Connection, CREF_BY_ID); + return; + } + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); +} /* AwakenReceive */ + + +VOID +CompleteReceive( + PTP_CONNECTION Connection, + BOOLEAN EndOfMessage, + IN ULONG BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called by ProcessIncomingData when the current receive + must be completed. Depending on whether the current frame being + processed is a DATA_FIRST_MIDDLE or DATA_ONLY_LAST, and also whether + all of the data was processed, the EndOfMessage flag will be set accordingly + by the caller to indicate that a message boundary was received. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + EndOfMessage - BOOLEAN set to true if TDI_END_OF_RECORD should be reported. + + BytesTransferred - Number of bytes copied in this receive. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PIRP Irp; + ULONG BytesReceived; + PIO_STACK_LOCATION IrpSp; + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" CompleteReceive: Entered.\n"); + } + + + if (Connection->SpecialReceiveIrp) { + + PIRP Irp = Connection->SpecialReceiveIrp; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = BytesTransferred; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->Flags |= CONNECTION_FLAGS_RC_PENDING; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->SpecialReceiveIrp = FALSE; + + ++Connection->ReceivedTsdus; + + ExInterlockedInsertHeadList( + &Connection->Provider->IrpCompletionQueue, + &Irp->Tail.Overlay.ListEntry, + Connection->ProviderInterlock); + + // + // NOTE: NbfAcknowledgeDataOnlyLast releases + // the connection spinlock. + // + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + } else { + KIRQL cancelIrql; + + if (EndOfMessage) { + + // + // The messages has been completely received, ack it. + // + // We set DEFERRED_ACK and DEFERRED_NOT_Q here, which + // will cause an ack to be piggybacked if any data is + // sent during the call to CompleteReceive. If this + // does not happen, then we will call AcknowledgeDataOnlyLast + // which will will send a DATA ACK or queue a request for + // a piggyback ack. We do this *after* calling CompleteReceive + // so we know that we will complete the receive back to + // the client before we ack the data, to prevent the + // next receive from being sent before this one is + // completed. + // + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->DeferredFlags |= + (CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + Connection->Flags |= CONNECTION_FLAGS_RC_PENDING; + + } else { + + // + // Send a receive outstanding (even though we don't + // know that we have a receive) to get him to + // reframe his send. Pre-2.0 clients require a + // no receive before the receive outstanding. + // + // BUGBUG: what if this fails (due to no send packets)? + // + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + // + // If there is a receive posted, make it current and + // send a receive outstanding. + // + // BUGBUG: need general function for this, which sends + // NO_RECEIVE if appropriate. + // + + ActivateReceive (Connection); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveBytesUnaccepted) { + if (Connection->MessageBytesReceived >= Connection->ReceiveBytesUnaccepted) { + Connection->ReceiveBytesUnaccepted = 0; + } else { + Connection->ReceiveBytesUnaccepted -= Connection->MessageBytesReceived; + } + } + + // + // NOTE: The connection lock is held here. + // + + if (IsListEmpty (&Connection->ReceiveQueue)) { + + ASSERT ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0); + + // + // Release the cancel spinlock out of order. Since we were + // already at DPC level when it was acquired, there is no + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + } else { + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + BytesReceived = Connection->MessageBytesReceived; + + + // + // Complete the TdiReceive request at the head of the + // connection's ReceiveQueue. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" CompleteReceive: Normal IRP is present.\n"); + } + + p = RemoveHeadList (&Connection->ReceiveQueue); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IoSetCancelRoutine(Irp, NULL); + + // + // Release the cancel spinlock out of order. Since we were + // already at DPC level when it was acquired, there is no + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // If this request should generate no back traffic, then + // disable piggyback acks for it. + // + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + if (IRP_RECEIVE_FLAGS(IrpSp) & TDI_RECEIVE_NO_RESPONSE_EXP) { + Connection->CurrentReceiveAckQueueable = FALSE; + } + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = Irp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = + EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (iNext; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + ++Connection->ReceivedTsdus; + + // + // This can be called with locks held. + // + NbfCompleteReceiveIrp( + Irp, + EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + BytesReceived); + + } + + + // + // If NOT_Q is still set, that means that the deferred ack was + // not satisfied by anything resulting from the call to + // CompleteReceive, so we need to ack or queue an ack here. + // + + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) != 0) { + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + // + // NOTE: NbfAcknowledgeDataOnlyLast releases + // the connection spinlock. + // + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + } + +} /* CompleteReceive */ + + +VOID +NbfCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The receive is found on the connection's receive queue; if it + is the current request it is cancelled and the connection + goes into "cancelled receive" mode, otherwise it is cancelled + silently. + + In "cancelled receive" mode the connection makes it appear to + the remote the data is being received, but in fact it is not + indicated to the transport or buffered on our end + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PIRP ReceiveIrp; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + ULONG BytesReceived; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_RECEIVE)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + // + // See if this is the IRP for the current receive request. + // + + ACQUIRE_SPIN_LOCK (Connection->LinkSpinLock, &oldirql); + + BytesReceived = Connection->MessageBytesReceived; + + p = Connection->ReceiveQueue.Flink; + + // + // If there is a receive active and it is not a special + // IRP, then see if this is it. + // + + if (((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0) && + (!Connection->SpecialReceiveIrp)) { + + ReceiveIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + if (ReceiveIrp == Irp) { + + // + // yes, it is the active receive. Turn on the RCV_CANCELLED + // bit instructing the connection to drop the rest of the + // data received (until the DOL comes in). + // + + Connection->Flags |= CONNECTION_FLAGS_RCV_CANCELLED; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + (VOID)RemoveHeadList (&Connection->ReceiveQueue); + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = ReceiveIrp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = ReceiveIrp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (iNext; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled in-progress receive %lx on %lx\n", + Irp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteReceiveIrp (ReceiveIrp, STATUS_CANCELLED, 0); + return; + + } + + } + + + // + // If we fall through to here, the IRP was not the active receive. + // Scan through the list, looking for this IRP. + // + + Found = FALSE; + + while (p != &Connection->ReceiveQueue) { + + ReceiveIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (ReceiveIrp == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = ReceiveIrp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = ReceiveIrp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (iNext; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled receive %lx on %lx\n", + ReceiveIrp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteReceiveIrp (ReceiveIrp, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + +#if DBG + DbgPrint("NBF: Tried to cancel receive %lx on %lx, not found\n", + Irp, Connection); +#endif + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + +} + + +VOID +NbfCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The receive is looked for on the address file's + receive datagram queue; if it is found it is cancelled. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PLIST_ENTRY p; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_RECEIVE_DATAGRAM)); + + AddressFile = IrpSp->FileObject->FsContext; + Address = AddressFile->Address; + + // + // Since this IRP is still in the cancellable state, we know + // that the address file is still around (although it may be in + // the process of being torn down). See if the IRP is on the list. + // + + Found = FALSE; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (CONTAINING_RECORD(p, IRP, Tail.Overlay.ListEntry) == Irp) { + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + +#if DBG + DbgPrint("NBF: Canceled receive datagram %lx on %lx\n", + Irp, AddressFile); +#endif + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Receive DG cancelled", Address, AREF_REQUEST); + + } else { + +#if DBG + DbgPrint("NBF: Tried to cancel receive datagram %lx on %lx, not found\n", + Irp, AddressFile); +#endif + + } + +} /* NbfCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/nbf/request.c b/private/ntos/tdi/nbf/request.c new file mode 100644 index 000000000..5547e31ee --- /dev/null +++ b/private/ntos/tdi/nbf/request.c @@ -0,0 +1,1376 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + This module contains code which implements the TP_REQUEST object. + Routines are provided to create, destroy, reference, and dereference, + transport request objects. + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include +#include +#endif // RASAUTODIAL + +// +// External variables +// +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported routines +// +VOID +NbfNoteNewConnection( + PTP_CONNECTION Connection, + PDEVICE_CONTEXT DeviceContext + ); +#endif // RASAUTODIAL + + +VOID +NbfTdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when a request + such as TdiSend, TdiReceive, TdiSendDatagram, TdiReceiveDatagram, etc., + encounters a timeout. This routine cleans up the activity and cancels it. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_REQUEST block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + PTP_CONNECTION Connection; +#if DBG + LARGE_INTEGER time, difference; +#endif + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PDEVICE_CONTEXT DeviceContext; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Request = (PTP_REQUEST)DeferredContext; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("RequestTimeoutHandler: Entered, Request %lx\n", Request); + } + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + Request->Flags &= ~REQUEST_FLAGS_TIMER; + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + +#if DBG + KeQuerySystemTime (&time); + difference.QuadPart = time.QuadPart - (Request->Time).QuadPart; + NbfPrint1 ("RequestTimeoutHandler: Request timed out, queued for %ld seconds\n", + difference.LowPart / SECONDS); +#endif + + // + // find reason for timeout + // + + IrpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + if (IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) { + switch (IrpSp->MinorFunction) { + + // + // none of these should time out. + // + + case TDI_SEND: + case TDI_ACCEPT: + case TDI_SET_INFORMATION: + case TDI_SET_EVENT_HANDLER: + case TDI_SEND_DATAGRAM: + case TDI_RECEIVE_DATAGRAM: + case TDI_RECEIVE: + +#if DBG + NbfPrint1 ("RequestTimeoutHandler: Request: %lx Timed out, and shouldn't have!\n", + Request); +#endif + ASSERT (FALSE); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfCompleteRequest (Request, STATUS_IO_TIMEOUT, 0); + break; + + + case TDI_LISTEN: + case TDI_CONNECT: + +#if DBG + NbfPrint2 ("RequestTimeoutHandler: %s Failed, Request: %lx\n", + IrpSp->MinorFunction == TDI_LISTEN ? + "Listen" : + IrpSp->MinorFunction == TDI_CONNECT ? + "Connect" : "Disconnect", + Request); +#endif + Connection = (PTP_CONNECTION)(Request->Context); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Since these requests are part of the connection + // itself, we just stop the connection and the + // request will get torn down then. If we get the + // situation where the request times out before + // it is queued to the connection, then the code + // that is about to queue it will check the STOPPING + // flag and complete it then. + // + // Don't stop the connection if an automatic connection + // is in progress. + // + +#if DBG + DbgPrint("RequestTimeoutHandler: AUTOCONNECTING=0x%x\n", Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING); +#endif + if (!(Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING)) + NbfStopConnection (Connection, STATUS_IO_TIMEOUT); + break; + + case TDI_DISCONNECT: + + // + // We don't create requests for TDI_DISCONNECT any more. + // + + ASSERT(FALSE); + break; + + case TDI_QUERY_INFORMATION: + + DeviceContext = (PDEVICE_CONTEXT)IrpSp->FileObject->DeviceObject; + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters; + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint1 ("RequestTimeout: %lx:\n", DeviceContext); + } + + // + // Determine if the request is done, or if we should + // requeue it. + // + + --Request->Retries; + + if (Request->Retries > 0) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Send another packet out, and restart the timer. + // + + if (query->QueryType == TDI_QUERY_FIND_NAME) { + + NbfSendQueryFindName ( + DeviceContext, + Request); + + } else if (query->QueryType == TDI_QUERY_ADAPTER_STATUS) { + + PUCHAR SingleSR; + UINT SingleSRLength; + + // + // Send the STATUS_QUERY frames out as + // single-route source routing. + // + // BUGBUG: On a second status query this should + // really be sent directed, but currently we + // don't record the address anywhere. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + NbfSendStatusQuery ( + DeviceContext, + Request, + &DeviceContext->NetBIOSAddress, + SingleSR, + SingleSRLength); + + } else { + + ASSERT (FALSE); + + } + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // That's it, we retried enough, complete it. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (Request->BytesWritten > 0) { + + NbfCompleteRequest (Request, STATUS_SUCCESS, Request->BytesWritten); + + } else { + + NbfCompleteRequest (Request, STATUS_IO_TIMEOUT, Request->BytesWritten); + + } + + + } + + break; + + default: +#if DBG + NbfPrint2 ("RequestTimeoutHandler: Unknown Request Timed out, Request: %lx Type: %x\n", + Request, IrpSp->MinorFunction); +#endif + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + break; + + } // end of switch + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + + NbfDereferenceRequest ("Timeout", Request, RREF_TIMER); // for the timeout + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Timeout: stopping", Request, RREF_TIMER); // for the timeout + + } + + LEAVE_NBF; + return; + +} /* RequestTimeoutHandler */ + + +VOID +NbfAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ) + +/*++ + +Routine Description: + + This routine allocates a request packet from nonpaged pool and initializes + it to a known state. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a place where this routine will return + a pointer to a transport request structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_REQUEST Request; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_REQUEST)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate request: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 104, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + *TransportRequest = NULL; + return; + } + + Request = (PTP_REQUEST)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_REQUEST), + 'rFBN'); + if (Request == NULL) { + PANIC("NBF: Could not allocate request: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 204, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + *TransportRequest = NULL; + return; + } + RtlZeroMemory (Request, sizeof(TP_REQUEST)); + + DeviceContext->MemoryUsage += sizeof(TP_REQUEST); + ++DeviceContext->RequestAllocated; + + Request->Type = NBF_REQUEST_SIGNATURE; + Request->Size = sizeof (TP_REQUEST); + + Request->ResponseBuffer = NULL; + + Request->Provider = DeviceContext; + Request->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Request->SpinLock); + KeInitializeDpc (&Request->Dpc, NbfTdiRequestTimeoutHandler, (PVOID)Request); + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + + *TransportRequest = Request; + +} /* NbfAllocateRequest */ + + +VOID +NbfDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ) + +/*++ + +Routine Description: + + This routine frees a request packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a transport request structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportRequest); + --DeviceContext->RequestAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_REQUEST); + +} /* NbfDeallocateRequest */ + + +NTSTATUS +NbfCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ) + +/*++ + +Routine Description: + + This routine creates a transport request and associates it with the + specified IRP, context, and queue. All major requests, including + TdiSend, TdiSendDatagram, TdiReceive, and TdiReceiveDatagram requests, + are composed in this manner. + +Arguments: + + Irp - Pointer to an IRP which was received by the transport for this + request. + + Context - Pointer to anything to associate this request with. This + value is not interpreted except at request cancelation time. + + Flags - A set of bitflags indicating the disposition of this request. + + Timeout - Timeout value (if non-zero) to start a timer for this request. + If zero, then no timer is activated for the request. + + TpRequest - If the function returns STATUS_SUCCESS, this will return + pointer to the TP_REQUEST structure allocated. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PLIST_ENTRY p; + PIO_STACK_LOCATION irpSp; +#if DBG + LARGE_INTEGER Time; +#endif + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint0 ("NbfCreateRequest: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation(Irp); + DeviceContext = (PDEVICE_CONTEXT)irpSp->FileObject->DeviceObject; + +#if DBG + if (!MmIsNonPagedSystemAddressValid (DeviceContext->RequestPool.Flink)) { + NbfPrint2 ("NbfCreateRequest: RequestList hosed: %lx DeviceContext: %lx\n", + &DeviceContext->RequestPool, DeviceContext); + } +#endif + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->RequestPool); + if (p == &DeviceContext->RequestPool) { + + if ((DeviceContext->RequestMaxAllocated == 0) || + (DeviceContext->RequestAllocated < DeviceContext->RequestMaxAllocated)) { + + NbfAllocateRequest (DeviceContext, &Request); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated request at %lx\n", Request); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 404, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + Request = NULL; + + } + + if (Request == NULL) { + ++DeviceContext->RequestExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateConnection: Could not allocate request object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + } + + ++DeviceContext->RequestInUse; + if (DeviceContext->RequestInUse > DeviceContext->RequestMaxInUse) { + ++DeviceContext->RequestMaxInUse; + } + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // fill out the request. + // + + // Request->Provider = DeviceContext; + Request->IoRequestPacket = Irp; + Request->Buffer2 = Buffer2; + Request->Buffer2Length = Buffer2Length; + Request->Flags = Flags; + Request->Context = Context; + Request->ReferenceCount = 1; // initialize reference count. + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_RREFS; Counter++) { + Request->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfCompleteRequest + + Request->RefTypes[RREF_CREATION] = 1; + } +#endif + +#if DBG + Request->Completed = FALSE; + Request->Destroyed = FALSE; + Request->TotalReferences = 0; + Request->TotalDereferences = 0; + Request->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalRequestList, &Request->GlobalLinkage, &NbfGlobalInterlock); + StoreRequestHistory (Request, TRUE); +#endif + +#if DBG + KeQuerySystemTime (&Time); // ugly, but effective + Request->Time.LowPart = Time.LowPart; + Request->Time.HighPart = Time.HighPart; +#endif + + IF_NBFDBG (NBF_DEBUG_IRP) { + if (Irp->MdlAddress != NULL) { + PMDL mdl; + NbfPrint2 ("NbfCreateRequest: Map request %lx Irp %lx MdlChain \n", + Request, Request->IoRequestPacket); + mdl = Request->Buffer2; + while (mdl != NULL) { + NbfPrint4 ("Mdl %lx Va %lx StartVa %lx Flags %x\n", + mdl, MmGetSystemAddressForMdl(mdl), MmGetMdlVirtualAddress(mdl), + mdl->MdlFlags); + mdl = mdl->Next; + } + } + } + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint3 ("NbfCreateRequest: Request %lx Buffer2: %lx Irp: %lx\n", Request, + Buffer2, Irp); + } + + if ((Timeout.LowPart == 0) && (Timeout.HighPart == 0)) { + + // no timeout + } else { + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint3 ("NbfCreateRequest: Starting timer %lx%lx Flags %lx\n", + Timeout.HighPart, Timeout.LowPart, Request->Flags); + } + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Create: timer", Request, RREF_TIMER); // one for the timer + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + } + + *TpRequest = Request; + + return STATUS_SUCCESS; +} /* NbfCreateRequest */ + + +VOID +NbfDestroyRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine returns a request block to the free pool. + +Arguments: + + Request - Pointer to a TP_REQUEST block to return to the free pool. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint0 ("NbfDestroyRequest: Entered.\n"); + } + +#if DBG + if (Request->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed request 0x%lx\n", Request); + DbgBreakPoint (); + } + Request->Destroyed = TRUE; +#if 1 + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&Request->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#else + ExInterlockedRemoveHeadList (Request->GlobalLinkage.Blink, &NbfGlobalInterlock); +#endif +#endif + ASSERT(Request->Completed); + + // + // Return the request to the caller with whatever status is in the IRP. + // + + IF_NBFDBG (NBF_DEBUG_IRP) { + NbfPrint1 ("NbfCompleteRequest: Completing IRP: %lx\n", + Request->IoRequestPacket); + } + + // + // Now dereference the owner of this request so that we are safe when + // we finally tear down the {connection, address}. The problem we're + // facing here is that we can't allow the user to assume semantics; + // the end of life for a connection must truly be the real end of life. + // for that to occur, we reference the owning object when the request is + // created and we dereference it just before we return it to the pool. + // + + switch (Request->Owner) { + case ConnectionType: + NbfDereferenceConnection ("Removing Connection",((PTP_CONNECTION)Request->Context), CREF_REQUEST); + break; + +#if DBG + case AddressType: + ASSERT (FALSE); + NbfDereferenceAddress ("Removing Address", ((PTP_ADDRESS)Request->Context), AREF_REQUEST); + break; +#endif + + case DeviceContextType: + NbfDereferenceDeviceContext ("Removing Address", ((PDEVICE_CONTEXT)Request->Context), DCREF_REQUEST); + break; + } + + // + // Unmap a possibly mapped buffer. We've only mapped the buffer if the + // Irp Major function is not method 0. (of 0, 1, 2, and 3.) + // + + IF_NBFDBG (NBF_DEBUG_IRP) { + { + PMDL mdl; + NbfPrint2 ("NbfDestroyRequest: Unmap request %lx Irp %lx MdlChain \n", + Request, Request->IoRequestPacket); + mdl = Request->Buffer2; + while (mdl != NULL) { + NbfPrint4 ("Mdl %lx Va %lx StartVa %lx Flags %x\n", + mdl, MmGetSystemAddressForMdl(mdl), MmGetMdlVirtualAddress(mdl), + mdl->MdlFlags); + mdl = mdl->Next; + } + } + } + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + DeviceContext = Request->Provider; + + LEAVE_NBF; + IoCompleteRequest (Request->IoRequestPacket, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + // + // Put the request back on the free list. NOTE: we have the + // lock held here. + // + + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + --DeviceContext->RequestInUse; + + if ((DeviceContext->RequestAllocated - DeviceContext->RequestInUse) > + DeviceContext->RequestInitAllocated) { + NbfDeallocateRequest (DeviceContext, Request); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated request at %lx\n", Request); + } + } else { + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} /* NbfDestroyRequest */ + + +#if DBG +VOID +NbfRefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport request. + +Arguments: + + Request - Pointer to a TP_REQUEST block. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefRequest: Entered, ReferenceCount: %x\n", + Request->ReferenceCount); + } + +#if DBG + StoreRequestHistory( Request, TRUE ); +#endif + + ASSERT (Request->ReferenceCount > 0); + + result = InterlockedIncrement (&Request->ReferenceCount); + +} /* NbfRefRequest */ +#endif + + +VOID +NbfDerefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine dereferences a transport request by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyRequest to remove it from the system. + +Arguments: + + Request - Pointer to a transport request object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefRequest: Entered, ReferenceCount: %x\n", + Request->ReferenceCount); + } + +#if DBG + StoreRequestHistory( Request, FALSE ); +#endif + + result = InterlockedDecrement (&Request->ReferenceCount); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + NbfDestroyRequest (Request); + } + +} /* NbfDerefRequest */ + + +VOID +NbfCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport request object, completing the I/O, + stopping the timeout, and freeing up the request object itself. + +Arguments: + + Request - Pointer to a transport request object. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + NTSTATUS FinalStatus = Status; + NTSTATUS CopyStatus; + BOOLEAN TimerWasSet; + + ASSERT (Status != STATUS_PENDING); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteRequest: Entered Request %lx, Request->Flags %lx\n", + Request, Request->Flags); + } + +#if DBG + if (Request->Completed) { + NbfPrint1 ("attempt to completed already-completed request 0x%lx\n", Request); + DbgBreakPoint (); + } + Request->Completed = TRUE; +#endif + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + Request->Flags |= REQUEST_FLAGS_STOPPING; + + // + // Cancel the pending timeout on this request. Not all requests + // have their timer set. If this request has the TIMER bit set, + // then the timer needs to be cancelled. If it cannot be cancelled, + // then the timer routine will be run, so we just return and let + // the timer routine worry about cleaning up this request. + // + + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + TimerWasSet = KeCancelTimer (&Request->Timer); + + if (TimerWasSet) { + NbfDereferenceRequest ("Complete: stop timer", Request, RREF_TIMER); + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfCompleteRequest: Canceled timer: %lx.\n", &Request->Timer); + } + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Irp = Request->IoRequestPacket; + +#ifdef RASAUTODIAL + // + // If this is a connect operation that has + // returned with either STATUS_SUCCESS or + // STATUS_BAD_NETWORK_PATH, then + // inform the automatic connection driver. + // + if (fAcdLoadedG) { + IrpSp = IoGetCurrentIrpStackLocation(Irp); + if (IrpSp->MinorFunction == TDI_CONNECT && + FinalStatus == STATUS_SUCCESS) + { + KIRQL adirql; + BOOLEAN fEnabled; + + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled) { + NbfNoteNewConnection( + IrpSp->FileObject->FsContext, + (PDEVICE_CONTEXT)IrpSp->FileObject->DeviceObject); + } + } + } +#endif // RASAUTODIAL + + // + // For requests associated with a device context, we need + // to copy the data from the temp buffer to the MDL and + // free the temp buffer. + // + + if (Request->ResponseBuffer != NULL) { + + if ((FinalStatus == STATUS_SUCCESS) || + (FinalStatus == STATUS_BUFFER_OVERFLOW)) { + + CopyStatus = TdiCopyBufferToMdl ( + Request->ResponseBuffer, + 0L, + Information, + Irp->MdlAddress, + 0, + &Information); + + if (CopyStatus != STATUS_SUCCESS) { + FinalStatus = CopyStatus; + } + + } + + ExFreePool (Request->ResponseBuffer); + Request->ResponseBuffer = NULL; + + } + + // + // Install the return code in the IRP so that when we call NbfDestroyRequest, + // it will get completed with the proper return status. + // + + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + // + // The entire transport is done with this request. + // + + NbfDereferenceRequest ("Complete", Request, RREF_CREATION); // remove creation reference. + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + +} /* NbfCompleteRequest */ + + +#if DBG +VOID +NbfRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a send IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefSendIrp: Entered, ReferenceCount: %x\n", + IRP_SEND_REFCOUNT(IrpSp)); + } + + ASSERT (IRP_SEND_REFCOUNT(IrpSp) > 0); + + InterlockedIncrement (&IRP_SEND_REFCOUNT(IrpSp)); + +} /* NbfRefSendIrp */ + + +VOID +NbfDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport send IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport send IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefSendIrp: Entered, ReferenceCount: %x\n", + IRP_SEND_REFCOUNT(IrpSp)); + } + + result = InterlockedDecrement (&IRP_SEND_REFCOUNT(IrpSp)); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + + PIRP Irp = IRP_SEND_IRP(IrpSp); + + IRP_SEND_REFCOUNT(IrpSp) = 0; + IRP_SEND_IRP (IrpSp) = NULL; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + +} /* NbfDerefSendIrp */ +#endif + + +VOID +NbfCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport send IRP. + +Arguments: + + Irp - Pointer to a send IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_SEND_CONNECTION(IrpSp); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteSendIrp: Entered IRP %lx, connection %lx\n", + Irp, Connection); + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + NbfDereferenceSendIrp ("Complete", IrpSp, RREF_CREATION); // remove creation reference. + + NbfDereferenceConnectionMacro ("Removing Connection", Connection, CREF_SEND_IRP); + +} /* NbfCompleteSendIrp */ + + +#if DBG +VOID +NbfRefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a receive IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefReceiveIrpLocked: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + ASSERT (IRP_RECEIVE_REFCOUNT(IrpSp) > 0); + + IRP_RECEIVE_REFCOUNT(IrpSp)++; + +} /* NbfRefReceiveIrpLocked */ +#endif + + +VOID +NbfDerefReceiveIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport receive IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport receive IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefReceiveIrp: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + result = ExInterlockedAddUlong ( + (PULONG)&IRP_RECEIVE_REFCOUNT(IrpSp), + (ULONG)-1, + (IRP_RECEIVE_CONNECTION(IrpSp)->LinkSpinLock)); + + ASSERT (result > 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 1) { + + PIRP Irp = IRP_RECEIVE_IRP(IrpSp); + + ExInterlockedInsertTailList( + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), + &Irp->Tail.Overlay.ListEntry, + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); + + } + +} /* NbfDerefReceiveIrp */ + + +#if DBG +VOID +NbfDerefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport receive IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport receive IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefReceiveIrpLocked: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + result = IRP_RECEIVE_REFCOUNT(IrpSp)--; + + ASSERT (result > 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 1) { + + PIRP Irp = IRP_RECEIVE_IRP(IrpSp); + + ExInterlockedInsertTailList( + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), + &Irp->Tail.Overlay.ListEntry, + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); + + } + +} /* NbfDerefReceiveIrpLocked */ +#endif + + +VOID +NbfCompleteReceiveIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport receive IRP. + + NOTE: THIS ROUTINE MUST BE CALLED WITH THE CONNECTION SPINLOCK + HELD. + +Arguments: + + Irp - Pointer to a receive IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_RECEIVE_CONNECTION(IrpSp); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteReceiveIrp: Entered IRP %lx, connection %lx\n", + Irp, Connection); + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + NbfDereferenceReceiveIrpLocked ("Complete", IrpSp, RREF_CREATION); // remove creation reference. + +} /* NbfCompleteReceiveIrp */ diff --git a/private/ntos/tdi/nbf/send.c b/private/ntos/tdi/nbf/send.c new file mode 100644 index 000000000..a8a6cc7f3 --- /dev/null +++ b/private/ntos/tdi/nbf/send.c @@ -0,0 +1,503 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSend + o TdiSendDatagram + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiSend( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSend request for the transport provider. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, cancelIrql; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SEND parameters; + PIRP TempIrp; + + // + // Determine which connection this send belongs on. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // Check that this is really a connection. + // + + if ((connection->Size != sizeof (TP_CONNECTION)) || + (connection->Type != NBF_CONNECTION_SIGNATURE)) { +#if DBG + NbfPrint2 ("TdiSend: Invalid Connection %lx Irp %lx\n", connection, Irp); +#endif + return STATUS_INVALID_CONNECTION; + } + +#if DBG + Irp->IoStatus.Information = 0; // initialize it. + Irp->IoStatus.Status = 0x01010101; // initialize it. +#endif + + // + // Interpret send options. + // + +#if DBG + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + if ((parameters->SendFlags & TDI_SEND_PARTIAL) != 0) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("NbfTdiSend: TDI_END_OF_RECORD not found.\n"); + } + } +#endif + + // + // Now we have a reference on the connection object. Queue up this + // send to the connection object. + // + + // + // We would normally add a connection reference of type + // CREF_SEND_IRP, however we delay doing this until we + // know we are not going to call PacketizeSend with the + // second parameter TRUE. If we do call that it assumes + // we have not added the reference. + // + + IRP_SEND_IRP(irpSp) = Irp; + IRP_SEND_REFCOUNT(irpSp) = 1; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + Irp->IoStatus.Status = connection->Status; + Irp->IoStatus.Information = 0; + + NbfDereferenceSendIrp ("Complete", irpSp, RREF_CREATION); // remove creation reference. + + } else { + + // + // Once the reference is in, LinkSpinLock will stay valid. + // + + NbfReferenceConnection ("Verify Temp Use", connection, CREF_BY_ID); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + +#if DBG + NbfSends[NbfSendsNext].Irp = Irp; + NbfSends[NbfSendsNext].Request = NULL; + NbfSends[NbfSendsNext].Connection = (PVOID)connection; + { + ULONG i,j; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + if (parameters->SendLength > TRACK_TDI_CAPTURE) { + NbfSends[NbfSendsNext].Contents[0] = 0xFF; + } else { + NbfSends[NbfSendsNext].Contents[0] = (UCHAR)parameters->SendLength; + } + + i = 1; + while (i < TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + for ( va = MmGetSystemAddressForMdl (mdl), + j = MmGetMdlByteCount (mdl); + (i < TRACK_TDI_CAPTURE) && (j > 0); + i++, j-- ) { + NbfSends[NbfSendsNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfSendsNext++; + if (NbfSendsNext >= TRACK_TDI_LIMIT) NbfSendsNext = 0; +#endif + + // + // If this IRP has been cancelled already, complete it now. + // + + if (Irp->Cancel) { + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + NbfReferenceConnection("TdiSend cancelled", connection, CREF_SEND_IRP); + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp (Irp, STATUS_CANCELLED, 0); + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + // + // Insert onto the send queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NbfCancelSend); + + // + // Release the cancel spinlock out of order. We were at DPC level + // when we acquired both the cancel and link spinlocks, so the irqls + // don't need to be swapped. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // If this connection is waiting for an EOR to appear because a non-EOR + // send failed at some point in the past, fail this send. Clear the + // flag that causes this if this request has the EOR set. + // + // BUGBUG: Should the FailSend status be clearer here? + // + + if ((connection->Flags & CONNECTION_FLAGS_FAILING_TO_EOR) != 0) { + + NbfReferenceConnection("TdiSend failing to EOR", connection, CREF_SEND_IRP); + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + // + // BUGBUG: Should we save status from real failure? + // + + FailSend (connection, STATUS_LINK_FAILED, TRUE); + + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR; + } + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Failing to EOR", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + + // + // If the send state is either IDLE or W_EOR, then we should + // begin packetizing this send. Otherwise, some other event + // will cause it to be packetized. + // + + // + // NOTE: If we call StartPacketizingConnection, we make + // sure that it is the last operation we do on this + // connection. This allows us to "hand off" the reference + // we have to that function, which converts it into + // a reference for being on the packetize queue. + // + +// NbfPrint2 ("TdiSend: Sending, connection %lx send state %lx\n", +// connection, connection->SendState); + + switch (connection->SendState) { + + case CONNECTION_SENDSTATE_IDLE: + + InitializeSend (connection); // sets state to PACKETIZE + + // + // If we can, packetize right now. + // + + if (!(connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + +#if DBG + NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE); + NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); +#endif + + // + // This releases the spinlock. Note that PacketizeSend + // assumes that the current SendIrp has a reference + // of type RREF_PACKET; + // + +#if DBG + NbfReferenceSendIrp ("Packetize", irpSp, RREF_PACKET); +#else + ++IRP_SEND_REFCOUNT(irpSp); // OK since it was just queued. +#endif + PacketizeSend (connection, TRUE); + + } else { + +#if DBG + NbfReferenceConnection("TdiSend packetizing", connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Stopping or already packetizing", connection, CREF_BY_ID); // release lookup hold. +#endif + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } + + break; + + case CONNECTION_SENDSTATE_W_EOR: + connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + // + // Adjust the send variables on the connection so that + // they correctly point to this new send. We can't call + // InitializeSend to do that, because we need to keep + // track of the other outstanding sends on this connection + // which have been sent but are a part of this message. + // + + TempIrp = CONTAINING_RECORD( + connection->SendQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + + connection->sp.CurrentSendIrp = TempIrp; + connection->sp.CurrentSendMdl = TempIrp->MdlAddress; + connection->sp.SendByteOffset = 0; + connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(TempIrp)); + + // + // StartPacketizingConnection removes the CREF_BY_ID + // reference. + // + + NbfReferenceConnection("TdiSend W_EOR", connection, CREF_SEND_IRP); + + StartPacketizingConnection (connection, TRUE); + break; + + default: +// NbfPrint2 ("TdiSend: Sending, unknown state! connection %lx send state %lx\n", +// connection, connection->SendState); + // + // The connection is in another state (such as + // W_ACK or W_LINK), we just need to make sure + // to call InitializeSend if the new one is + // the first one on the list. + // + + // + // BUGBUG: Currently InitializeSend sets SendState, + // we should fix this. + // + + if (connection->SendQueue.Flink == &Irp->Tail.Overlay.ListEntry) { + ULONG SavedSendState; + SavedSendState = connection->SendState; + InitializeSend (connection); + connection->SendState = SavedSendState; + } + +#if DBG + NbfReferenceConnection("TdiSend other", connection, CREF_SEND_IRP); + NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); +#endif + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } + + } + + KeLowerIrql (oldirql); + return STATUS_PENDING; + +} /* TdiSend */ + + +NTSTATUS +NbfTdiSendDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SENDDG parameters; + UINT MaxUserData; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("TdiSendDG: Invalid address %lx Irp %lx\n", + addressFile, Irp); + } + return status; + } + + address = addressFile->Address; + parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters); + + // + // Check that the length is short enough. + // + + MacReturnMaxDataSize( + &address->Provider->MacInfo, + NULL, + 0, + address->Provider->MaxSendPacketSize, + FALSE, + &MaxUserData); + + if (parameters->SendLength > + (MaxUserData - sizeof(DLC_FRAME) - sizeof(NBF_HDR_CONNECTIONLESS))) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_INVALID_PARAMETER; + + } + + // + // If we are on a disconnected RAS link, then fail the datagram + // immediately. + // + + if ((address->Provider->MacInfo.MediumAsync) && + (!address->Provider->MediumSpeedAccurate)) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_DEVICE_NOT_READY; + } + + // + // Check that the target address includes a Netbios component. + // + + if (!(NbfValidateTdiAddress( + parameters->SendDatagramInformation->RemoteAddress, + parameters->SendDatagramInformation->RemoteAddressLength)) || + (NbfParseTdiAddress(parameters->SendDatagramInformation->RemoteAddress, TRUE) == NULL)) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_BAD_NETWORK_PATH; + } + + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & (ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_CONFLICT)) != 0) { + + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = (address->Flags & ADDRESS_FLAGS_STOPPING) ? + STATUS_NETWORK_NAME_DELETED : STATUS_DUPLICATE_NAME; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + NbfReferenceAddress ("Send datagram", address, AREF_REQUEST); + Irp->IoStatus.Information = parameters->SendLength; + InsertTailList ( + &address->SendDatagramQueue, + &Irp->Tail.Overlay.ListEntry); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + + // + // The request is queued. Ship the next request at the head of the queue, + // provided the completion handler is not active. We serialize this so + // that only one MDL and NBF datagram header needs to be statically + // allocated for reuse by all send datagram requests. + // + + (VOID)NbfSendDatagramsOnAddress (address); + + } + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* NbfTdiSendDatagram */ diff --git a/private/ntos/tdi/nbf/sendeng.c b/private/ntos/tdi/nbf/sendeng.c new file mode 100644 index 000000000..1b6556581 --- /dev/null +++ b/private/ntos/tdi/nbf/sendeng.c @@ -0,0 +1,3657 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + sendeng.c + +Abstract: + + This module contains code that implements the send engine for the + Jetbeui transport provider. This code is responsible for the following + basic activities, including some subordinate glue. + + 1. Packetizing TdiSend requests already queued up on a TP_CONNECTION + object, using I-frame packets acquired from the PACKET.C module, + and turning them into shippable packets and placing them on the + TP_LINK's WackQ. In the process of doing this, the packets are + actually submitted as I/O requests to the Physical Provider, in + the form of PdiSend requests. + + 2. Retiring packets queued to a TP_LINK's WackQ and returning them to + the device context's pool for use by other links. In the process + of retiring acked packets, step 1 may be reactivated. + + 3. Resending packets queued to a TP_LINK's WackQ because of a reject + condition on the link. This involves no state update in the + TP_CONNECTION object. + + 4. Handling of Send completion events from the Physical Provider, + to allow proper synchronization of the reuse of packets. + + 5. Completion of TdiSend requests. This is triggered by the receipt + (in IFRAMES.C) of a DataAck frame, or by a combination of other + frames when the proper protocol has been negotiated. One routine + in this routine is responsible for the actual mechanics of TdiSend + request completion. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +extern ULONG NbfSendsIssued; +extern ULONG NbfSendsCompletedInline; +extern ULONG NbfSendsCompletedOk; +extern ULONG NbfSendsCompletedFail; +extern ULONG NbfSendsPended; +extern ULONG NbfSendsCompletedAfterPendOk; +extern ULONG NbfSendsCompletedAfterPendFail; +#endif + + +// +// Temporary variables to control piggyback ack usage. +// +#define NbfUsePiggybackAcks 1 +#if DBG +ULONG NbfDebugPiggybackAcks = 0; +#endif + + +#if DBG +// +// *** This is the original version of StartPacketizingConnection, which +// is now a macro on the free build. It has been left here as the +// fully-commented version of the code. +// + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate + ) + +/*++ + +Routine Description: + + This routine is called to place a connection on the PacketizeQueue + of its device context object. Then this routine starts packetizing + the first connection on that queue. + + *** The Connection LinkSpinLock must be held on entry to this routine. + + *** THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Immediate - TRUE if the connection should be packetized + immediately; FALSE if the connection should be queued + up for later packetizing (implies that ReceiveComplete + will be called in the future, which packetizes always). + + NOTE: If this is TRUE, it also implies that we have + a connection reference of type CREF_BY_ID which we + will "convert" into the CREF_PACKETIZE_QUEUE one. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("StartPacketizingConnection: Entered for connection %lx.\n", + Connection); + } + + DeviceContext = Connection->Provider; + + // + // If this connection's SendState is set to PACKETIZE and if + // we are not already on the PacketizeQueue, then go ahead and + // append us to the end of that queue, and remember that we're + // on it by setting the CONNECTION_FLAGS_PACKETIZE bitflag. + // + // Also don't queue it if the connection is stopping. + // + + if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + if (!Immediate) { + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + } else { +#if DBG + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID); +#endif + } + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Immediate) { + NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID); + } + } + + if (Immediate) { + PacketizeConnections (DeviceContext); + } + +} /* StartPacketizingConnection */ +#endif + + +VOID +PacketizeConnections( + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine attempts to packetize all connections waiting on the + PacketizeQueue of the DeviceContext. + + +Arguments: + + DeviceContext - Pointer to a DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_CONNECTION Connection; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeConnections: Entered for device context %lx.\n", + DeviceContext); + } + + // + // Pick connections off of the device context's packetization queue + // until there are no more left to pick off. For each one, we call + // PacketizeSend. Note this routine can be executed concurrently + // on multiple processors and it doesn't matter; multiple connections + // may be packetized concurrently. + // + + while (TRUE) { + + p = ExInterlockedRemoveHeadList( + &DeviceContext->PacketizeQueue, + &DeviceContext->SpinLock); + + if (p == NULL) { + break; + } + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE); + } else { + NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + PacketizeSend (Connection, FALSE); // releases the lock. + } + } + +} /* PacketizeConnections */ + + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection, + IN BOOLEAN Direct + ) + +/*++ + +Routine Description: + + This routine packetizes the current TdiSend request on the specified + connection as much as limits will permit. A given here is that there + is an active send on the connection that needs further packetization. + + NOTE: This routine is called with the connection spinlock held and + returns with it released. THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Direct - TRUE if we are called from TdiSend. This implies that + the connection does not have a reference of type CREF_SEND_IRP, + which we need to add before we leave. + +Return Value: + + none. + +--*/ + +{ + ULONG MaxFrameSize, FrameSize; + ULONG PacketBytes; + PNDIS_BUFFER PacketDescriptor; + PDEVICE_CONTEXT DeviceContext; + PTP_PACKET Packet; + NTSTATUS Status; + PNBF_HDR_CONNECTION NbfHeader; + BOOLEAN LinkCheckpoint; + BOOLEAN SentPacket = FALSE; + BOOLEAN ExitAfterSendOnePacket = FALSE; + PIO_STACK_LOCATION IrpSp; + ULONG LastPacketLength; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeSend: Entered for connection %lx.\n", Connection); + } + + DeviceContext = Connection->Provider; + + ASSERT (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE); + + // + // Just loop until one of three events happens: (1) we run out of + // packets from NbfCreatePacket, (2) we completely packetize the send, + // or (3) we can't send any more packets because SendOnePacket failed. + // + +#if DBG + + // + // Convert the queue reference into a packetize one. It is OK + // to do this with the lock held because we know that the refcount + // must already be at least one, so we don't drop to zero. + // + + NbfReferenceConnection ("PacketizeSend", Connection, CREF_PACKETIZE); + NbfDereferenceConnection ("Off packetize queue", Connection, CREF_PACKETIZE_QUEUE); +#endif + + MaxFrameSize = Connection->MaximumDataSize; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeSend: MaxFrameSize for user data=%ld.\n", MaxFrameSize); + } + + + // + // It is possible for a frame to arrive during the middle of this loop + // (such as a NO_RECEIVE) that will put us into a new state (such as + // W_RCVCONT). For this reason, we have to check the state every time + // (at the end of the loop). + // + + do { + + if (!NT_SUCCESS (NbfCreatePacket (DeviceContext, Connection->Link, &Packet))) { + + // + // We need a packet to finish packetizing the current send, but + // there are no more packets available in the pool right now. + // Set our send state to W_PACKET, and put this connection on + // the PacketWaitQueue of the device context object. Then, + // when NbfDestroyPacket frees up a packet, it will check this + // queue for starved connections, and if it finds one, it will + // take a connection off the list and set its send state to + // SENDSTATE_PACKETIZE and put it on the PacketizeQueue. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: NbfCreatePacket failed.\n"); + } + Connection->SendState = CONNECTION_SENDSTATE_W_PACKET; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + // Don't queue him if the connection is stopping. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); +#if DBG + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + DbgPrint ("NBF: BUGBUG! Trying to PacketWait stopping connection %lx\n", Connection); + DbgBreakPoint(); + } +#endif + Connection->Flags |= CONNECTION_FLAGS_W_PACKETIZE; + if (!Connection->OnPacketWaitQueue) { + Connection->OnPacketWaitQueue = TRUE; + InsertTailList( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (!SentPacket) { + NbfDereferenceSendIrp ("No packet", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + } + if (Direct) { + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + } + + NbfDereferenceConnection ("No packet", Connection, CREF_PACKETIZE); + return; + + } + + // + // Set the length of the packet now, while only the + // header is attached. + // + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Connection->Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + // Add a reference count to the request, and keep track of + // which request it is. We rely on NbfDestroyPacket to + // remove the reference. + + IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp); + + Packet->Owner = IrpSp; + // Packet->Action = PACKET_ACTION_IRP_SP; + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("PacketizeSend: Packet %x ref IrpSp %x.\n", Packet, Packet->Owner); + } + + // + // For performance reasons, the first time through here on + // a direct call, we have a IrpSp reference already. + // + + if (SentPacket) { + NbfReferenceSendIrp ("Packetize", IrpSp, RREF_PACKET); + } + + // + // Now build a DATA_ONLY_LAST header in this frame. If it + // turns out we need a DFM, we change it. The header we copy + // already has ResponseCorrelator set to our current correlator + // and TransmitCorrelator set to the last one we received from + // him (if we do not piggyback an ack, then we zero out + // TransmitCorrelator). + // + + NbfHeader = (PNBF_HDR_CONNECTION)&(Packet->Header[Connection->Link->HeaderLength + sizeof(DLC_I_FRAME)]); + *(NBF_HDR_CONNECTION UNALIGNED *)NbfHeader = Connection->NetbiosHeader; + + ASSERT (RESPONSE_CORR(NbfHeader) != 0); + + // + // Determine if we need the resynch bit here. + // + + if (Connection->Flags & CONNECTION_FLAGS_RESYNCHING) { + + NbfHeader->Data2Low = 1; + Connection->Flags &= ~CONNECTION_FLAGS_RESYNCHING; + + } else { + + NbfHeader->Data2Low = 0; + + } + + + // + // build an NDIS_BUFFER chain that describes the buffer we're using, and + // thread it off the NdisBuffer. This chain may not complete the + // packet, as the remaining part of the MDL chain may be shorter than + // the packet. + // + + FrameSize = MaxFrameSize; + + // + // Check if we have less than FrameSize left to send. + // + + if (Connection->sp.MessageBytesSent + FrameSize > Connection->CurrentSendLength) { + + FrameSize = Connection->CurrentSendLength - Connection->sp.MessageBytesSent; + + } + + + // + // Make a copy of the MDL chain for this send, unless + // there are zero bytes left. + // + + if (FrameSize != 0) { + + // + // If the whole send will fit inside one packet, + // then there is no need to duplicate the MDL + // (note that this may include multi-MDL sends). + // + + if ((Connection->sp.SendByteOffset == 0) && + (Connection->CurrentSendLength == FrameSize)) { + + PacketDescriptor = (PNDIS_BUFFER)Connection->sp.CurrentSendMdl; + PacketBytes = FrameSize; + Connection->sp.CurrentSendMdl = NULL; + Connection->sp.SendByteOffset = FrameSize; + Packet->PacketNoNdisBuffer = TRUE; + + } else { + + Status = BuildBufferChainFromMdlChain ( + DeviceContext, + Connection->sp.CurrentSendMdl, + Connection->sp.SendByteOffset, + FrameSize, + &PacketDescriptor, + &Connection->sp.CurrentSendMdl, + &Connection->sp.SendByteOffset, + &PacketBytes); + + if (!NT_SUCCESS(Status)) { + + if (NbfHeader->Data2Low) { + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferencePacket (Packet); // remove creation hold. + goto BufferChainFailure; + } + + } + + // + // Chain the buffers to the packet, unless there + // are zero bytes of data. + // + + Connection->sp.MessageBytesSent += PacketBytes; + NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor); + + } else { + + PacketBytes = 0; + Connection->sp.CurrentSendMdl = NULL; + + } + + { + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + {PNDIS_BUFFER NdisBuffer; + NdisQueryPacket(Packet->NdisPacket, NULL, NULL, &NdisBuffer, NULL); + NbfPrint1 ("PacketizeSend: NDIS_BUFFER Built, chain is: %lx is Packet->Head\n", NdisBuffer); + NdisGetNextBuffer (NdisBuffer, &NdisBuffer); + while (NdisBuffer != NULL) { + NbfPrint1 (" %lx is Next\n", + NdisBuffer); + NdisGetNextBuffer (NdisBuffer, &NdisBuffer); + }} + } + + // + // Have we run out of Mdl Chain in this request? + // + +#if DBG + if (PacketBytes < FrameSize) { + ASSERT (Connection->sp.CurrentSendMdl == NULL); + } +#endif + + if ((Connection->sp.CurrentSendMdl == NULL) || + (Connection->CurrentSendLength <= Connection->sp.MessageBytesSent)) { + + // + // Yep. We know that we've exhausted the current request's buffer + // here, so see if there's another request without EOF set that we + // can build start throwing into this packet. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Used up entire request.\n"); + } + + if (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) { + + // + // We are sending the last packet in a message. Change + // the packet type and indicate in the connection object's + // send state that we are waiting for a DATA_ACK NetBIOS- + // level acknowlegement. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Request has EOR, making pkt a DOL.\n"); + } + + // + // Keep track of how many consecutive sends we have done. + // + + Connection->ConsecutiveSends++; + Connection->ConsecutiveReceives = 0; + + // + // Change it to a DOL with piggyback ack allowed if wanted. + // + + ASSERT (NbfHeader->Command == NBF_CMD_DATA_ONLY_LAST); + if (!(IRP_SEND_FLAGS(IrpSp) & + TDI_SEND_NO_RESPONSE_EXPECTED) && + (Connection->ConsecutiveSends < 2)) { + if (NbfUsePiggybackAcks) { + NbfHeader->Data1 |= DOL_OPTIONS_ACK_W_DATA_ALLOWED; + } + } + + Connection->SendState = CONNECTION_SENDSTATE_W_ACK; + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + ExitAfterSendOnePacket = TRUE; + + } else { + + // + // We are sending the last packet in this request. If there + // are more requests in the connection's SendQueue, then + // advance complex send pointer to point to the next one + // in line. Otherwise, if there aren't any more requests + // ready to packetize, then we enter the W_EOR state and + // stop packetizing. Note that we're waiting here for the TDI + // client to come up with data to send; we're just hanging out + // until then. + // + // DGB: Note that this will allow the last packet in the + // request to be smaller than the max packet length. This + // is not addressed anywhere that I can find in the NBF + // spec, and will be interesting to test against a non-NT + // NBF protocol. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Request doesn't have EOR.\n"); + } + + NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE; + + if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) { + + Connection->SendState = CONNECTION_SENDSTATE_W_EOR; + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + ExitAfterSendOnePacket = TRUE; + + } else { + + Connection->sp.CurrentSendIrp = + CONTAINING_RECORD ( + Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink, + IRP, + Tail.Overlay.ListEntry); + Connection->sp.CurrentSendMdl = + Connection->sp.CurrentSendIrp->MdlAddress; + Connection->sp.SendByteOffset = 0; + Connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + } + } + + } else { + + NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE; + + } + + // + // Before we release the spinlock, see if we want to + // piggyback an ack on here. + // + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + + // + // Turn off the flags. We don't take it off the queue, + // that will be handled by the timer function. + // + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + ASSERT (DOL_OPTIONS_ACK_INCLUDED == DFM_OPTIONS_ACK_INCLUDED); + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("A"); + } +#endif + + // + // TRANSMIT_CORR(NbfHeader) is already set correctly. + // + + NbfHeader->Data1 |= DOL_OPTIONS_ACK_INCLUDED; + + } else { + + TRANSMIT_CORR(NbfHeader) = (USHORT)0; + + } + + // + // To prevent a send "crossing" the receive and + // causing a bogus piggyback ack timeout (this + // only matters if a receive indication is in + // progress). + // + + Connection->CurrentReceiveAckQueueable = FALSE; + + SentPacket = TRUE; + LastPacketLength = + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION) + PacketBytes; + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + LastPacketLength); + + Packet->NdisIFrameLength = LastPacketLength; + + ASSERT (Connection->LinkSpinLock == &Connection->Link->SpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, &LinkCheckpoint); + + if (Status == STATUS_LINK_FAILED) { + + // + // If SendOnePacket failed due to the link being + // dead, then we tear down the link. + // + + FailSend (Connection, STATUS_LINK_FAILED, TRUE); // fail the send + NbfDereferencePacket (Packet); // remove creation hold. + if (Direct) { + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + } + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); + + return; + + } else { + + // + // SendOnePacket returned success, so update our counters; + // + + DeviceContext->TempIFrameBytesSent += PacketBytes; + ++DeviceContext->TempIFramesSent; + + if ((Status == STATUS_SUCCESS) && LinkCheckpoint) { + + // + // We are checkpointing; this means that SendOnePacket + // will already have set the state to W_LINK and turned + // off the PACKETIZE flag, so we should leave. When + // the checkpoint response is received, we will + // resume packetizing. We don't have to worry about + // doing all the other recovery stuff (resetting + // the piggyback ack flag, complex send pointer, etc.) + // because the send did in fact succeed. + // + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE); + } + return; + + } else if (ExitAfterSendOnePacket || + (Status == STATUS_MORE_PROCESSING_REQUIRED)) { + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE); + } + return; + + } + } + } + +BufferChainFailure:; + + // + // Note that we may have fallen out of the BuildBuffer... if above with + // Status set to STATUS_INSUFFICIENT_RESOURCES. if we have, we'll just + // stick this connection back onto the packetize queue and hope the + // system gets more resources later. + // + + + if (!NT_SUCCESS (Status)) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: SendOnePacket failed.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Indicate we're waiting on favorable link conditions. + // + + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If we are exiting and we sent a packet without + // polling, we need to start T1. + // + + if (Direct) { + + // + // We have to do the CREF_SEND_IRP reference that is missing. + // + +#if DBG + NbfReferenceConnection("TdiSend", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); + } + + return; + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // It is probable that a NetBIOS frame arrived while we released + // the connection's spin lock, so our state has probably changed. + // When we cycle around this loop again, we will have the lock + // again, so we can test the connection's send state. + // + + } while (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE); + + // + // Clear the PACKETIZE flag, indicating that we're no longer on the + // PacketizeQueue or actively packetizing. The flag was set by + // StartPacketizingConnection to indicate that the connection was + // already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE); + } + +} /* PacketizeSend */ + + +VOID +CompleteSend( + PTP_CONNECTION Connection, + IN USHORT Correlator + ) + +/*++ + +Routine Description: + + This routine is called because the connection partner acknowleged + an entire message at the NetBIOS Frames Protocol level, either through + a DATA_ACK response, or a RECEIVE_OUTSTANDING, or RECEIVE_CONTINUE, + or NO_RECEIVE response where the number of bytes specified exactly + matches the number of bytes sent in the message. Here we retire all + of the TdiSends on the connection's SendQueue up to and including the + one with the TDI_END_OF_RECORD bitflag set. For each request, we + complete the I/O. + + NOTE: This function is called with the connection spinlock + held and returns with it held, but it may release it in the + middle. THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Correlator - The correlator in the DATA_ACK or piggybacked ack. + + OldIrqlP - Returns the IRQL at which the connection spinlock + was acquired. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + KIRQL cancelIrql; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("CompleteSend: Entered for connection %lx.\n", Connection); + } + + + // + // Make sure that the correlator is the expect one, and + // that we are in a good state (don't worry about locking + // since this is an unusual case anyway). + // + + if (Correlator != Connection->NetbiosHeader.ResponseCorrelator) { + NbfPrint0 ("NbfCompleteSend: ack ignored, wrong correlator\n"); + return; + } + + if (Connection->SendState != CONNECTION_SENDSTATE_W_ACK) { + NbfPrint0 ("NbfCompleteSend: ack not expected\n"); + return; + } + + // + // Pick off TP_REQUEST objects from the connection's SendQueue until + // we find one with an END_OF_RECORD mark embedded in it. + // + + while (!(IsListEmpty(&Connection->SendQueue))) { + + // + // We know for a fact that we wouldn't be calling this routine if + // we hadn't received an acknowlegement for an entire message, + // since NBF doesn't provide stream mode sends. Therefore, we + // know that we will run into a request with the END_OF_RECORD + // mark set BEFORE we will run out of requests on that queue, + // so there is no reason to check to see if we ran off the end. + // Note that it's possible that the send has been failed and the + // connection not yet torn down; if this has happened, we could be + // removing from an empty queue here. Make sure that doesn't happen. + // + + p = RemoveHeadList(&Connection->SendQueue); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Request = NULL; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_SUCCESS; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_SENDS) != 0){ + NbfPrint1 ("CompleteSend: Completing send request %lx\n", Irp); + if (++Connection->DeferredPasses >= 4) { + Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_SENDS; + Connection->DeferredPasses = 0; + } + + } + + } +#endif + + + // + // Complete the send. Note that this may not actually call + // IoCompleteRequest for the Irp until sometime later, if the + // in-progress LLC resending going on below us needs to complete. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Since the irp is no longer on the send list, the cancel routine + // cannot find it and will just return. We must grab the cancel + // spinlock to lock out the cancel function while we null out + // the Irp->CancelRoutine field. + // + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp ( + Irp, + STATUS_SUCCESS, + IRP_SEND_LENGTH(IrpSp)); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ++Connection->TransmittedTsdus; + + if (EndOfRecord) { + break; + } + + } + + // + // We've finished processing the current send. Update our state. + // + // Note: The connection spinlock is held here. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + // + // If there is another send pending on the connection, then initialize + // it and start packetizing it. + // + + if (!(IsListEmpty (&Connection->SendQueue))) { + + InitializeSend (Connection); + + // + // This code is similar to calling StartPacketizingConnection + // with the second parameter FALSE. + // + + if ((!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &Connection->Provider->PacketizeQueue, + &Connection->PacketizeLinkage, + &Connection->Provider->SpinLock); + + } + + } + + // + // NOTE: We return with the lock held. + // + +} /* CompleteSend */ + + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ) + +/*++ + +Routine Description: + + This routine is called because something on the link caused this send to be + unable to complete. There are a number of possible reasons for this to have + happened, but all will fail with the common error STATUS_LINK_FAILED. + or NO_RECEIVE response where the number of bytes specified exactly + Here we retire all of the TdiSends on the connection's SendQueue up to + and including the current one, which is the one that failed. + + Later - Actually, a send failing is cause for the entire circuit to wave + goodbye to this life. We now simply tear down the connection completly. + Any future sends on this connection will be blown away. + + NOTE: THIS FUNCTION MUST BE CALLED WITH THE SPINLOCK HELD. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + BOOLEAN GotCurrent = FALSE; + KIRQL cancelIrql; + + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("FailSend: Entered for connection %lx.\n", Connection); + } + + + // + // Pick off IRP objects from the connection's SendQueue until + // we get to this one. If this one does NOT have an EOF mark set, we'll + // need to keep going until we hit one that does have EOF set. Note that + // this may cause us to continue failing sends that have not yet been + // queued. (We do all this because NBF does not provide stream mode sends.) + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfReferenceConnection ("Failing Send", Connection, CREF_COMPLETE_SEND); + + do { + if (IsListEmpty (&Connection->SendQueue)) { + + // + // got an empty list, so we've run out of send requests to fail + // without running into an EOR. Set the connection flag that will + // cause all further sends to be failed up to an EOR and get out + // of here. + // + + Connection->Flags |= CONNECTION_FLAGS_FAILING_TO_EOR; + break; + } + p = RemoveHeadList (&Connection->SendQueue); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + if (Irp == Connection->sp.CurrentSendIrp) { + GotCurrent = TRUE; + } + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Status = RequestStatus; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteSendIrp (Irp, RequestStatus, 0); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ++Connection->TransmissionErrors; + + } while (!EndOfRecord & !GotCurrent); + + // + // We've finished processing the current send. Update our state. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->sp.CurrentSendIrp = NULL; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Blow away this connection; a failed send is a terrible thing to waste. + // Note that we are not on any packetizing queues or similar things at this + // point; we'll just disappear into the night. + // + +#if MAGIC + if (NbfEnableMagic) { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (Connection->Provider, Connection->Link); + } +#endif + + if (StopConnection) { +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "FailSend stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + NbfStopConnection (Connection, STATUS_LINK_FAILED); + } + +#if DBG + //DbgBreakPoint (); +#endif + + NbfDereferenceConnection ("FailSend", Connection, CREF_COMPLETE_SEND); + +} /* FailSend */ + +#if DBG +// +// *** This is the original version of InitializeSend, which is now a macro. +// It has been left here as the fully-commented version of the code. +// + + +VOID +InitializeSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called whenever the next send on a connection should + be initialized; that is, all of the fields associated with the state + of the current send are set to refer to the first send on the SendQueue. + + WARNING: This routine is executed with the Connection lock acquired + since it must be atomically executed with the caller's setup. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("InitializeSend: Entered for connection %lx.\n", Connection); + } + + ASSERT (!IsListEmpty (&Connection->SendQueue)); + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + Connection->FirstSendIrp = + CONTAINING_RECORD (Connection->SendQueue.Flink, IRP, Tail.Overlay.ListEntry); + Connection->FirstSendMdl = Connection->FirstSendIrp->MdlAddress; + Connection->FirstSendByteOffset = 0; + Connection->sp.MessageBytesSent = 0; + Connection->sp.CurrentSendIrp = Connection->FirstSendIrp; + Connection->sp.CurrentSendMdl = Connection->FirstSendMdl; + Connection->sp.SendByteOffset = Connection->FirstSendByteOffset; + Connection->CurrentSendLength = + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + Connection->StallCount = 0; + Connection->StallBytesSent = 0; + + // + // The send correlator isn't used for much; it is used so we + // can distinguish which send a piggyback ack is acking. + // + + if (Connection->NetbiosHeader.ResponseCorrelator == 0xffff) { + Connection->NetbiosHeader.ResponseCorrelator = 1; + } else { + ++Connection->NetbiosHeader.ResponseCorrelator; + } + +} /* InitializeSend */ +#endif + + +VOID +ReframeSend( + PTP_CONNECTION Connection, + ULONG BytesReceived + ) + +/*++ + +Routine Description: + + This routine is called to reset the send state variables in the connection + object to correctly point to the first byte of data to be transmitted. + In essence, this is the byte-level acknowlegement processor at the NetBIOS + level for this transport. + + This is not straightforward because potentially multiple send requests + may be posted to the connection to comprise a single message. When a + send request has its TDI_END_OF_RECORD option bitflag set, then that + send is the last one to be sent in a logical message. Therefore, we + assume that the multiple-send scenario is the general case. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + BytesReceived - Number of bytes received thus far. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PMDL Mdl; + ULONG Offset; + ULONG BytesLeft; + ULONG MdlBytes; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ReframeSend: Entered for connection %lx, Flags: %lx Current Mdl: %lx\n", + Connection, Connection->Flags, Connection->sp.CurrentSendMdl); + } + + // + // The caller is responsible for restarting the packetization process + // on this connection. In some cases (i.e., NO_RECEIVE handler) we + // don't want to start packetizing, so that's why we do it elsewhere. + // + + // + // Examine all of the send requests and associated MDL chains starting + // with the first one at the head of the connection's SendQueue, advancing + // our complex current send pointer through the requests and MDL chains + // until we reach the byte count he's specified. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // In the case where a local disconnect has been issued, and we get a frame + // that causes us to reframe the send our FirstSendIrp and FirstMdl + // pointers are stale. Catch this condition and prevent faults caused by + // this. A better fix would be to change the logic that switches the + // connection sendstate from idle to W_LINK to not do that. However, this + // is a broader change than fixing it right here. + // + + if (IsListEmpty(&Connection->SendQueue)) { + RELEASE_DPC_SPIN_LOCK(Connection->LinkSpinLock); + return; + } + + BytesLeft = BytesReceived; + Irp = Connection->FirstSendIrp; + Mdl = Connection->FirstSendMdl; + if (Mdl) { + MdlBytes = MmGetMdlByteCount (Mdl); + } else { + MdlBytes = 0; // zero-length send + } + Offset = Connection->FirstSendByteOffset; + +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + NbfPrint3 ("ReFrameSend: Called with Connection %lx FirstSend %lx CurrentSend %lx\n", + Connection, Connection->FirstSendIrp, Connection->sp.CurrentSendIrp); + Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_SENDS; + Connection->DeferredPasses = 0; + } +#endif + + // + // We loop through while we have acked bytes left to account for, + // advancing our pointers and completing any sends that have been + // completely acked. + // + + while (BytesLeft != 0) { + + if (Mdl == NULL) { + KIRQL cancelIrql; + + // + // We have exhausted the MDL chain on this request, so it has + // been implicitly acked. That means we must complete the I/O + // by dereferencing the request before we reframe further. + // + + p = RemoveHeadList (&Connection->SendQueue); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + // + // Since the irp is no longer on the list, the cancel routine + // won't find it. Grab the cancel spinlock to synchronize + // and complete the irp. + // + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp (Irp, STATUS_SUCCESS, Offset); + + // + // Now continue with the next request in the list. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + p = Connection->SendQueue.Flink; + if (p == &Connection->SendQueue) { + + ULONG DumpData[2]; + + // + // The byte acknowledgement was for more than the + // total length of sends we have outstanding; to + // avoid problems we tear down the connection. + // +#if DBG + NbfPrint2 ("NbfReframeSend: Got %d extra bytes acked on %lx\n", + BytesLeft, Connection); + ASSERT (FALSE); +#endif + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + DumpData[0] = Offset; + DumpData[1] = BytesLeft; + + NbfWriteGeneralErrorLog( + Connection->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 1, + STATUS_INVALID_NETWORK_RESPONSE, + L"REFRAME", + 2, + DumpData); + + NbfStopConnection (Connection, STATUS_INVALID_NETWORK_RESPONSE); + + return; + + } + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + Mdl = Irp->MdlAddress; + MdlBytes = MmGetMdlByteCount (Mdl); + Offset = 0; + + } else if (MdlBytes > (Offset + BytesLeft)) { + + // + // This MDL has more data than we really need. Just use + // part of it. Then get out, because we're done. + // + + Offset += BytesLeft; + BytesLeft = 0; + break; + + } else { + + // + // This MDL does not have enough data to satisfy the ACK, so + // use as much data as it has, and cycle around again. + // + + Offset = 0; + BytesLeft -= MdlBytes; + Mdl = Mdl->Next; + + if (Mdl != NULL) { + MdlBytes = MmGetMdlByteCount (Mdl); + } + + } + } + + // + // Tmp debugging; we want to see if we got byte acked + // for the entire send. This will break if we have + // non-EOR sends. + // + +#if DBG + if (BytesReceived != 0) { + ASSERTMSG ("NbfReframeSend: Byte ack for entire send\n", + Mdl != NULL); + } +#endif + + // + // We've acked some data, possibly on a byte or message boundary. + // We must pretend we're sending a new message all over again, + // starting with the byte immediately after the last one he acked. + // + + Connection->FirstSendIrp = Irp; + Connection->FirstSendMdl = Mdl; + Connection->FirstSendByteOffset = Offset; + + // + // Since we haven't started sending this new reframed message yet, + // we set our idea of the current complex send pointer to the first + // complex send pointer. + // + + Connection->sp.MessageBytesSent = 0; + Connection->sp.CurrentSendIrp = Irp; + Connection->sp.CurrentSendMdl = Mdl; + Connection->sp.SendByteOffset = Offset; + Connection->CurrentSendLength -= BytesReceived; + Connection->StallCount = 0; + Connection->StallBytesSent = 0; + +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + + { + PLIST_ENTRY p; + NbfPrint0 ("ReFrameSend: Walking Send List:\n"); + + for ( + p = Connection->SendQueue.Flink; + p != &Connection->SendQueue; + p=p->Flink ) { + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + NbfPrint1 (" Irp %lx\n", Irp); + } + }} +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + +} /* ReframeSend */ + + +VOID +NbfCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send. + The send is found on the connection's send queue; if it is the + current request it is cancelled and the connection is torn down, + otherwise it is silently cancelled. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PIRP SendIrp; + PLIST_ENTRY p; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_SEND)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + + // + // See if this is the IRP for the current send request. + // + + ACQUIRE_SPIN_LOCK (Connection->LinkSpinLock, &oldirql); + NbfReferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND); + + p = Connection->SendQueue.Flink; + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + if (SendIrp == Irp) { + + // + // yes, it is the first one on the send queue, so + // trash the send/connection. The first send is a special case + // there are multiple pointers to the send request. Just stop the + // connection. + // + + // p = RemoveHeadList (&Connection->SendQueue); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + // + // Prevent anyone from getting in to packetize before we + // call NbfStopConnection. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled in-progress send %lx on %lxn", + SendIrp, Connection); +#endif + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + // NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + + // + // Since we are cancelling the current send, blow away + // the connection. + // + + NbfStopConnection (Connection, STATUS_CANCELLED); + + KeLowerIrql (oldirql1); + + } else { + + // + // Scan through the list, looking for this IRP. If we + // cancel anything up to the first EOR on the list + // we still tear down the connection since this would + // mess up our packetizing otherwise. We set CancelledFirstEor + // to FALSE when we pass an IRP without SEND_PARTIAL. + // + // NO MATTER WHAT WE MUST SHUT DOWN THE CONNECTION!!!! + +#if 0 + if (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL)) { + CancelledFirstEor = FALSE; + } else { + CancelledFirstEor = TRUE; + } +#endif + + Found = FALSE; + p = p->Flink; + while (p != &Connection->SendQueue) { + + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (SendIrp == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled queued send %lx on %lx\n", + SendIrp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + // + // STOP THE CONNECTION NO MATTER WHAT!!! + // + NbfStopConnection (Connection, STATUS_CANCELLED); + + KeLowerIrql (oldirql1); + break; + + } +#if 0 + else { + + if (CancelledFirstEor && (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL))) { + CancelledFirstEor = FALSE; + } + } +#endif + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + +#if DBG + DbgPrint("NBF: Tried to cancel send %lx on %lx, not found\n", + Irp, Connection); +#endif + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + + } + + NbfDereferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND); + +} + + +BOOLEAN +ResendPacket ( + PTP_LINK Link, + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine resends a packet on the link. Since this is a resend, we + are careful to not reset the state unless all resends have completed. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - pointer to packet to be resent. + +Return Value: + + True if resending should continue; FALSE otherwise. + +--*/ + +{ + BOOLEAN PollFinal; + PDLC_I_FRAME DlcHeader; + UINT DataLength; + + + // + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ReSendPacket: %lx NdisPacket: %lx # %x\n", + Packet, Packet->NdisPacket, + DlcHeader->RcvSeq >>1); + IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) { + {PUCHAR q; + USHORT i; + q = Packet->Header; + for (i=0;i<20;i++) { + NbfPrint1 (" %2x",q[i]); + } + NbfPrint0 ("\n");} + } + } + + DataLength = Packet->NdisIFrameLength; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + Link->WindowErrors++; + + PollFinal = (BOOLEAN)((DlcHeader->RcvSeq & DLC_I_PF) != 0); + + StopT2 (Link); // since this is potentially acking some frames + + if (Link->Provider->MacInfo.MediumAsync) { + if (PollFinal) { + ASSERT (Packet->Link != NULL); + NbfReferenceLink ("ResendPacket", Link, LREF_START_T1); + } else { + StartT1 (Link, 0); + } + } else { + StartT1 (Link, PollFinal ? DataLength : 0); // restart transmission timer + } + + // + // Update the expected next receive in case it's changed + // + + if (PollFinal) { + + DlcHeader->RcvSeq = DLC_I_PF; // set the poll bit. + Link->SendState = SEND_STATE_CHECKPOINTING; + + Link->ResendingPackets = FALSE; + + } else { + + DlcHeader->RcvSeq = 0; + + } + + // + // DlcHeader->RcvSeq has Link->NextReceive inserted by NbfNdisSend. + // + + NbfReferencePacket (Packet); // so we don't remove it in send completion + + NbfReferenceLink ("ResendPacket", Link, LREF_NDIS_SEND); + + ASSERT (Packet->PacketSent == TRUE); + Packet->PacketSent = FALSE; + + // + // Update our "bytes resent" counters. + // + + DataLength -= + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + + ADD_TO_LARGE_INTEGER( + &Link->Provider->Statistics.DataFrameBytesResent, + DataLength); + ++Link->Provider->Statistics.DataFramesResent; + + + // + // Send the packet (this release the link spinlock). + // + + NbfNdisSend (Link, Packet); + + ++Link->PacketsResent; + + NbfDereferenceLink ("ResendPacket", Link, LREF_NDIS_SEND); + + // + // if this packet has POLL set, stop the resending so the + // link doesn't get all twisted up. + // + + if (PollFinal) { + + // + // so we're in the state of having sent a poll and not + // sending anything else until we get a final. This avoids + // overrunning the remote. Note that we leave the routine + // with state LINK_SENDSTATE_REJECTING, which guarentees + // we won't start any new sends until we traverse through + // this routine again. + // + // + + return FALSE; + } + + return TRUE; +} + +BOOLEAN +ResendLlcPackets ( + PTP_LINK Link, + UCHAR AckSequenceNumber, + BOOLEAN Resend + ) + +/*++ + +Routine Description: + + This routine advances the state of a data link connection by retiring + all of the packets on the link's WackQ that have send sequence numbers + logically less than that number specified as the AckSequenceNumber, and + resending those above that number. The packets are disposed of by + dereferencing them. We cannot simply destroy them because this + acknowlegement might arrive even before the Physical Provider has had a + chance to issue a completion event for the associated I/O. + + NOTE: This function is called with the link spinlock held and + returns with it held, but it may release it in between. THIS + ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + AckSequenceNumber - An unsigned number specifing the sequence number of + the first packet within the window that is NOT acknowleged. + + Resend - if TRUE, resend packets. If FALSE, just remove them from the + wackq and get out. + +Return Value: + + none. + +--*/ + +{ + PTP_PACKET packet; + PLIST_ENTRY p, p1; + UCHAR packetSeq; + BOOLEAN passedAck = FALSE; + PDLC_I_FRAME DlcHeader; + SCHAR Difference; + BOOLEAN ReturnValue = FALSE; +// NDIS_STATUS ndisStatus; + + // + // Move through the queue, releasing those we've been acked for and resending + // others above that. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ResendLlcPackets: Link %lx, Ack: %x, LinkLastAck: %x.\n", + Link, AckSequenceNumber, Link->LastAckReceived); + NbfPrint0 ("RLP: Walking WackQ, Packets:\n"); + p = Link->WackQ.Flink; // p = ptr, 1st pkt's linkage. + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + NbfPrint4 ("RLP: Pkt: %lx # %x Flags: %d %d\n", packet, + (UCHAR)(DlcHeader->SendSeq >> 1), packet->PacketSent, packet->PacketNoNdisBuffer); + p = packet->Linkage.Flink; + } + } + + // + // If somebody else is resending LLC packets (which means they + // are in this function with Resend == TRUE), then ignore + // this frame. This is because it may ack a frame that he + // is in the middle of resending, which will cause problems. + // + // BUGBUG: This isn't a great solution, we should keep track + // of where the other guy is and avoid stepping on him. This + // might mess up his walking of the queue however. + // + + if (Link->ResendingPackets) { + NbfPrint1("ResendLlcPackets: Someone else resending on %lx\n", Link); + return TRUE; + } + + // + // We have already checked that AckSequenceNumber is reasonable. + // + + Link->LastAckReceived = AckSequenceNumber; + + if (Resend) { + + // + // Only one person can be resending or potentially resending + // at one time. + // + + Link->ResendingPackets = TRUE; + } + + // + // Resend as many packets as we have window to send. We spin through the + // queue and remove those packets that have been acked or that are + // sequence numbered logically below the current ack number. The flags + // PACKET_FLAGS_RESEND and PACKET_FLAGS_SENT correspond to the three states + // a packet on this queue can be in: + // + // 1) if _RESEND is set, the packet has not been acked + // + // 2) if _SENT is set, the packet send has completed (conversely, if NOT + // set, the packet has not yet been completely sent, thus it is + // unnecessary to resend it). + // 3) if _RESEND and _SENT are both set, the packet has been sent and not + // acked and is grist for our mills. + // 4) if neither is set, the world is coming to an end next Thursday. + // + + p=Link->WackQ.Flink; + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + + // + // if both bits aren't set we can't do a thing with this packet, or, + // for that matter, with the rest of the packet list. We can't + // have reached the ack number yet, as these packets haven't even + // completed sending. + // (Later) actually, we can have reached passedAck, and if we did + // we're in a world of hurt. We can't send more regular packets, + // but we can't send any resend packets either. Force the link to + // checkpoint and things will clear themselves up later. + // + + if (!(packet->PacketSent)) { + if (passedAck) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("ResendLLCPacket: Can't send WACKQ Packet RcvSeq %x %x \n", + DlcHeader->RcvSeq, DlcHeader->SendSeq); + } + + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + + // + // Don't start checkpointing if we already are. + // + + Link->SendState = SEND_STATE_CHECKPOINTING; + StopTi (Link); + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // start checkpoint timeout. + Link->ResendingPackets = FALSE; + + // + // Try this...in this case don't actually send + // an RR, since his response might put us right + // back here. When T1 expires we will recover. + // + // NbfSendRr (Link, TRUE, TRUE); + + } else { + + Link->ResendingPackets = FALSE; + + } + + return TRUE; + } + + // + // Don't break, since passedAck is FALSE all we will + // do in the next section is TpDereferencePacket, which + // is correct. + // + // break; + } + + // + // This loop is somewhat schizo; at this point, if we've not yet reached + // the ack number, we'll be ditching the packet. If we've gone through + // the ack number, we'll be re-transmitting. Note that in the first + // personality, we are always looking at the beginning of the list. + // + + // + // NOTE: Link spinlock is held here. + // + + packetSeq = (UCHAR)(DlcHeader->SendSeq >> 1); + if (!passedAck){ + + // + // Compute the signed difference here; see if + // packetSeq is equal to or "greater than" + // LastAckReceived. + // + + Difference = packetSeq - Link->LastAckReceived; + + if (((Difference >= 0) && (Difference < 0x40)) || + (Difference < -0x40)) { + + // + // We have found a packet on the queue that was + // not acknowledged by LastAckReceived. + // + + if (Link->SendState == SEND_STATE_CHECKPOINTING) { + + // + // If we are checkpointing, we should not do any of + // the passedAck things (i.e. any of the things which + // potentially involve sending packets) - adb 7/30/91. + // + + if (Resend) { + Link->ResendingPackets = FALSE; + } + return TRUE; + } + + if (!Resend) { + + // + // If we are not supposed to resend, then exit. + // Since there are still packets on the queue + // we restart T1. + // + + StopTi (Link); + StartT1 (Link, 0); // start checkpoint timeout. + return TRUE; + } + + // + // Lock out senders, so we maintain packet sequences properly + // + + Link->SendState = SEND_STATE_REJECTING; // we're resending. + + passedAck = TRUE; + + // + // Note that we don't advance the pointer to the next packet; + // thus, we will resend this packet on the next pass through + // the while loop (taking the passedAck branch). + // + + } else { + p1 = RemoveHeadList (&Link->WackQ); + ASSERTMSG (" ResendLLCPacket: Packet not at queue head!\n", (p == p1)); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + ReturnValue = TRUE; + NbfDereferencePacket (packet); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = Link->WackQ.Flink; + } + + } else { +// NbfPrint1 ("RLP: # %x\n",packetSeq); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // If this call returns FALSE (because we checkpoint) + // it clears ResendingPacket before it returns. + // + + if (!ResendPacket (Link, packet)) { + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + return ReturnValue; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = p->Flink; + } + } + + // + // NOTE: Link spinlock is held here. + // + + if (passedAck) { + + // + // If we exit through here with passedAck TRUE, it means that we + // successfully called ResendPacket on every packet in the + // WackQ, which means we did not resend a poll packet, so we + // can start sending normally again. We have to clear + // ResendingPackets here. + // + + Link->SendState = SEND_STATE_READY; + Link->ResendingPackets = FALSE; + StartTi (Link); + + } else if (!Resend) { + + // + // If Resend is FALSE (in which case passedAck will also be FALSE, + // by the way), and the WackQ is empty, that means that we + // successfully acknowledged all the packets on a non-final + // frame. In this case T1 may be running, but in fact is not + // needed since there are no sends outstanding. + // + + if (Link->WackQ.Flink == &Link->WackQ) { + StopT1 (Link); // BUGBUG: StartTi?? + } + Link->SendState = SEND_STATE_READY; + StartTi (Link); + + } else { + + // + // Resend is TRUE, but passedAck is FALSE; we came in + // expecting to resend, but didn't. This means that + // we have emptied the queue after receiving an + // RR/f, i.e. this send window is done and we can + // update our send window size, etc. + // + + Link->ResendingPackets = FALSE; + + if (Link->Provider->MacInfo.MediumAsync) { + return ReturnValue; + } + + if (Link->WindowErrors > 0) { + + // + // We had transmit errors on this window. + // + + Link->PrevWindowSize = Link->SendWindowSize; + + // + // We use 100 ms delay as the cutoff for a LAN. + // + + if (Link->Delay < (100*MILLISECONDS)) { + + // + // On a LAN, if we have a special case + // if one packet was lost; this means the + // final packet was retransmitted once. In + // that case, we keep track of Consecutive + // LastPacketLost, and if it reaches 2, then + // we lock the send window at its current + // value minus one. + // + + if (Link->WindowErrors == 1) { + + ++Link->ConsecutiveLastPacketLost; + + if (Link->ConsecutiveLastPacketLost >= 2) { + + // + // Freeze the window wherever it was. + // + + if (Link->SendWindowSize > Link->Provider->MinimumSendWindowLimit) { + Link->MaxWindowSize = Link->SendWindowSize - 1; + Link->SendWindowSize = (UCHAR)Link->MaxWindowSize; + } + + } + + // + // Otherwise, we leave the window where it is. + // + + } else { + + Link->ConsecutiveLastPacketLost = 0; + Link->SendWindowSize -= (UCHAR)Link->WindowErrors; + + } + + } else { + + // + // On a WAN we cut the send window in half, + // regardless of how many frames were retransmitted. + // + + Link->SendWindowSize /= 2; + Link->WindowsUntilIncrease = 1; // in case Prev is also 1. + Link->ConsecutiveLastPacketLost = 0; + + } + + if ((SCHAR)Link->SendWindowSize < 1) { + Link->SendWindowSize = 1; + } + + // + // Reset our counters for the next window. + // + + Link->WindowErrors = 0; + + } else { + + // + // We have successfully sent a window of data, increase + // the send window size unless we are at the limit. + // We use 100 ms delay as the WAN/LAN cutoff. + // + + if ((ULONG)Link->SendWindowSize < Link->MaxWindowSize) { + + if (Link->Delay < (100*MILLISECONDS)) { + + // + // On a LAN, increase the send window by 1. + // + // BUGBUG: Need to determine optimal window + // size. + // + + Link->SendWindowSize++; + + } else { + + // + // On a WAN, increase the send window by 1 until + // we hit PrevWindowSize, then do it more slowly. + // + + if (Link->SendWindowSize < Link->PrevWindowSize) { + + Link->SendWindowSize++; + + // + // If we just increased it to the previous window + // size, prepare for the next time through here. + // + + if (Link->SendWindowSize == Link->PrevWindowSize) { + Link->WindowsUntilIncrease = Link->SendWindowSize; + } + + } else { + + // + // We passed the previous size, so only update every + // WindowsUntilIncrease times. + // + + if (--Link->WindowsUntilIncrease == 0) { + + Link->SendWindowSize++; + Link->WindowsUntilIncrease = Link->SendWindowSize; + + } + } + } + + if ((ULONG)Link->SendWindowSize > Link->Provider->Statistics.MaximumSendWindow) { + Link->Provider->Statistics.MaximumSendWindow = Link->SendWindowSize; + } + + } + + // + // Clear this since we had no errors. + // + + Link->ConsecutiveLastPacketLost = 0; + + } + + } + + return ReturnValue; + +} /* ResendLlcPackets */ + + +VOID +NbfSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + NdisContext - the value associated with the adapter binding at adapter + open time (which adapter we're talking on). + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSEND_PACKET_TAG SendContext; + PTP_PACKET Packet; + KIRQL oldirql1; + ProtocolBindingContext; // avoid compiler warnings + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfSendsCompletedAfterPendFail++; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n", + NdisPacket, NbfGetNdisStatus (NdisStatus)); + } + } else { + NbfSendsCompletedAfterPendOk++; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n", + NdisPacket, NbfGetNdisStatus (NdisStatus)); + } + } +#endif + + SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0]; + + switch (SendContext->Type) { + case TYPE_I_FRAME: + + // + // Just dereference the packet. There are a couple possibilities here. + // First, the I/O completion might happen before an ACK is received, + // in which case this will remove one of the references, but not both. + // Second, the LLC ACK for this packet may have already been processed, + // in which case this will destroy the packet. Third, this packet may + // be resent, either before or after this call, in which case the deref + // won't destroy the packet. + // + // NbfDereferencePacket will call PacketizeSend if it determines that + // there is at least one connection waiting to be packetized because + // of out-of-resource conditions or because its window has been opened. + // + + Packet = ((PTP_PACKET)SendContext->Frame); + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + if (Packet->Provider->MacInfo.MediumAsync) { + + if (Packet->Link) { + + ASSERT (Packet->NdisIFrameLength > 0); + + ACQUIRE_DPC_SPIN_LOCK (&Packet->Link->SpinLock); + StartT1 (Packet->Link, Packet->NdisIFrameLength); + RELEASE_DPC_SPIN_LOCK (&Packet->Link->SpinLock); + + NbfDereferenceLink ("Send completed", Packet->Link, LREF_START_T1); + } + + if (Packet->PacketizeConnection) { + + PTP_CONNECTION Connection = IRP_SEND_CONNECTION((PIO_STACK_LOCATION)(Packet->Owner)); + PDEVICE_CONTEXT DeviceContext = Packet->Provider; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (Connection->Flags & CONNECTION_FLAGS_PACKETIZE); + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->SpinLock); + + NbfReferenceConnection ("Delayed packetizing", Connection, CREF_PACKETIZE_QUEUE); + InsertTailList(&DeviceContext->PacketizeQueue, &Connection->PacketizeLinkage); + + if (!DeviceContext->WanThreadQueued) { + + DeviceContext->WanThreadQueued = TRUE; + ExQueueWorkItem(&DeviceContext->WanDelayedQueueItem, DelayedWorkQueue); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } else { + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("PacketizeConnection FALSE", Connection, CREF_TEMP); + + Packet->PacketizeConnection = FALSE; + + } + } +#if DBG + if (Packet->PacketSent) { + DbgPrint ("NbfSendCompletionHandler: Packet %lx already completed\n", Packet); + DbgBreakPoint(); + } +#endif + Packet->PacketSent = TRUE; + + NbfDereferencePacket (Packet); + + KeLowerIrql (oldirql1); + break; + + case TYPE_UI_FRAME: + + // + // just destroy the frame; name stuff doesn't depend on having any + // of the sent message left around after the send completed. + // + + NbfDestroyConnectionlessFrame ((PDEVICE_CONTEXT)SendContext->Owner, + (PTP_UI_FRAME)SendContext->Frame); + break; + + case TYPE_ADDRESS_FRAME: + + // + // Addresses get their own frames; let the address know it's ok to + // use the frame again. + // + + NbfSendDatagramCompletion ((PTP_ADDRESS)SendContext->Owner, + NdisPacket, + NdisStatus ); + break; + } + + return; + +} /* NbfSendCompletionHandler */ + + +NTSTATUS +SendOnePacket( + IN PTP_CONNECTION Connection, + IN PTP_PACKET Packet, + IN BOOLEAN ForceAck, + OUT PBOOLEAN LinkCheckpoint OPTIONAL + ) + +/*++ + +Routine Description: + + This routine sends a connection-oriented packet by calling the NDIS + Send service. At least one event will occur following + (or during) the Send request's processing. (1) The Send request + will complete through the I/O system, calling IoCompleteRequest. + (2) The sequenced packet will be acknowleged at the LLC level, or it + will be rejected and reset at the LLC level. If the packet is resent, + then it remains queued at the TP_LINK object. If the packet is ACKed, + then is removed from the link's WackQ and the Action field in the + TP_PACKET structure dictates what operation to perform next. + + NOTE: This routine is called with the link spinlock held. THIS + ROUTINE MUST BE CALLED AT DPC LEVEL. + + NOTE: This routine will now accept all frames unless the link + is down. If the link cannot send, the packet will be queued and + sent when possible. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Packet - Pointer to a TP_PACKET object. + + ForceAck - Boolean that, if true, indicates this packet should always have + the Poll bit set; this force the other side to ack immediately, + which is necessary for correct session teardown. + + LinkCheckpoint - If specified, will return TRUE if the link has + just entered a checkpoint state. In this case the status + will be STATUS_SUCCESS, but the connection should stop + packetizing now (in fact, to close a window, the connection + is put into the W_LINK state if this status will be + returned, so he must stop because somebody else may + already be doing it). + +Return Value: + + STATUS_LINK_FAILED - the link is dead or not ready. + STATUS_SUCCESS - the packet has been sent. + STATUS_INSUFFICIENT_RESOURCES - the packet has been queued. + +--*/ + +{ + PTP_LINK Link; + PDLC_I_FRAME DlcHeader; + PNDIS_BUFFER ndisBuffer; + ULONG SendsOutstanding; + BOOLEAN Poll = FALSE; + NTSTATUS Status; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("SendOnePacket: Entered, connection %lx, packet %lx DnisPacket %lx.\n", + Connection, Packet, Packet->NdisPacket); + } + + Link = Connection->Link; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + UINT PLength, PCount; + UINT BLength; + PVOID BAddr; + NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength); + NbfPrint3 ("Sending Data Packet: %lx, Length: %lx Pages: %lx\n", + Packet->NdisPacket, PLength, PCount); + while (ndisBuffer != NULL) { + NdisQueryBuffer(ndisBuffer, &BAddr, &BLength); + NbfPrint3 ("Sending Data Packet: Buffer %08lx Length %08lx Va %08lx\n", + ndisBuffer, BLength, BAddr); + NdisGetNextBuffer (ndisBuffer, &ndisBuffer); + } + } + + // + // If the general state of the link is not READY, then we can't ship. + // This failure can be expected under some conditions, and may not cause + // failure of the send. + // + + if (Link->State != LINK_STATE_READY) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("SendOnePacket: Link state is not READY (%ld).\n", Link->State); + } + + // + // determine what to do with this problem. If we shouldn't be sending + // here, percolate an error upward. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("SendOnePacket: Link Bad state, link: %lx Link Flags %lx Link State %lx\n", + Link, Link->Flags, Link->State); + } + return STATUS_LINK_FAILED; + } + + + SendsOutstanding = (((ULONG)Link->NextSend+128L-(ULONG)Link->LastAckReceived)%128L); + + // + // Format LLC header while we've got the spinlock to atomically update + // the link's state information. + // + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]); + DlcHeader->SendSeq = (UCHAR)(Link->NextSend << 1); + Link->NextSend = (UCHAR)((Link->NextSend + 1) & 0x7f); + DlcHeader->RcvSeq = 0; // Link->NextReceive is inserted by NbfNdisSend + + // + // Before we release the spinlock, we append the packet to the + // end of the link's WackQ, so that if an ACK arrives before the NdisSend + // completes, it will be on the queue already. Also, mark the packet as + // needing resend, which is canceled by AckLLCPackets, and used by + // ResendLLCPackets. Thus, all packets will need to be resent until they + // are acked. + // + + ASSERT (Packet->PacketSent == FALSE); + + InsertTailList (&Link->WackQ, &Packet->Linkage); + //SrvCheckListIntegrity( &Link->WackQ, 200 ); + + + // + // If the send state is not READY, we can't ship. + // This failure is mostly caused by flow control or retransmit in progress, + // and is never cause for failure of the send. + // + + if ((Link->SendState != SEND_STATE_READY) || + (Link->LinkBusy) || + (SendsOutstanding >= (ULONG)Link->SendWindowSize)) { + + if ((Link->SendWindowSize == 1) || ForceAck) { + DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit. + if (Link->Provider->MacInfo.MediumAsync) { + Packet->Link = Link; + } + } + + Packet->PacketSent = TRUE; // allows it to be resent. + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + if (Link->SendState != SEND_STATE_READY) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("SendOnePacket: Link send state not READY (%ld).\n", Link->SendState); + } + } else if (Link->LinkBusy) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + PANIC ("SendOnePacket: Link is busy.\n"); + } + } else if (SendsOutstanding >= (ULONG)Link->SendWindowSize) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("SendOnePacket: No link send window; N(S)=%ld,LAR=%ld,SW=%ld.\n", + Link->NextSend, Link->LastAckReceived, Link->SendWindowSize); + } + } +#endif + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Reference the packet since it is given to the NDIS driver. + // + +#if DBG + NbfReferencePacket (Packet); +#else + ++Packet->ReferenceCount; // OK since it is not queued anywhere. +#endif + + // + // If this is the last I-frame in the window, then indicate that we + // should checkpoint. Also checkpoint if the sender is requesting + // acknowledgement (currently on SendSessionEnd does this). + // By default, this will also be a command frame. + // + + if (((SendsOutstanding+1) >= (ULONG)Link->SendWindowSize) || + ForceAck) { + Link->SendState = SEND_STATE_CHECKPOINTING; + StopTi (Link); + DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit. + Poll = TRUE; + + } + + + // + // If we are polling, and the caller cares about it, then + // we set LinkCheckpoint, and also set up the connection to + // be waiting for resources. We do this now, before the send, + // so that even if the ack is receive right away, we will + // be in a good state. When we return LinkCheckpoint TRUE + // the caller realizes that he no longer owns the right + // to "packetize" and exits immediately. + // + // We also want to start our retransmission timer so, if this + // packet gets dropped, we will know to retransmit it. The + // exception is if LinkCheckpoint was specified, then we + // only StartT1 of we are not polling (the caller will + // ensure it is started if he exits before we poll). + // + + if (ARGUMENT_PRESENT(LinkCheckpoint)) { + + if (Poll) { + + // + // If the connection still has send state PACKETIZE, + // then change it to W_LINK. If it is something else + // (such as W_PACKET or W_ACK) then don't worry, when + // that condition clears he will repacketize and the + // link conditions will be re-examined. In all + // case we turn off the PACKETIZE flag, because when + // we return with LinkCheckpoint TRUE he will stop + // packetizing, and to close the window we turn it + // off now (before the NdisSend) rather than then. + // + + ASSERT (Connection->LinkSpinLock == &Link->SpinLock); + if (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) { + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + } + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + if (Link->Provider->MacInfo.MediumAsync) { + Packet->Link = Link; + NbfReferenceLink ("Send I-frame", Link, LREF_START_T1); + } else { + StartT1 (Link, Packet->NdisIFrameLength); + } + *LinkCheckpoint = TRUE; + + } else { + + StartT1 (Link, 0); + *LinkCheckpoint = FALSE; + + } + + } else { + + // + // If LinkCheckpoint is not true, then we are sending + // an I-frame other than DFM/DOL. In this case, as + // an optimization, we'll set W_LINK if a) we are + // polling b) we are IDLE (to avoid messing up other + // states such as W_ACK). This will avoid a window + // where we don't go W_LINK until after the next + // send tries to packetize and fails. + // + + if (Poll) { + + ASSERT (Connection->LinkSpinLock == &Link->SpinLock); + if (Connection->SendState == CONNECTION_SENDSTATE_IDLE) { + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + } + + } + + // + // This is an optimization; we know that if LinkCheckpoint + // is present than we are being called from PacketizeSend; + // in this case the Link will have the LREF_CONNECTION + // reference and the connection will have the CREF_PACKETIZE + // reference, so we don't have to reference the link + // again. + // + + NbfReferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND); + + + // + // Start the retransmission timer. + // + + if (Link->Provider->MacInfo.MediumAsync) { + if (Poll) { + Packet->Link = Link; + NbfReferenceLink ("ResendPacket", Link, LREF_START_T1); + } else { + StartT1 (Link, 0); + } + } else { + StartT1 (Link, Poll ? Packet->NdisIFrameLength : 0); + } + + } + + // + // Since this I-frame contains an N(R), it is potentially ACKing some + // previously received I-frames as reverse traffic. So we stop our + // delayed acknowlegement timer. + // + + StopT2 (Link); + + if ((Link->Provider->MacInfo.MediumAsync) && + (ARGUMENT_PRESENT(LinkCheckpoint)) && + (Link->SendWindowSize >= 3) && + (!Poll) && (SendsOutstanding == (ULONG)(Link->SendWindowSize-2))) { + + Status = STATUS_MORE_PROCESSING_REQUIRED; + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + NbfReferenceConnection ("PacketizeConnection TRUE", Connection, CREF_TEMP); + Packet->PacketizeConnection = TRUE; + + } else { + + Status = STATUS_SUCCESS; + } + + // + // Send the packet; no locks held. Note that if the send fails, we will + // NOT fail upward; we allow things to continue onward. This lets us retry + // the send multiple times before we crap out; additionally, it keeps us + // from failing obscurely when sending control Iframes. + // + // NOTE: NbfNdisSend releases the link spinlock. + // + + NbfNdisSend (Link, Packet); + + Link->PacketsSent++; + + // + // Remove the reference made above if needed. + // + + if (!ARGUMENT_PRESENT(LinkCheckpoint)) { + NbfDereferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND); + } + + return Status; + +} /* SendOnePacket */ + + +VOID +SendControlPacket( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine sends a connection-oriented packet by calling the Physical + Provider's Send service. While SendOnePacket is used to send an I- + frame, this routine is used to send one of the following: RR, RNR, REJ, + SABME, UA, DISC, DM, FRMR, TEST, and XID. + + NOTE: This function is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT i; + PUCHAR p; + PNDIS_BUFFER ndisBuffer; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("SendControlPacket: Entered for link %lx, packet %lx, NdisPacket %lx\n 00:", + Link, Packet, Packet->NdisPacket); + IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) { + UINT PLength, PCount; + UINT BLength; + PVOID BAddr; + p = Packet->Header; + for (i=0;i<20;i++) { + NbfPrint1 (" %2x",p[i]); + } + NbfPrint0 ("\n"); + NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength); + NbfPrint3 ("Sending Control Packet: %lx, Length: %lx Pages: %lx\n", + Packet->NdisPacket, PLength, PCount); + while (ndisBuffer != NULL) { + NdisQueryBuffer (ndisBuffer, &BAddr, &BLength); + NbfPrint3 ("Sending Control Packet: Buffer %08lx Length %08lx Va %08lx\n", + ndisBuffer, BLength, BAddr); + NdisGetNextBuffer (ndisBuffer, &ndisBuffer); + } + } + } + + ASSERT (Packet->PacketSent == FALSE); + + NbfReferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND); + + // + // Send the packet (we have the lock, NbfNdisSend released + // it. + // + + NbfNdisSend (Link, Packet); + + NbfDereferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND); + +} /* SendControlPacket */ + + +VOID +NbfNdisSend( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of NdisSend + and after assigning the receive sequence number it locks out other + sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: This routine is called with the link spinlock held, + and it returns with it released. THIS ROUTINE MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PLIST_ENTRY p; + PDLC_S_FRAME DlcHeader; + PNDIS_PACKET TmpNdisPacket; + ULONG result; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + if (Link->Provider->UniProcessor) { + + // + // On a uni-processor, we can send without fear of + // being interrupted by an incoming packet. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]); + + if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) { + + // + // It's not a U-frame, so we assign RcvSeq. + // + + DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1); + + } + +#if DBG + NbfSendsIssued++; +#endif + + INCREMENT_COUNTER (Link->Provider, PacketsSent); + + if (Link->Loopback) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + Link->Provider, + Packet->NdisPacket, + Link->LoopbackDestinationIndex + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + Link->Provider->NdisBindingHandle, + Packet->NdisPacket); + + } + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + + switch (NdisStatus) { + + case NDIS_STATUS_PENDING: +#if DBG + NbfSendsPended++; +#endif + break; + + case NDIS_STATUS_SUCCESS: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedOk++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + break; + + default: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedFail++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n", + NbfGetNdisStatus (NdisStatus)); + } + break; + + } + + } else { + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Link->NdisSendsInProgress > 0) { + + p = (PLIST_ENTRY)(Packet->NdisPacket->MacReserved); + InsertTailList (&Link->NdisSendQueue, p); + ++Link->NdisSendsInProgress; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence field in the packet (note that the RcvSeq + // field is in the same place for I- and S-frames. + // + + Link->NdisSendsInProgress = 1; + + while (TRUE) { + + DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]); + + if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) { + + // + // It's not a U-frame, so we assign RcvSeq. + // + + DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1); + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + NbfSendsIssued++; +#endif + + INCREMENT_COUNTER (Link->Provider, PacketsSent); + + if (Link->Loopback) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + Link->Provider, + Packet->NdisPacket, + Link->LoopbackDestinationIndex + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + Link->Provider->NdisBindingHandle, + Packet->NdisPacket); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = ExInterlockedAddUlong( + &Link->NdisSendsInProgress, + (ULONG)-1, + &Link->SpinLock); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + + switch (NdisStatus) { + + case NDIS_STATUS_PENDING: +#if DBG + NbfSendsPended++; +#endif + break; + + case NDIS_STATUS_SUCCESS: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedOk++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + break; + + default: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedFail++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n", + NbfGetNdisStatus (NdisStatus)); + } + break; + + } + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. + // + + if (result == 1) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + p = RemoveHeadList(&Link->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Link->NdisSendQueue); + + // + // Get back the TP_PACKET by using the Frame pointer in the + // ProtocolReserved field of the NDIS_PACKET. + // + + TmpNdisPacket = CONTAINING_RECORD (p, NDIS_PACKET, MacReserved[0]); + Packet = (PTP_PACKET)(((PSEND_PACKET_TAG)(&TmpNdisPacket->ProtocolReserved[0]))->Frame); + + } // while loop + + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + +} /* NbfNdisSend */ + + +VOID +RestartLinkTraffic( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine continues the activities of the connections on a link. + + NOTE: This function is called with the link spinlock held and + it returns with it released. THIS FUNCTION MUST BE CALLED AT + DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("RestartLinkTraffic: Entered for link %lx.\n", Link); + } + + // + // Link conditions may have cleared up. Make all connections on this + // link eligible for more packetization if they are in W_LINK state. + // + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + ASSERT (connection->LinkSpinLock == &Link->SpinLock); + + // + // If we tried to send a plain-ole data frame DFM/DOL, but + // link conditions were not satisfactory, then we changed + // send state to W_LINK. Check for that now, and possibly + // start repacketizing. + // + + if (connection->SendState == CONNECTION_SENDSTATE_W_LINK) { + if (!(IsListEmpty (&connection->SendQueue))) { + + connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + // + // This is similar to calling StartPacketizingConnection + // with the Immediate set to FALSE. + // + + if (!(connection->Flags & CONNECTION_FLAGS_PACKETIZE) && + (connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &connection->Provider->PacketizeQueue, + &connection->PacketizeLinkage, + &connection->Provider->SpinLock); + + } + + } else { + connection->SendState = CONNECTION_SENDSTATE_IDLE; + } + } + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +} /* RestartLinkTraffic */ + + +VOID +NbfProcessWanDelayedQueue( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the thread routine which restarts packetizing + that has been delayed on WAN to allow RRs to come in. + This is very similar to PacketizeConnections. + +Arguments: + + Parameter - A pointer to the device context. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + PTP_CONNECTION Connection; + KIRQL oldirql; + + DeviceContext = (PDEVICE_CONTEXT)Parameter; + + // + // Packetize all waiting connections + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + ASSERT (DeviceContext->WanThreadQueued); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + while (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + p = RemoveHeadList(&DeviceContext->PacketizeQueue); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE); + } else { + NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + PacketizeSend (Connection, FALSE); // releases the lock. + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + DeviceContext->WanThreadQueued = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + KeLowerIrql (oldirql); + +} /* NbfProcessWanDelayedQueue */ + + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN PDEVICE_CONTEXT DeviceContext, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source Mdl chain and + offset into it. We assume we don't know the length of the source Mdl chain, + and we must allocate the NDIS_BUFFERs for the destination chain, which + we do from the NDIS buffer pool. + + The NDIS_BUFFERs that are returned are mapped and locked. (Actually, the pages in + them are in the same state as those in the source MDLs.) + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. TrueLength is set to 0. + +Environment: + + Kernel Mode, Source Mdls locked. It is recommended, although not required, + that the source Mdls be mapped and locked prior to calling this routine. + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentMdl - Points to the start of the Mdl chain from which to draw the + packet. + + ByteOffset - Offset within this MDL to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + Destination - returned pointer to the NDIS_BUFFER chain describing the packet. + + NewCurrentMdl - returned pointer to the Mdl that would be used for the next + byte of packet. NULL if the source Mdl chain was exhausted. + + NewByteOffset - returned offset into the NewCurrentMdl for the next byte of + packet. NULL if the source Mdl chain was exhausted. + + TrueLength - The actual length of the returned NDIS_BUFFER Chain. If less than + DesiredLength, the source Mdl chain was exhausted. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded (even if + shorter than the desired chain). + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while building the + destination chain. + +--*/ +{ + ULONG AvailableBytes; + PMDL OldMdl; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + // + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint3 ("BuildBufferChain: Mdl: %lx Offset: %ld Length: %ld\n", + CurrentMdl, ByteOffset, DesiredLength); + } + + AvailableBytes = MmGetMdlByteCount (CurrentMdl) - ByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + OldMdl = CurrentMdl; + *NewCurrentMdl = OldMdl; + *NewByteOffset = ByteOffset + AvailableBytes; + *TrueLength = AvailableBytes; + + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + DeviceContext->NdisBufferPool, + OldMdl, + ByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *Destination = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *Destination = NewNdisBuffer; + + +// IF_NBFDBG (NBF_DEBUG_SENDENG) { +// PVOID PAddr, UINT PLen; +// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen); +// NbfPrint4 ("BuildBufferChain: (start)Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n", +// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr); +// } + + // + // Was the first NDIS_BUFFER enough data, or are we out of Mdls? + // + + if ((AvailableBytes == DesiredLength) || (OldMdl->Next == NULL)) { + if (*NewByteOffset >= MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + while (OldMdl != NULL) { + AvailableBytes = DesiredLength - *TrueLength; + if (AvailableBytes > MmGetMdlByteCount (OldMdl)) { + AvailableBytes = MmGetMdlByteCount (OldMdl); + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + DeviceContext->NdisBufferPool, + OldMdl, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*Destination != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*Destination); + NdisFreeBuffer (*Destination); + *Destination = NewNdisBuffer; + } + + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *NewCurrentMdl = CurrentMdl; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + *TrueLength += AvailableBytes; + *NewByteOffset = AvailableBytes; + +// IF_NBFDBG (NBF_DEBUG_SENDENG) { +// PVOID PAddr, UINT PLen; +// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen); +// NbfPrint4 ("BuildBufferChain: (continue) Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n", +// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr); +// } + + if (*TrueLength == DesiredLength) { + if (*NewByteOffset == MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + } // while (mdl chain exists) + + *NewCurrentMdl = NULL; + *NewByteOffset = 0; + return STATUS_SUCCESS; + +} // BuildBufferChainFromMdlChain + diff --git a/private/ntos/tdi/nbf/sources b/private/ntos/tdi/nbf/sources new file mode 100644 index 000000000..6d6ab7f87 --- /dev/null +++ b/private/ntos/tdi/nbf/sources @@ -0,0 +1,79 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nbf + +TARGETNAME=nbf +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc;..\..\..\inc + +#C_DEFINES = -DRASAUTODIAL +C_DEFINES = -DRASAUTODIAL -D_PNP_POWER + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=action.c \ + address.c \ + autodial.c \ + connect.c \ + connobj.c \ + devctx.c \ + dlc.c \ + event.c \ + framecon.c \ + framesnd.c \ + iframes.c \ + info.c \ + link.c \ + linktree.c \ + nbf.rc \ + nbfcnfg.c \ + nbfdrvr.c \ + nbfdebug.c \ + nbfmac.c \ + nbfndis.c \ + nbfpnp.c \ + packet.c \ + rcv.c \ + rcveng.c \ + request.c \ + send.c \ + sendeng.c \ + spnlckdb.c \ + timer.c \ + uframes.c + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF + +PRECOMPILED_INCLUDE=precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/nbf/spnlckdb.c b/private/ntos/tdi/nbf/spnlckdb.c new file mode 100644 index 000000000..485431ad5 --- /dev/null +++ b/private/ntos/tdi/nbf/spnlckdb.c @@ -0,0 +1,156 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + spnlckdb.c + +Abstract: + + This module contains code which allows debugging of spinlock related NBF + problems. Most of this code is conditional on the manifest constant + NBF_LOCKS. + +Author: + + David Beaver 13-Feb-1991 + (From Chuck Lenzmeier, Jan 1991) + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef NBF_LOCKS + +KSPIN_LOCK NbfGlobalLock = NULL; +PKTHREAD NbfGlobalLockOwner = NULL; +ULONG NbfGlobalLockRecursionCount = 0; +ULONG NbfGlobalLockMaxRecursionCount = 0; +KIRQL NbfGlobalLockPreviousIrql = (KIRQL)-1; +BOOLEAN NbfGlobalLockPrint = 1; + +#define PRINT_ERR if ( (NbfGlobalLockPrint & 1) != 0 ) DbgPrint +#define PRINT_INFO if ( (NbfGlobalLockPrint & 2) != 0 ) DbgPrint + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ) +{ + KIRQL previousIrql; + + PKTHREAD currentThread = KeGetCurrentThread( ); + + if ( NbfGlobalLockOwner == currentThread ) { + + ASSERT( Lock != NULL ); // else entering NBF with lock held + + ASSERT( NbfGlobalLockRecursionCount != 0 ); + NbfGlobalLockRecursionCount++; + if ( NbfGlobalLockRecursionCount > NbfGlobalLockMaxRecursionCount ) { + NbfGlobalLockMaxRecursionCount = NbfGlobalLockRecursionCount; + } + + PRINT_INFO( "NBF reentered from %s/%ld, new count %ld\n", + FileName, LineNumber, NbfGlobalLockRecursionCount ); + + } else { + + ASSERT( Lock == NULL ); // else missing an ENTER_NBF call + + KeAcquireSpinLock( &NbfGlobalLock, &previousIrql ); + + ASSERT( NbfGlobalLockRecursionCount == 0 ); + NbfGlobalLockOwner = currentThread; + NbfGlobalLockPreviousIrql = previousIrql; + NbfGlobalLockRecursionCount = 1; + + PRINT_INFO( "NBF entered from %s/%ld\n", FileName, LineNumber ); + + } + + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + return; + +} // NbfAcquireSpinLock + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ) +{ + PKTHREAD currentThread = KeGetCurrentThread( ); + KIRQL previousIrql; + + ASSERT( NbfGlobalLockOwner == currentThread ); + ASSERT( NbfGlobalLockRecursionCount != 0 ); + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + if ( --NbfGlobalLockRecursionCount == 0 ) { + + ASSERT( Lock == NULL ); // else not exiting NBF, but releasing lock + + NbfGlobalLockOwner = NULL; + previousIrql = NbfGlobalLockPreviousIrql; + NbfGlobalLockPreviousIrql = (KIRQL)-1; + + PRINT_INFO( "NBF exited from %s/%ld\n", FileName, LineNumber ); + + KeReleaseSpinLock( &NbfGlobalLock, previousIrql ); + + } else { + + ASSERT( Lock != NULL ); // else exiting NBF with lock held + + PRINT_INFO( "NBF semiexited from %s/%ld, new count %ld\n", + FileName, LineNumber, NbfGlobalLockRecursionCount ); + + } + + return; + +} // NbfReleaseSpinLock + +VOID +NbfFakeSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) +{ + ENTER_NBF; + NbfSendCompletionHandler (ProtocolBindingContext, NdisPacket, NdisStatus); + LEAVE_NBF; +} + +VOID +NbfFakeTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +{ + ENTER_NBF; + NbfTransferDataComplete (BindingContext, NdisPacket, NdisStatus, BytesTransferred); + LEAVE_NBF; +} + +#endif // def NBF_LOCKS diff --git a/private/ntos/tdi/nbf/testnbf.c b/private/ntos/tdi/nbf/testnbf.c new file mode 100644 index 000000000..91a3ca645 --- /dev/null +++ b/private/ntos/tdi/nbf/testnbf.c @@ -0,0 +1,1466 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + testtdi.c + +Abstract: + + Kernel Mode test program for any Tdi network provider. This routine is an + example of how to use the TDI interface at the kernel level. + +Author: + + Dave Beaver (DBeaver) 5 June 1991 + +Revision History: + +--*/ + +#include "nbf.h" +#include + +#define TRANSPORT_NAME L"\\Device\\Nbf" + +PSZ ServerName = "DCTDISERVER "; +PSZ ClientName = "DCTDICLIENT "; +PSZ AnyName = "* "; + +static PUCHAR TextBuffer; // dynamically allocated non-paged buffer. +ULONG c9_Xmt = 0xff; +ULONG c9_Rcv = 0xff; +ULONG c9_Iteration = 0xffffffff; + +static ULONG TextBufferLength; // size of the above in bytes. +#define BUFFER_SIZE 0xffff +PUCHAR RBuff; +PUCHAR XBuff; +UCHAR c9_ListBlock[512]; +UCHAR c9_ConnBlock[512]; + +extern KEVENT TdiSendEvent; +extern KEVENT TdiReceiveEvent; +extern KEVENT TdiServerEvent; + +ULONG ApcContext; + +NTSTATUS +TSTRCVCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + DbgPrint ("TSTRCVCompletion event: %lx\n" , Context); +// KeSetEvent ((PKEVENT)Context, 0, TRUE); + return STATUS_MORE_PROCESSING_REQUIRED; + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); +} + +#define InitWaitObject(_event)\ + KeInitializeEvent (\ + _event,\ + SynchronizationEvent,\ + FALSE) + +VOID +NbfTestTimer( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +TtdiOpenAddress ( + IN PHANDLE FileHandle, + IN PSZ Name + ); + +NTSTATUS +TtdiOpenConnection ( + IN PHANDLE FileHandle, + IN ULONG ConnectionContext + ); + + +NTSTATUS +TtdiOpenAddress ( + IN PHANDLE FileHandle, + IN PSZ Name) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + UNICODE_STRING NameString; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + PTDI_ADDRESS_NETBIOS AddressName; + PTRANSPORT_ADDRESS Address; + PTA_ADDRESS AddressType; + int i; + + DbgPrint ("TtdiOpenAddress: Opening "); + DbgPrint (Name); + DbgPrint (".\n"); + RtlInitUnicodeString (&NameString, TRANSPORT_NAME); + InitializeObjectAttributes ( + &ObjectAttributes, + &NameString, + 0, + NULL, + NULL); + + EaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool (NonPagedPool, 100); + if (EaBuffer == NULL) { + DbgBreakPoint (); + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TDI_ADDRESS_NETBIOS); + + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiTransportAddress[i]; + } + + Address = (PTRANSPORT_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + Address->TAAddressCount = 1; + + AddressType = (PTA_ADDRESS)((PUCHAR)Address + sizeof (Address->TAAddressCount)); + + AddressType->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + AddressType->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + + AddressName = (PTDI_ADDRESS_NETBIOS)((PUCHAR)AddressType + + sizeof (AddressType->AddressType) + sizeof (AddressType->AddressLength)); + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + for (i=0;i<16;i++) { + AddressName->NetbiosName[i] = Name[i]; + } + + Status = IoCreateFile ( + FileHandle, + 0, // desired access. + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + FO_SYNCHRONOUS_IO, // file attributes. + 0, + 0, + 0, // create options. + EaBuffer, // EA buffer. + (PUCHAR)&AddressName->NetbiosName[i] - (PUCHAR)EaBuffer + 1, // ea length + CreateFileTypeNone, + (PVOID)NULL, + 0 ); // EA length. + + if (!NT_SUCCESS( Status )) { + DbgPrint ("TtdiOpenAddress: FAILURE, NtCreateFile returned status code=%lC.\n", Status); + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("TtdiOpenAddress: FAILURE, IoStatusBlock.Status contains status code=%lC.\n", Status); + } + + DbgPrint ("TtdiOpenAddress: returning\n"); + + return Status; +} /* TtdiOpenAddress */ + + +NTSTATUS +TtdiOpenConnection (IN PHANDLE FileHandle, IN ULONG ConnectionContext) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + UNICODE_STRING NameString; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + int i; + + DbgPrint ("TtdiOpenConnection: Opening Context %lx...\n ", + ConnectionContext); + RtlInitUnicodeString (&NameString, TRANSPORT_NAME); + InitializeObjectAttributes ( + &ObjectAttributes, + &NameString, + 0, + NULL, + NULL); + + EaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool (NonPagedPool, 100); + if (EaBuffer == NULL) { + DbgBreakPoint (); + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; + EaBuffer->EaValueLength = sizeof (ULONG); + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiConnectionContext[i]; + } + + RtlMoveMemory ( + &EaBuffer->EaName[EaBuffer->EaValueLength + 1], + &ConnectionContext, + sizeof (PVOID)); + + Status = NtCreateFile ( + FileHandle, + 0, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + FO_SYNCHRONOUS_IO, // file attributes. + 0, + 0, + 0, // create options. + EaBuffer, // EA buffer. + 100); // EA length. + + if (!NT_SUCCESS( Status )) { + DbgPrint ("TtdiOpenConnection: FAILURE, NtCreateFile returned status code=%lC.\n", Status); + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("TtdiOpenConnection: FAILURE, IoStatusBlock.Status contains status code=%lC.\n", Status); + } + + DbgPrint ("TtdiOpenConnection: returning\n"); + + return Status; +} /* TtdiOpenEndpoint */ + +NTSTATUS +CloseAddress (IN HANDLE FileHandle) +{ + NTSTATUS Status; + + Status = NtClose (FileHandle); + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("CloseAddress: FAILURE, NtClose returned status code=%lC.\n", Status); + } else { + DbgPrint ("CloseAddress: NT_SUCCESS.\n"); + } + + return Status; +} /* CloseAddress */ + + +BOOLEAN +TtdiSend() +{ + USHORT i, Iteration, Increment; + HANDLE RdrHandle, RdrConnectionHandle; + KEVENT Event1; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL SendMdl, ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PUCHAR SendBuffer; + ULONG SendBufferLength; + PIRP Irp; + + Status = KeWaitForSingleObject (&TdiSendEvent, Suspended, KernelMode, FALSE, NULL); + + SendBufferLength = (ULONG)BUFFER_SIZE; + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Send Test ******\n" ); + + XBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (XBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for send buffer exiting\n"); + return FALSE; + } + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ClientName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&RdrHandle, AnyName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of client: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + // + // Open the connection on the transport. + // + + Status = TtdiOpenConnection (&RdrConnectionHandle, 1); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + TdiBuildAssociateAddress (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + RdrHandle); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success AssociateAddress\n"); + } + } + + // + // Post a TdiConnect to the client endpoint. + // + + RequestInformation.RemoteAddress = ConnectBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_CONNECT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildConnect ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + &ReturnInformation); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Connect: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Iosb status Connect: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Connect Iosb\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: Connect FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Connect Immediate\n"); + } + } + + DbgPrint( "\n****** Send Test: SUCCESSFUL TdiConnect: ******\n"); + + // + // Send/receive 1 or 10 messages. + // + + SendBuffer = (PUCHAR)ExAllocatePool (NonPagedPool, SendBufferLength); + if (SendBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + SendMdl = IoAllocateMdl (SendBuffer, SendBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (SendMdl); + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // Cycle the buffer length from 0 up through the maximum for Tdi. after a + // couple of shots at the full range in one byte steps, increment by ever + // increasing amounts to get to the max. + // + + CurrentBufferLength = 0; + Increment = 1; + for (Iteration=1; Iteration<(USHORT)c9_Iteration; Iteration++) { + CurrentBufferLength += Increment; + if (CurrentBufferLength > MessageBufferLength) { + CurrentBufferLength = 0; + Increment = 1; + } + if (CurrentBufferLength > 7500) { + Increment++; + } + if ((USHORT)((Iteration / 100) * 100) == Iteration) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + } + for (i=0; i<(USHORT)CurrentBufferLength; i++) { + SendBuffer [i] = (UCHAR)(i + Iteration % 256 ); + MessageBuffer [i] = 0; // zap this sucker with something. + } + + // + // Now issue a send on the client side. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_SEND, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildSend (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + 0, + CurrentBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Send: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Iosb status Send: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success SendIosb\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: Send FAILED Status: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Send Immediate\n"); + } + } + + if (Iosb1.Information != CurrentBufferLength) { + DbgPrint ("SendTest: Bytes sent <> Send buffer size.\n"); + DbgPrint ("SendTest: BytesToSend=%ld. BytesSent=%ld.\n", + CurrentBufferLength, Iosb1.Information); + } + + } + + // + // We're done with this endpoint. Close it and get out. + // + + Status = CloseAddress (RdrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on 2nd Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Send Test ******\n" ); + return TRUE; +} /* Send */ + + +BOOLEAN +TtdiReceive() +{ + USHORT i, Iteration, Increment; + SHORT j,k; + HANDLE SvrHandle, SvrConnectionHandle; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL SendMdl, ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PUCHAR SendBuffer; + ULONG SendBufferLength; + PIRP Irp; + KEVENT Event1; + + Status = KeWaitForSingleObject (&TdiReceiveEvent, Suspended, KernelMode, FALSE, NULL); + + SendBufferLength = (ULONG)BUFFER_SIZE; + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Receive Test ******\n" ); + + XBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (XBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for send buffer exiting\n"); + return FALSE; + } + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ClientName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&SvrHandle, ServerName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Address: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SvrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Address: %lC ******\n", Status ); + return FALSE; + } + + Status = TtdiOpenConnection (&SvrConnectionHandle, 2); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SvrConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + DbgPrint ("Build Irp %lx, Handle %lx \n", + Irp, SvrHandle); + + TdiBuildAssociateAddress ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + SvrHandle); + InitWaitObject (&Event1); + + { + PULONG Temp=(PULONG)IoGetNextIrpStackLocation (Irp); + DbgPrint ("Built IrpSp %lx %lx %lx %lx %lx \n", *(Temp++), *(Temp++), + *(Temp++), *(Temp++), *(Temp++)); + } + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success AssociateAddress\n"); + } + } + + RequestInformation.RemoteAddress = ConnectBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + // + // Post a TdiListen to the server endpoint. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_LISTEN, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildListen ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + &ReturnInformation); + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Listen: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Listen Iosb status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success Listen IOSB\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success Listen Immediate\n"); + } + } + + + DbgPrint ("\n****** Receive Test: LISTEN just completed! ******\n"); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ACCEPT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAccept (Irp, DeviceObject, ConnectionObject, NULL, NULL, &RequestInformation, NULL, 0); + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Accept: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Accept Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Accept FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // We have the connection data now. Sanity check it. + // + + DbgPrint ("\n****** Receive Test: LISTEN completed successfully! ******\n"); + + // + // Receive/receive 1 or 10 messages. + // + + SendBuffer = (PUCHAR)ExAllocatePool (NonPagedPool, SendBufferLength); + if (SendBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + SendMdl = IoAllocateMdl (SendBuffer, SendBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (SendMdl); + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // Cycle the buffer length from 0 up through the maximum for Tdi. after a + // couple of shots at the full range in one byte steps, increment by ever + // increasing amounts to get to the max. + // + + CurrentBufferLength = 0; + Increment = 1; + for (Iteration=1; Iteration<(USHORT)c9_Iteration; Iteration++) { + CurrentBufferLength += Increment; + if (CurrentBufferLength > MessageBufferLength) { + CurrentBufferLength = 0; + Increment = 1; + } + if (CurrentBufferLength > 7500) { + Increment++; + } + if ((USHORT)((Iteration / 100) * 100) == Iteration) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + } + for (i=0; i<(USHORT)CurrentBufferLength; i++) { + SendBuffer [i] = (UCHAR)(i + Iteration % 256 ); + MessageBuffer [i] = 0; // zap this sucker with something. + } + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_RECEIVE, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildReceive (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + MessageBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Receive: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Receive Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // The receive completed. Make sure the data is correct. + // + + if (Iosb1.Information != CurrentBufferLength) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + DbgPrint ("ReceiveTest: Bytes received <> bytes sent.\n"); + DbgPrint ("ReceiveTest: BytesToSend=%ld. BytesReceived=%ld.\n", + CurrentBufferLength, Iosb1.Information); + } + + if (i == (USHORT)CurrentBufferLength) { +// DbgPrint ("ReceiveTest: Message contains correct data.\n"); + } else { + DbgPrint ("ReceiveTest: Message data corrupted at offset %lx of %lx.\n", (ULONG)i, (ULONG)SendBufferLength); + DbgPrint ("ReceiveTest: Data around corrupted location:\n"); + for (j=-4;j<=3;j++) { + DbgPrint ("%08lx ", (ULONG) i+j*16); + for (k=(SHORT)i+(j*(SHORT)16);k<(SHORT)i+((j+(SHORT)1)*(SHORT)16);k++) { + DbgPrint ("%02x",MessageBuffer [k]); + } + for (k=(SHORT)i+(j*(SHORT)16);k<(SHORT)i+((j+(SHORT)1)*(SHORT)16);k++) { + DbgPrint ("%c",MessageBuffer [k]); + } + DbgPrint ("\n"); + } + DbgPrint ("ReceiveTest: End of Corrupt Data.\n"); + } + } + + // + // We're done with this endpoint. Close it and get out. + // + + Status = CloseAddress (SvrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on 1st Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Receive Test ******\n" ); + return TRUE; +} /* Receive */ + +BOOLEAN +TtdiServer() +{ + USHORT i; + HANDLE RdrHandle, SrvConnectionHandle; + KEVENT Event1; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PIRP Irp; + + Status = KeWaitForSingleObject (&TdiServerEvent, Suspended, KernelMode, FALSE, NULL); + + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Server Test ******\n" ); + + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = AnyName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&RdrHandle, ServerName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of client: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + // + // Now loop forever trying to get a connection from a remote client to + // this server. We will create connections until we run out of resources, + // and we will echo the data we are sent back along the same connection. + // Sends and Receives are always asynchronous, while listens are + // synchronous. + // + + while (TRUE) { + + // + // Open the connection on the transport. + // + + Status = TtdiOpenConnection (&SrvConnectionHandle, 1); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SrvConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + // + // Now register the device handler for receives + // + +// Irp = TdiBuildInternalDeviceControlIrp ( +// TDI_SET_EVENT_HANDLER, +// DeviceObject, +// ConnectionObject, +// &Event1, +// &Iosb1); + +// TdiBuildSetEventHandler (Irp, +// DeviceObject, +// ConnectionObject, +// TSTRCVCompletion, +// &Event1, +// TDI_RECEIVE_HANDLER, +// TdiTestReceiveHandler, +// ConnectionObject); + +// Status = IoCallDriver (DeviceObject, Irp); + +// if (Status == STATUS_PENDING) { +// Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); +// if (!NT_SUCCESS(Status)) { +// DbgPrint( "\n****** Server Test: FAILED Event1 Wait Register: %lC ******\n", Status ); +// return FALSE; +// } +// if (!NT_SUCCESS(Iosb1.Status)) { +// DbgPrint( "\n****** Server Test: FAILED Register Iosb status: %lC ******\n", Status ); +// return FALSE; +// } +// +// } else { +// if (!NT_SUCCESS (Status)) { +// DbgPrint( "\n****** Server Test: RegisterHandler FAILED Status: %lC ******\n", Status ); +// return FALSE; +// } +// } + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAssociateAddress (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + RdrHandle); + + Status = IoCallDriver (DeviceObject, Irp); + + // IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Server Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Server Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // Post a TdiListen to the server endpoint. + // + + RequestInformation.RemoteAddress = ListenBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_LISTEN, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildListen ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + NULL); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED Event1 Wait Listen: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Server Test: FAILED Listen Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Server Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + DbgPrint ("\n****** Server Test: LISTEN just completed! ******\n"); + + // + // accept the connection from the remote + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ACCEPT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAccept ( + Irp, + DeviceObject, + ConnectionObject, + NULL, + NULL, + &RequestInformation, + NULL, + 0); + + Status = IoCallDriver (DeviceObject, Irp); + + // IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Accept: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Accept Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Accept Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // Get a buffer for the continued read/write loop. + // + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // have a receive buffer, and a connection; go ahead and read and write + // until the remote disconnects. + // + + while (TRUE) { + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_RECEIVE, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildReceive (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + MessageBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Receive: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Receive Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + + // + // Check to see if the remote has disconnected, which is + // the only reason for us shutting down/ + // + + if (Status == STATUS_REMOTE_DISCONNECT) { + + // + // We've been disconnected from; get out + // + + NtClose (SrvConnectionHandle); + break; + } + + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + + // + // successful return, what length is the data? + // + + CurrentBufferLength = Iosb1.Information; + } + } + + // + // send the data back + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_SEND, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildSend (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + 0, + CurrentBufferLength); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Send: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Send Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + + DbgPrint( "\n****** Receive Test: Send FAILED Status: %lC ******\n", Status ); + NtClose (SrvConnectionHandle); + break; + + } + } + } // end of receive/send while + + IoFreeMdl (ReceiveMdl); + ExFreePool (MessageBuffer); + + } + + // + // We're done with this address. Close it and get out. + // + + Status = CloseAddress (RdrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on 2nd Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Send Test ******\n" ); + return TRUE; +} /* Server */ diff --git a/private/ntos/tdi/nbf/testtdi.c b/private/ntos/tdi/nbf/testtdi.c new file mode 100644 index 000000000..6cf1b335f --- /dev/null +++ b/private/ntos/tdi/nbf/testtdi.c @@ -0,0 +1,291 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tstrcv.c + +Abstract: + + start receive side tests utility + +Author: + + Dave Beaver (dbeaver) 24-Mar-1991 + +Revision History: + +--*/ + +// +// download a ub board +// + +typedef unsigned char uchar_t; + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#define TDIDEV "\\Device\\Nbf" +char Tdidevice[] = TDIDEV; /* default device */ +char *Tdidev = Tdidevice; + +HANDLE FileHandle; + +VOID +usage( + VOID + ); + + +NTSTATUS +main ( + IN SHORT argc, + IN PSZ argv[] + ) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + STRING NameString; + UNICODE_STRING unicodeString; + PUCHAR buffer; + ULONG IoControlCode; + int n; + CHAR c; + + for( n = 1; n < argc && argv[n][0] == '-' ; ++n ) { + c = argv[n][1]; + + switch( c ) { + + case 's': // send test + IoControlCode = IOCTL_TDI_SEND_TEST; + break; + + case 'r': // receive test + IoControlCode = IOCTL_TDI_RECEIVE_TEST; + + break; + + case 'b': /* both test */ + IoControlCode = IOCTL_TDI_SERVER_TEST; + + break; + + default: + usage (); + break; + + } + } + + printf ("Opening TDI device: %s \n", Tdidev); + RtlInitString (&NameString, Tdidev); + Status = RtlAnsiStringToUnicodeString( + &unicodeString, + &NameString, + TRUE); + + buffer = (PUCHAR)malloc (100); + + Status = TdiOpenNetbiosAddress (&FileHandle, buffer, (PVOID)&NameString, NULL); + + RtlFreeUnicodeString(&unicodeString); + free (buffer); + + if (!NT_SUCCESS( Status )) { + printf ("FAILURE, Unable to open TDI driver %s, status: %lx.\n", + Tdidev,Status); + return (Status); + } + + if (!(NT_SUCCESS( IoStatusBlock.Status ))) { + printf ("FAILURE, Unable to open TDI driver %s, IoStatusBlock.Status: %lx.\n", + Tdidev, IoStatusBlock.Status); + return (IoStatusBlock.Status); + } + + // + // start the test + // + + printf("Starting test.... "); + Status = NtDeviceIoControlFile( + FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IoControlCode, + NULL, + 0, + NULL, + 0); + + if (!NT_SUCCESS( Status )) { + printf ("FAILURE, Unable to start test: %lx.\n", Status); + return (Status); + } + + if (!(NT_SUCCESS( IoStatusBlock.Status ))) { + printf ("FAILURE, Unable to start test: %lx.\n", IoStatusBlock.Status); + return (IoStatusBlock.Status); + } + + NtClose (FileHandle); + + return STATUS_SUCCESS; + +} + + +NTSTATUS +TdiOpenNetbiosAddress ( + IN OUT PHANDLE FileHandle, + IN PUCHAR Buffer, + IN PVOID DeviceName, + IN PVOID Address) + +/*++ + +Routine Description: + + Opens an address on the given file handle and device. + +Arguments: + + FileHandle - the returned handle to the file object that is opened. + + Buffer - pointer to a buffer that the ea is to be built in. This buffer + must be at least 40 bytes long. + + DeviceName - the Unicode string that points to the device object. + + Name - the address to be registered. If this pointer is NULL, the routine + will attempt to open a "control channel" to the device; that is, it + will attempt to open the file object with a null ea pointer, and if the + transport provider allows for that, will return that handle. + +Return Value: + + An informative error code if something goes wrong. STATUS_SUCCESS if the + returned file handle is valid. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + PTRANSPORT_ADDRESS TAAddress; + PTA_ADDRESS AddressType; + PTDI_ADDRESS_NETBIOS AddressName; + PSZ Name; + ULONG Length; + int i; + + if (Address != NULL) { + Name = (PSZ)Address; + try { + Length = sizeof (FILE_FULL_EA_INFORMATION) + + sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + + if (EaBuffer == NULL) { + return STATUS_UNSUCCESSFUL; + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TDI_ADDRESS_NETBIOS) + + sizeof (TRANSPORT_ADDRESS); + + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiTransportAddress[i]; + } + + TAAddress = (PTRANSPORT_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + TAAddress->TAAddressCount = 1; + + AddressType = (PTA_ADDRESS)((PUCHAR)TAAddress + sizeof (TAAddress->TAAddressCount)); + + AddressType->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + AddressType->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + + AddressName = (PTDI_ADDRESS_NETBIOS)((PUCHAR)AddressType + + sizeof (AddressType->AddressType) + sizeof (AddressType->AddressLength)); + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + for (i=0;i<16;i++) { + AddressName->NetbiosName[i] = Name[i]; + } + } except(EXCEPTION_EXECUTE_HANDLER) { + + // + // Couldn't touch the passed parameters; just return an error + // status. + // + + return GetExceptionCode(); + } + } else { + EaBuffer = NULL; + Length = 0; + } + + InitializeObjectAttributes ( + &ObjectAttributes, + DeviceName, + 0, + NULL, + NULL); + + Status = NtCreateFile ( + FileHandle, + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access. + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + 0, // file attributes. + FILE_SHARE_READ | FILE_SHARE_WRITE, // share access. + FILE_CREATE, // create disposition. + 0, // create options. + EaBuffer, // EA buffer. + Length); // EA length. + + if (!NT_SUCCESS( Status )) { + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + } + + return Status; +} /* TdiOpenNetbiosAddress */ + +VOID +usage( + VOID + ) +{ + printf( "usage: tsttdi [-r] [-s] -[b]\n"); + printf( "usage: -r run receive test.\n" ); + printf( "usage: -b run server test.\n" ); + printf( "usage: -s run send test.\n" ); + printf( "\n" ); + exit( 1 ); +} + diff --git a/private/ntos/tdi/nbf/timer.c b/private/ntos/tdi/nbf/timer.c new file mode 100644 index 000000000..0664f0aa6 --- /dev/null +++ b/private/ntos/tdi/nbf/timer.c @@ -0,0 +1,2762 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code that implements the lightweight timer system + for the NBF protocol provider. This is not a general-purpose timer system; + rather, it is specific to servicing LLC (802.2) links with three timers + each. + + Services are provided in macro form (see NBFPROCS.H) to start and stop + timers. This module contains the code that gets control when the timer + in the device context expires as a result of calling kernel services. + The routine scans the device context's link database, looking for timers + that have expired, and for those that have expired, their expiration + routines are executed. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +ULONG StartTimer = 0; +ULONG StartTimerSet = 0; +ULONG StartTimerT1 = 0; +ULONG StartTimerT2 = 0; +ULONG StartTimerDelayedAck = 0; +ULONG StartTimerLinkDeferredAdd = 0; +ULONG StartTimerLinkDeferredDelete = 0; + + +#if DBG +extern ULONG NbfDebugPiggybackAcks; +ULONG NbfDebugShortTimer = 0; +#endif + +#if DBG +// +// These are temp, to track how the timers are working +// +ULONG TimerInsertsAtEnd = 0; +ULONG TimerInsertsEmpty = 0; +ULONG TimerInsertsInMiddle = 0; +#endif + +// +// These are constants calculated by InitializeTimerSystem +// to be the indicated amound divided by the tick increment. +// + +ULONG NbfTickIncrement = 0; +ULONG NbfTwentyMillisecondsTicks = 0; +ULONG NbfShortTimerDeltaTicks = 0; +ULONG NbfMaximumIntervalTicks = 0; // usually 60 seconds in ticks + +LARGE_INTEGER DueTimeDelta = { (ULONG)(-SHORT_TIMER_DELTA), -1 }; + +VOID +ExpireT2Timer( + PTP_LINK Link + ); + +VOID +StopStalledConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + + + +ULONG +GetTimerInterval( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + GetTimerInterval returns the difference in time between the + current time and Link->CurrentTimerStart (in ticks). + We limit the interval to 60 seconds. A value of 0 may + be returned which should be interpreted as 1/2. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + The interval. + +--*/ + +{ + + LARGE_INTEGER CurrentTick; + LARGE_INTEGER Interval; + + + // + // Determine the current tick; the start tick has been saved + // in Link->CurrentTimerStart. + // + + KeQueryTickCount (&CurrentTick); + + // + // Determine the difference between now and then. + // + + Interval.QuadPart = CurrentTick.QuadPart - + (Link->CurrentTimerStart).QuadPart; + + // + // If the gap is too big, return 1 minute. + // + + if (Interval.HighPart != 0 || (Interval.LowPart > NbfMaximumIntervalTicks)) { + return NbfMaximumIntervalTicks; + } + + return Interval.LowPart; + +} /* GetTimerInterval */ + + +VOID +BackoffCurrentT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called if T1 expires and we are about to + retransmit a poll frame. It backs off CurrentT1Timeout, + up to a limit of 10 seconds. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + + // + // We must have previously sent a poll frame if we are + // calling this. + // + // BUGBUG: we need spinlock guarding for MP. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + ++Link->CurrentPollRetransmits; + + // + // T1 backs off 1.5 times each time. + // + + Link->CurrentT1Timeout += (Link->CurrentT1Timeout >> 1); + + // + // Limit T1 to 10 seconds. + // + + if (Link->CurrentT1Timeout > ((10 * SECONDS) / SHORT_TIMER_DELTA)) { + Link->CurrentT1Timeout = (10 * SECONDS) / SHORT_TIMER_DELTA; + } + +} /* BackoffCurrentT1Timeout */ + + +VOID +UpdateBaseT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a response to a poll frame is + received. StartT1 will have been called when the frame is + sent. The routine updates the link's T1 timeout as well + as delay and throughput. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + ULONG Delay; + ULONG ShiftedTicksDelay; + + // + // We must have previously sent a poll frame if we are + // calling this. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + Delay = GetTimerInterval (Link); + + if (Link->CurrentPollRetransmits == 0) { + + // + // Convert the delay into NBF ticks, shifted by + // DLC_TIMER_ACCURACY and also multiplied by 4. + // We want to divide by SHORT_TIMER_DELTA, then + // shift left by DLC_TIMER_ACCURACY+2. We divide + // by NbfShortTimerDeltaTicks because the Delay + // is returned in ticks. + // + // We treat a delay of 0 as 1/2, so we use 1 + // shifted left by (DLC_TIMER_ACCURACY+1). + // + + if (Delay == 0) { + + ShiftedTicksDelay = (1 << (DLC_TIMER_ACCURACY + 1)) / + NbfShortTimerDeltaTicks; + + } else { + + ShiftedTicksDelay = (Delay << (DLC_TIMER_ACCURACY + 2)) / + NbfShortTimerDeltaTicks; + + } + + + // + // Use the timing information to update BaseT1Timeout, + // if the last frame sent was large enough to matter + // (we use half of the max frame size here). This is + // so we don't shrink the timeout too much after sending + // a short frame. However, we update even for small frames + // if the last time we sent a poll we had to retransmit + // it, since that means T1 is much too small and we should + // increase it as much as we can. We also update for any + // size frame if the new delay is bigger than the current + // value, so we can ramp up quickly if needed. + // + + if (ShiftedTicksDelay > Link->BaseT1Timeout) { + + // + // If our new delay is more, than we weight it evenly + // with the previous value. + // + + Link->BaseT1Timeout = (Link->BaseT1Timeout + + ShiftedTicksDelay) / 2; + + } else if (Link->CurrentT1Backoff) { + + // + // If we got a retransmit last time, then weight + // the new timer more heavily than usual. + // + + Link->BaseT1Timeout = ((Link->BaseT1Timeout * 3) + + ShiftedTicksDelay) / 4; + + } else if (Link->CurrentPollSize >= Link->BaseT1RecalcThreshhold) { + + // + // Normally, the new timeout is 7/8 the previous value and + // 1/8 the newly observed delay. + // + + Link->BaseT1Timeout = ((Link->BaseT1Timeout * 7) + + ShiftedTicksDelay) / 8; + + } + + // + // Restrict the real timeout to a minimum based on + // the link speed (always >= 400 ms). + // + + if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) { + + Link->BaseT1Timeout = Link->MinimumBaseT1Timeout; + + } + + + // + // Update link delay and throughput also. Remember + // that a delay of 0 should be interpreted as 1/2. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + + + // + // We had no retransmits last time, so go back to current base. + // + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + Link->CurrentT1Backoff = FALSE; + + } else { + + Link->CurrentT1Backoff = TRUE; + + if (!(Link->ThroughputAccurate)) { + + // + // If we are just starting up, we have to update the + // throughput even on a retransmit, so we get *some* + // value there. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + + } + + } + +} /* UpdateBaseT1Timeout */ + + +VOID +CancelT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when we have not received any + responses to a poll frame and are giving up rather + than retransmitting. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + + // + // We must have previously sent a poll frame if we are + // calling this. + // + // BUGBUG: We need spinlock guarding for MP. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + // + // We are stopping a polling condition, so reset T1. + // + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + Link->CurrentT1Backoff = FALSE; + + // + // Again, this isn't safe on MP (or UP, maybe). + // + + Link->CurrentPollOutstanding = FALSE; + +} /* CancelT1Timeout */ + + +VOID +UpdateDelayAndThroughput( + IN PTP_LINK Link, + IN ULONG TimerInterval + ) + +/*++ + +Routine Description: + + This routine is called when a response packet used to time + link delay has been received. It is assumed that StartT1 + or FakeStartT1 was called when the initial packet was sent. + + NOTE: For now, we also calculate throughput based on this. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + + TimerInterval - The link delay measured. + +Return Value: + + None. + +--*/ + +{ + + ULONG PacketSize; + + + if (Link->Delay == 0xffffffff) { + + // + // If delay is unknown, use this. + // + + Link->Delay = TimerInterval; + + } else if (Link->CurrentPollSize <= 64) { + + // + // Otherwise, for small frames calculate the new + // delay by averaging with the old one. + // + + Link->Delay = (Link->Delay + TimerInterval) / 2; + + } + + + // + // Calculate the packet size times the number of time units + // in 10 milliseconds, which will allow us to calculate + // throughput in bytes/10ms (we later multiply by 100 + // to obtain the real throughput in bytes/s). + // + // Given the size of MILLISECONDS, this allows packets of up + // to ~20K, so for bigger packets we just assume that (since + // throughput won't be an issue there). + // + + if (Link->CurrentPollSize > 20000) { + PacketSize = 20000 * (10 * MILLISECONDS); + } else { + PacketSize = Link->CurrentPollSize * (10*MILLISECONDS); + } + + // + // If throughput is not accurate, then we will use this + // packet only to calculate it. To avoid being confused + // by very small packets, assume a minimum size of 64. + // + + if ((!Link->ThroughputAccurate) && (PacketSize < (64*(10*MILLISECONDS)))) { + PacketSize = 64 * (10*MILLISECONDS); + } + + // + // PacketSize is going to be divided by TimerInterval; + // to prevent a zero throughput, we boost it up if needed. + // + + if (PacketSize < TimerInterval) { + PacketSize = TimerInterval; + } + + + if (Link->CurrentPollSize >= 512) { + + // + // Calculate throughput here by removing the established delay + // from the time. + // + + if ((Link->Delay + (2*MILLISECONDS)) < TimerInterval) { + + // + // If the current delay is less than the new timer + // interval (plus 2 ms), then subtract it off for a + // more accurate throughput calculation. + // + + TimerInterval -= Link->Delay; + + } + + // + // We assume by this point (sending a > 512-byte frame) we + // already have something established as Link->Throughput. + // + + if (!(Link->ThroughputAccurate)) { + + Link->Throughput.QuadPart = + UInt32x32To64((PacketSize / TimerInterval), 100); + + Link->ThroughputAccurate = TRUE; + +#if 0 + NbfPrint2 ("INT: %ld.%1.1d us\n", + TimerInterval / 10, TimerInterval % 10); + NbfPrint4 ("D: %ld.%1.1d us T: %ld (%d)/s\n", + Link->Delay / 10, Link->Delay % 10, + Link->Throughput.LowPart, Link->CurrentPollSize); +#endif + + } else { + + LARGE_INTEGER TwiceThroughput; + + // + // New throughput is the average of the old throughput, and + // the current packet size divided by the delay just observed. + // First we calculate the sum, then we shift right by one. + // + + TwiceThroughput.QuadPart = Link->Throughput.QuadPart + + UInt32x32To64((PacketSize / TimerInterval), 100); + + Link->Throughput.QuadPart = TwiceThroughput.QuadPart >> 1; + } + + } else if (!(Link->ThroughputAccurate)) { + + // + // We don't have accurate throughput, so just get an estimate + // by ignoring the delay on this small frame. + // + + Link->Throughput.QuadPart = + UInt32x32To64((PacketSize / TimerInterval), 100); + + } + +} /* UpdateDelayAndThroughput */ + + +VOID +FakeStartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ) + +/*++ + +Routine Description: + + This routine is called before sending a packet that will be used + to time link delay, but where StartT1 will not be started. + It is assumed that FakeUpdateBaseT1Timeout will be called + when the response is received. This is used for timing + frames that have a known immediate response, but are not + poll frames. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + + PacketSize - The size of the packet that was just sent. + +Return Value: + + None. + +--*/ + +{ + + Link->CurrentPollSize = PacketSize; + KeQueryTickCount(&Link->CurrentTimerStart); + +} /* FakeStartT1 */ + + +VOID +FakeUpdateBaseT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a response to a frame is + received, and we called FakeStartT1 when the initial + frame was sent. This is used for timing frames that have + a known immediate response, but are not poll frames. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + + +{ + ULONG Delay; + + Delay = GetTimerInterval (Link); + + // + // Convert the delay into NBF ticks, shifted by + // DLC_TIMER_ACCURACY and also multiplied by 4. + // We want to divide by SHORT_TIMER_DELTA, then + // shift left by DLC_TIMER_ACCURACY+2. We divide + // by NbfShortTimerDeltaTicks because the Delay + // is returned in ticks. We treat a Delay of 0 + // as 1/2 and calculate ((1/2) << x) as (1 << (x-1)). + // + // This timeout is treated as the correct value. + // + + if (Delay == 0) { + + Link->BaseT1Timeout = (1 << (DLC_TIMER_ACCURACY + 1)) / + NbfShortTimerDeltaTicks; + + } else { + + Link->BaseT1Timeout = (Delay << (DLC_TIMER_ACCURACY + 2)) / + NbfShortTimerDeltaTicks; + + } + + // + // Restrict the real timeout to a minimum based on + // the link speed (always >= 400 ms). + // + + if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) { + Link->BaseT1Timeout = Link->MinimumBaseT1Timeout; + } + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + // + // Update link delay and throughput also. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + +} /* FakeUpdateBaseT1Timeout */ + + +VOID +StartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ) + +/*++ + +Routine Description: + + This routine starts the T1 timer for the given link. If the link was + already on the list, it is moved to the tail. If not, it is inserted at + tail. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + + PollPacketSize - If a poll packet was just sent it is its size; + otherwise this will be 0 (when non-poll I-frames are sent). + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + if (PacketSize > 0) { + + // + // If we are sending an initial poll frame, then do timing stuff. + // + + Link->CurrentPollRetransmits = 0; + Link->CurrentPollSize = PacketSize; + Link->CurrentPollOutstanding = TRUE; + KeQueryTickCount(&Link->CurrentTimerStart); + + } else { + + Link->CurrentPollOutstanding = FALSE; + + } + + + // + // Insert us in the queue if we aren't in it. + // + + Link->T1 = DeviceContext->ShortAbsoluteTime+Link->CurrentT1Timeout; + + if (!Link->OnShortList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnShortList) { + Link->OnShortList = TRUE; + InsertTailList (&DeviceContext->ShortList, &Link->ShortList); + } + + if (!DeviceContext->a.i.ShortListActive) { + + StartTimerT1++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.ShortListActive = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + +} + + +VOID +StartT2( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds the given link to the T2 queue and starts the timer. + If the link is already on the queue, it is moved to the queue end. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + + if (DeviceContext->MacInfo.MediumAsync) { + + // + // On an async line, expire it as soon as possible. + // + + Link->T2 = DeviceContext->ShortAbsoluteTime; + + } else { + + Link->T2 = DeviceContext->ShortAbsoluteTime+Link->T2Timeout; + + } + + + if (!Link->OnShortList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnShortList) { + Link->OnShortList = TRUE; + InsertTailList (&DeviceContext->ShortList, &Link->ShortList); + } + + if (!DeviceContext->a.i.ShortListActive) { + + StartTimerT2++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.ShortListActive = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + +} + + +VOID +StartTi( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds the given link to the Ti queue and starts the timer. + As above, if the link is already on the queue it is moved to the queue end. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + + // + // On an easily disconnected link, with only server connections + // on this link, we set a long Ti timeout, and when it + // expires with no activity we start checkpointing, otherwise + // we assume things are OK. + // + + if (DeviceContext->EasilyDisconnected && Link->NumberOfConnectors == 0) { + Link->Ti = DeviceContext->LongAbsoluteTime + (2 * Link->TiTimeout); + Link->TiStartPacketsReceived = Link->PacketsReceived; + } else { + Link->Ti = DeviceContext->LongAbsoluteTime+Link->TiTimeout; + } + + + if (!Link->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnLongList) { + Link->OnLongList = TRUE; + InsertTailList (&DeviceContext->LongList, &Link->LongList); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + + +} + +#if DBG + +VOID +StopT1( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + // + // Again, this isn't safe on MP (or UP, maybe). + // + + Link->CurrentPollOutstanding = FALSE; + Link->T1 = 0; + +} + + +VOID +StopT2( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + Link->ConsecutiveIFrames = 0; + Link->T2 = 0; + +} + + +VOID +StopTi( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + Link->Ti = 0; +} +#endif + + +VOID +ExpireT1Timer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's T1 timer expires. T1 is the + retransmission timer, and is used to remember that a response is + expected to any of the following: (1) a checkpoint, (2) a transmitted + I-frame, (3) a SABME, or (4) a DISC. Cases 3 and 4 are actually + special forms of a checkpoint, since they are sent by this protocol + implementation with the poll bit set, effectively making them a + checkpoint sequence. + +Arguments: + + Link - Pointer to the TP_LINK object whose T1 timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDLC_I_FRAME DlcHeader; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + switch (Link->State) { + + case LINK_STATE_ADM: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: State=ADM, timeout not expected.\n"); + } + break; + + case LINK_STATE_READY: + + // + // We've sent an I-frame and haven't received an acknowlegement + // yet, or we are checkpointing, and must retry the checkpoint. + // Another possibility is that we're rejecting, and he hasn't + // sent anything yet. + // + + switch (Link->SendState) { + + case SEND_STATE_DOWN: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link READY but SendState=DOWN.\n"); + } + break; + + case SEND_STATE_READY: + + // + // We sent an I-frame and didn't get an acknowlegement. + // Initiate a checkpoint sequence. + // + + IF_NBFDBG (NBF_DEBUG_TIMER) { + {PTP_PACKET packet; + PLIST_ENTRY p; + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=READY .\n"); + NbfDumpLinkInfo (Link); + p=Link->WackQ.Flink; + NbfPrint0 ("ExpireT1Timer: Link WackQ entries:\n"); + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + NbfPrint2 (" %08lx %03d\n", p, + (DlcHeader->SendSeq >> 1)); + p = p->Flink; + }} + } + + Link->SendRetries = (UCHAR)Link->LlcRetries; + Link->SendState = SEND_STATE_CHECKPOINTING; + // Don't BackoffT1Timeout yet. + NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock + break; + + case SEND_STATE_REJECTING: + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=REJECTING.\n"); + NbfPrint0 ("so what do we do here? consult the manual...\n"); + } + Link->SendState = SEND_STATE_CHECKPOINTING; +// Link->SendRetries = Link->LlcRetries; +// break; // DGB: doing nothing is obviously wrong, we've +// // gotten a T1 expiration during resend. Try +// // an RR to say hey. + + case SEND_STATE_CHECKPOINTING: + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=CHECKPOINTING.\n"); + NbfDumpLinkInfo (Link); + } + if (--Link->SendRetries == 0) { + + // + // We have not gotten any response to RR-p packets, + // initiate orderly link teardown. + // + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfStopLink (Link); + + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer sending DISC (checkpoint failed)\n" ); + } +#endif + } else { + + BackoffCurrentT1Timeout (Link); + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock. + + } + break; + + default: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint1 ("ExpireT1Timer: Link State=READY, SendState=%ld (UNKNOWN).\n", + Link->SendState); + } + } + break; + + case LINK_STATE_CONNECTING: + + // + // We sent a SABME-c/p and have not yet received UA-r/f. This + // means we must decrement the retry count and if it is not yet + // zero, we issue another SABME command, because he has probably + // dropped our first one. + // + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to SABME)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in CONNECTING mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + } else { + BackoffCurrentT1Timeout (Link); + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + } + break; + + case LINK_STATE_W_POLL: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: State=W_POLL, timeout not expected.\n"); + } + break; + + case LINK_STATE_W_FINAL: + + // + // We sent our initial RR-c/p and have not received his RR-r/f. + // We have to restart the checkpoint, unless our retries have + // run out, in which case we just abort the link. + // + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=W_FINAL.\n"); + NbfDumpLinkInfo (Link); + } + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + + } else { + + BackoffCurrentT1Timeout (Link); + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock + + } + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We sent a DISC-c/p to disconnect this link and are awaiting + // his response, either a UA-r/f or DM-r/f. We have to issue + // the DISC again, unless we've tried a few times, in which case + // we just shut the link down. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ExpireT1Timer: Link State=W_DISC_RESP.\n"); + NbfDumpLinkInfo (Link); + } + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to DISC)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_DISC_RSP mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + + } else { + + // we don't bother calling BackoffCurrentT1Timeout for DISCs. + ++Link->CurrentPollRetransmits; + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // startup timer again. + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfSendDisc (Link, TRUE); // send DISC/p. + + } + break; + + default: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint1 ("ExpireT1Timer: State=%ld (UNKNOWN), timeout not expected.\n", + Link->State); + } + } + + +} /* ExpireT1Timer */ + + +VOID +ExpireT2Timer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's T2 timer expires. T2 is the + delayed acknowlegement timer in the LLC connection-oriented procedures. + The T2 timer is started when a valid I-frame is received but not + immediately acknowleged. Then, if reverse I-frame traffic is sent, + the timer is stopped, since the reverse traffic will acknowlege the + received I-frames. If no reverse I-frame traffic becomes available + to send, then this timer fires, causing a RR-r/0 to be sent so as + to acknowlege the received but as yet unacked I-frames. + +Arguments: + + Link - Pointer to the TP_LINK object whose T2 timer has expired. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT2Timer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfSendRr (Link, FALSE, FALSE); // send RR-r/f, release lock. + +} /* ExpireT2Timer */ + + +VOID +ExpireTiTimer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's Ti timer expires. Ti is the + inactivity timer, and serves as a keep-alive on a link basis, to + periodically perform some protocol exchange with the remote connection + partner that will implicitly reveal whether the link is still active + or not. This implementation simply uses a checkpoint sequence, but + some other protocols may choose to add protocol, including sending + a NetBIOS SESSION_ALIVE frame. If a checkpoint sequence is already + in progress, then we do nothing. + + This timer expiration routine is self-perpetuating; that is, it starts + itself after finishing its tasks every time. + +Arguments: + + Link - Pointer to the TP_LINK object whose Ti timer has expired. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireTiTimer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if ((Link->State != LINK_STATE_ADM) && + (Link->State != LINK_STATE_W_DISC_RSP) && + (Link->SendState != SEND_STATE_CHECKPOINTING)) { + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireTiTimer: Entered.\n"); + NbfDumpLinkInfo (Link); + } + + if (Link->Provider->EasilyDisconnected && Link->NumberOfConnectors == 0) { + + // + // On an easily disconnected network with only server connections, + // if there has been no activity in this timeout period then + // we trash the connection. + // + + if (Link->PacketsReceived == Link->TiStartPacketsReceived) { + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM); + + } else { + + // + // There was traffic, restart the timer. + // + + StartTi (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + } else { + +#if 0 + if ((Link->SendState == SEND_STATE_READY) && + (Link->T1 == 0) && + (!IsListEmpty (&Link->WackQ))) { + + // + // If we think the link is idle but there are packets + // on the WackQ, the link is messed up, disconnect it. + // + + NbfPrint1 ("NBF: Link %d hung at Ti expiration, recovering\n", Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + + } else { +#endif + + Link->SendState = SEND_STATE_CHECKPOINTING; + Link->PacketsSent = 0; + Link->PacketsResent = 0; + Link->PacketsReceived = 0; + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock. + +#if 0 + } +#endif + + } + + } else { + + Link->PacketsSent = 0; + Link->PacketsResent = 0; + Link->PacketsReceived = 0; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + if (Link->SendState == SEND_STATE_REJECTING) { + NbfPrint0 ("ExpireTiTimer: link state == rejecting, shouldn't be\n"); + } +#endif + + } + +#if 0 + // + // Startup the inactivity timer again. + // + + if (Link->State != LINK_STATE_ADM) { + StartTi (Link); + } +#endif + +} /* ExpireTiTimer */ + +#if 0 + +VOID +ExpirePurgeTimer( + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine is called when the device context's periodic adaptive + window algorithm timer expires. The timer perpetuates itself on a + regular basis. + +Arguments: + + DeviceContext - Pointer to the device context whose purge timer has expired. + +Return Value: + + none. + +--*/ + +{ + PTP_LINK Link; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpirePurgeTimer: Entered.\n"); + } + + // + // Scan through the link database on this device context and clear + // their worst window size limit. This will allow stuck links to + // grow their window again even though they encountered temporary + // congestion at the remote link station's adapter. + // + + while (!IsListEmpty (&DeviceContext->PurgeList)) { + p = RemoveHeadList (&DeviceContext->PurgeList); + Link = CONTAINING_RECORD (p, TP_LINK, PurgeList); + Link->WorstWindowSize = Link->MaxWindowSize; // maximum window possible. + + } + + // + // Restart purge timer. + // + + DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime + TIMER_PURGE_TICKS; + + +} /* ExpirePurgeTimer */ +#endif + + +VOID +ScanShortTimersDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is called at DISPATCH_LEVEL by the system at regular + intervals to determine if any link-level timers have expired, and + if they have, to execute their expiration routines. + +Arguments: + + DeferredContext - Pointer to our DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + PTP_CONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_TIMERDPC) { + NbfPrint0 ("ScanShortTimersDpc: Entered.\n"); + } + + DeviceContext = DeferredContext; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + DeviceContext->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + (DeviceContext->ShortTimerStart).QuadPart; + + TickDelta = TickDifference.LowPart / NbfShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + DeviceContext->ShortAbsoluteTime += TickDelta; + + if (DeviceContext->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + DeviceContext->ShortAbsoluteTime -= 0xe0000000; + + p = DeviceContext->ShortList.Flink; + while (p != &DeviceContext->ShortList) { + + Link = CONTAINING_RECORD (p, TP_LINK, ShortList); + + Timeout = Link->T1; + if (Timeout) { + Link->T1 = Timeout - 0xe0000000; + } + + Timeout = Link->T2; + if (Timeout) { + Link->T2 = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + // + // now, as the timers are started, links are added to the end of the + // respective queue for that timer. since we know the additions are + // done in an orderly fashion and are sequential, we must only traverse + // a particular timer list to the first entry that is greater than our + // timer. That entry and all further entries will not need service. + // When a timer is cancelled, we remove the link from the list. With all + // of this fooling around, we wind up only visiting those links that are + // actually in danger of timing out, minimizing time in this routine. + // + + // T1 timers first; this is the link-level response expected timer, and is + // the shortest one. + // T2 timers. This is the iframe response expected timer, and is typically + // about 300 ms. + + p = DeviceContext->ShortList.Flink; + while (p != &DeviceContext->ShortList) { + + Link = CONTAINING_RECORD (p, TP_LINK, ShortList); + + ASSERT (Link->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Link->State != LINK_STATE_ADM) { + + if (Link->T1 && (DeviceContext->ShortAbsoluteTime > Link->T1)) { + + Link->T1 = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireT1Timer (Link); // no spinlocks held + INCREMENT_COUNTER (DeviceContext, ResponseTimerExpirations); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + if (Link->T2 && (DeviceContext->ShortAbsoluteTime > Link->T2)) { + + Link->T2 = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireT2Timer (Link); // no spinlocks held + INCREMENT_COUNTER (DeviceContext, AckTimerExpirations); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + } + + if (!Link->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBF: Stop processing ShortList, %lx removed\n", Link); +#endif + break; + + } + + nextp = p->Flink; + + if ((Link->T1 == 0) && (Link->T2 == 0)) { + Link->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Link->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if ((Link->T1 != 0) || (Link->T2 != 0)) { + InsertTailList(&DeviceContext->ShortList, &Link->ShortList); + Link->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&DeviceContext->ShortList)) { + DeviceContext->a.i.ShortListActive = FALSE; + } + + // + // NOTE: DeviceContext->TimerSpinLock is held here. + // + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for NbfDeferredPasses times through + // here. If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + // NOTE: There is no expiration time for connections on this + // queue; it "expires" every time ScanShortTimersDpc runs. + // + + + for (p = DeviceContext->DataAckQueue.Flink; + p != &DeviceContext->DataAckQueue; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0) && + ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) { + continue; + } + + TickDifference.QuadPart = CurrentTick.QuadPart - + (Connection->ConnectStartTime).QuadPart; + + if ((TickDifference.HighPart == 0) && + (TickDifference.LowPart <= NbfTwentyMillisecondsTicks)) { + continue; + } + + NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + DeviceContext->DataAckQueueChanged = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) && + ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // + // We have to ensure we nest the spin lock acquisition + // correctly. + // + + Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_ACK; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (DeviceContext, PiggybackAckTimeouts); + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("T"); + } +#endif + + NbfSendDataAck (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (DeviceContext->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&DeviceContext->DataAckQueue)) { + DeviceContext->a.i.DataAckQueueActive = FALSE; + } + +#if 0 + + // + // NOTE: This is currently disabled, it may be reenabled + // at some point - adamba 9/1/92 + // + // If the adaptive purge timer has expired, then run the purge + // algorithm on all affected links. + // + + if (DeviceContext->ShortAbsoluteTime > DeviceContext->AdaptivePurge) { + DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime + + TIMER_PURGE_TICKS; + ExpirePurgeTimer (DeviceContext); + } +#endif + + // + // deferred processing. We will handle all link structure additions and + // deletions here; we must be the exclusive user of the link tree to do + // this. We verify that we are by examining the semaphore that tells us + // how many readers of the tree are curretly processing it. If there are + // any readers, we simply increment our "deferred processing locked out" + // counter and do something else. If we defer too many times, we simply + // bugcheck, as something is wrong somewhere in the system. + // + + if (!IsListEmpty (&DeviceContext->LinkDeferred)) { + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // now do additions or deletions if we can. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + while (!IsListEmpty (&DeviceContext->LinkDeferred)) { + p = RemoveHeadList (&DeviceContext->LinkDeferred); + DeviceContext->DeferredNotSatisfied = 0; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // now do an addition or deletion if we can. + // + + Link = CONTAINING_RECORD (p, TP_LINK, DeferredList); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint4 ("ScanShortTimersDPC: link off deferred queue %lx %lx %lx Flags: %lx \n", + Link, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, Link->DeferredFlags); + } + Link->DeferredList.Flink = Link->DeferredList.Blink = + &Link->DeferredList; + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) { + // Tried to do an operation we don't understand; whine. + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint2 ("ScanTimerDPC: Attempting deferred operation on nothing! \nScanTimerDPC: Link: %lx, DeviceContext->DeferredQueue: %lx\n", + Link, &DeviceContext->LinkDeferred); + DbgBreakPoint (); + } + InitializeListHead (&DeviceContext->LinkDeferred); + // We could have a hosed deferred operations queue here; + // BUGBUG: take some time to figure out if it is ok. + + } + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_ADD) != 0) { + + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_ADD; + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + + // + // It is being added and deleted; just destroy it. + // + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfDestroyLink (Link); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: Add AND Delete link: %lx\n",Link); + } + + } else { + + NbfAddLinkToTree (DeviceContext, Link); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: Added link to tree: %lx\n",Link); + } + + } + + } else if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfRemoveLinkFromTree (DeviceContext, Link); + NbfDestroyLink (Link); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: returning link %lx to LinkPool.\n", Link); + } + + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + + InitializeListHead (&DeviceContext->LinkDeferred); + + DeviceContext->a.i.LinkDeferredActive = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DataFrameBytesSent, + DeviceContext->TempIFrameBytesSent); + DeviceContext->Statistics.DataFramesSent += DeviceContext->TempIFramesSent; + + DeviceContext->TempIFrameBytesSent = 0; + DeviceContext->TempIFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DataFrameBytesReceived, + DeviceContext->TempIFrameBytesReceived); + DeviceContext->Statistics.DataFramesReceived += DeviceContext->TempIFramesReceived; + + DeviceContext->TempIFrameBytesReceived = 0; + DeviceContext->TempIFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + DeviceContext->ProcessingShortTimer = FALSE; + + if (DeviceContext->a.AnyActive && + (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&DeviceContext->ShortTimerStart); + KeSetTimer ( + &DeviceContext->ShortSystemTimer, + DueTimeDelta, + &DeviceContext->ShortTimerSystemDpc); + + } else { + +#if DBG + if (NbfDebugShortTimer) { + DbgPrint("x"); + } +#endif + NbfDereferenceDeviceContext ("Don't restart short timer", DeviceContext, DCREF_SCAN_TIMER); + + } + + + LEAVE_NBF; + return; + +} /* ScanShortTimersDpc */ + + +VOID +ScanLongTimersDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is called at DISPATCH_LEVEL by the system at regular + intervals to determine if any long timers have expired, and + if they have, to execute their expiration routines. + +Arguments: + + DeferredContext - Pointer to our DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER DueTime; + PLIST_ENTRY p, nextp; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_TIMERDPC) { + NbfPrint0 ("ScanLongTimersDpc: Entered.\n"); + } + + DeviceContext = DeferredContext; + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (++DeviceContext->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + DeviceContext->LongAbsoluteTime = 0x10000000; + + p = DeviceContext->LongList.Flink; + while (p != &DeviceContext->LongList) { + + Link = CONTAINING_RECORD (p, TP_LINK, LongList); + + Timeout = Link->Ti; + if (Timeout) { + Link->Ti = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + // + // now, as the timers are started, links are added to the end of the + // respective queue for that timer. since we know the additions are + // done in an orderly fashion and are sequential, we must only traverse + // a particular timer list to the first entry that is greater than our + // timer. That entry and all further entries will not need service. + // When a timer is cancelled, we remove the link from the list. With all + // of this fooling around, we wind up only visiting those links that are + // actually in danger of timing out, minimizing time in this routine. + // + + + // + // Ti timers. This is the inactivity timer for the link, used when no + // activity has occurred on the link in some time. We only check this + // every four expirations of the timer since the granularity is usually + // in the 30 second range. + // NOTE: DeviceContext->TimerSpinLock is held here. + // + + if ((DeviceContext->LongAbsoluteTime % 4) == 0) { + + p = DeviceContext->LongList.Flink; + while (p != &DeviceContext->LongList) { + + Link = CONTAINING_RECORD (p, TP_LINK, LongList); + + ASSERT (Link->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + +#if DBG + if (Link->SendState == SEND_STATE_REJECTING) { + NbfPrint0 ("Timer: link state == rejecting, shouldn't be\n"); + } +#endif + + if (Link->State != LINK_STATE_ADM) { + + if (Link->Ti && (DeviceContext->LongAbsoluteTime > Link->Ti)) { + + Link->Ti = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireTiTimer (Link); // no spinlocks held + ++DeviceContext->TiExpirations; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + } + + if (!Link->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBF: Stop processing LongList, %lx removed\n", Link); +#endif + break; + + } + + nextp = p->Flink; + + if (Link->Ti == 0) { + + Link->OnLongList = FALSE; + RemoveEntryList(p); + + if (Link->Ti != 0) { + InsertTailList(&DeviceContext->LongList, &Link->LongList); + Link->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + p = DeviceContext->DataAckQueue.Flink; + + while (p != &DeviceContext->DataAckQueue && + !DeviceContext->DataAckQueueChanged) { + + Connection = CONTAINING_RECORD (DeviceContext->DataAckQueue.Flink, TP_CONNECTION, DataAckLinkage); + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + p = p->Flink; + continue; + } + + NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Have to check again, because the connection might + // just have been stopped. + // + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + DeviceContext->DataAckQueueChanged = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + + // + // See if we got any multicast traffic last time. + // + + if (DeviceContext->MulticastPacketCount == 0) { + + ++DeviceContext->LongTimeoutsWithoutMulticast; + + if (DeviceContext->EasilyDisconnected && + (DeviceContext->LongTimeoutsWithoutMulticast > 5)) { + + PLIST_ENTRY p; + PTP_ADDRESS address; + + // + // We have had five timeouts in a row with no + // traffic, mark all the addresses as needing + // reregistration next time a connect is + // done on them. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + address->Flags |= ADDRESS_FLAGS_NEED_REREGISTER; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + } + + } else { + + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + } + + DeviceContext->MulticastPacketCount = 0; + + + // + // Every thirty seconds, check for stalled connections + // + + ++DeviceContext->StalledConnectionCount; + + if (DeviceContext->StalledConnectionCount == + (USHORT)((30 * SECONDS) / LONG_TIMER_DELTA)) { + + DeviceContext->StalledConnectionCount = 0; + StopStalledConnections (DeviceContext); + + } + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbfReceiveComplete((NDIS_HANDLE)DeviceContext); + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING) { + DueTime.HighPart = -1; + DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA); // delta time to next click. + KeSetTimer ( + &DeviceContext->LongSystemTimer, + DueTime, + &DeviceContext->LongTimerSystemDpc); + } else { + NbfDereferenceDeviceContext ("Don't restart long timer", DeviceContext, DCREF_SCAN_TIMER); + } + + LEAVE_NBF; + return; + +} /* ScanLongTimersDpc */ + + +VOID +StopStalledConnections( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine is called from ScanLongTimersDpc every 30 seconds. + It checks for connections that have not made any progress in + their sends in the last two minutes, and stops them. + +Arguments: + + DeviceContext - The device context to check. + +Return Value: + + none. + +--*/ + +{ + + PTP_ADDRESS Address, PrevAddress; + PTP_CONNECTION Connection, StalledConnection; + PLIST_ENTRY p, q; + + + // + // If we have crossed a thirty-second interval, then + // check each address for connections that have not + // made any progress in two minutes. + // + + PrevAddress = NULL; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD ( + p, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // By referencing the address, we ensure that it will stay + // in the AddressDatabase, this its Flink will stay valid. + // + + NbfReferenceAddress("checking for dead connections", Address, AREF_TIMER_SCAN); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (PrevAddress) { + NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN); + } + + // + // Scan this addresses connection database for connections + // that have not made progress in the last two minutes; we + // kill the first one we find. + // + + StalledConnection = NULL; + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + for (q = Address->ConnectionDatabase.Flink; + q != &Address->ConnectionDatabase; + q = q->Flink) { + + Connection = CONTAINING_RECORD (q, TP_CONNECTION, AddressList); + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (!IsListEmpty (&Connection->SendQueue)) { + + // + // If there is a connection on the queue... + // + + if (Connection->StallBytesSent == Connection->sp.MessageBytesSent) { + + // + // ...and it has not made any progress... + // + + if (Connection->StallCount >= 4) { + + // + // .. four times in a row, the connection is dead. + // + + if (!StalledConnection) { + StalledConnection = Connection; + NbfReferenceConnection ("stalled", Connection, CREF_STALLED); + } +#if DBG + DbgPrint ("NBF: Found connection %lx [%d for %d] stalled on %lx\n", + Connection, Connection->StallBytesSent, Connection->StallCount, Address); +#endif + + } else { + + // + // If it is stuck, increment the count. + // + + ++Connection->StallCount; + + } + + } else { + + Connection->StallBytesSent = Connection->sp.MessageBytesSent; + + } + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (StalledConnection) { + + PTP_LINK Link = StalledConnection->Link; + +#if DBG + DbgPrint("NBF: Stopping stalled connection %lx, link %lx\n", StalledConnection, Link); +#endif + + FailSend (StalledConnection, STATUS_IO_TIMEOUT, TRUE); // fail the send + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->State == LINK_STATE_READY) { + CancelT1Timeout (Link); + Link->State = LINK_STATE_W_DISC_RSP; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + } + + NbfDereferenceConnection ("stalled", StalledConnection, CREF_STALLED); + + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + PrevAddress = Address; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (PrevAddress) { + NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN); + } + +} /* StopStalledConnections */ + + +VOID +NbfStartShortTimer( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + // We use a trick to check all four active lists at the + // same time, but this depends on some alignment and + // size assumptions. + // + + ASSERT (sizeof(ULONG) >= 3 * sizeof(BOOLEAN)); + ASSERT ((PVOID)&DeviceContext->a.AnyActive == + (PVOID)&DeviceContext->a.i.ShortListActive); + + StartTimer++; + + if ((!DeviceContext->ProcessingShortTimer) && + (!(DeviceContext->a.AnyActive))) { + +#if DBG + if (NbfDebugShortTimer) { + DbgPrint("X"); + } +#endif + + NbfReferenceDeviceContext ("Start short timer", DeviceContext, DCREF_SCAN_TIMER); + + KeQueryTickCount(&DeviceContext->ShortTimerStart); + StartTimerSet++; + KeSetTimer ( + &DeviceContext->ShortSystemTimer, + DueTimeDelta, + &DeviceContext->ShortTimerSystemDpc); + + } + +} /* NbfStartShortTimer */ + + +VOID +NbfInitializeTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER DueTime; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("NbfInitializeTimerSystem: Entered.\n"); + } + + // + // Set these up. + // + + NbfTickIncrement = KeQueryTimeIncrement(); + + if (NbfTickIncrement > (20 * MILLISECONDS)) { + NbfTwentyMillisecondsTicks = 1; + } else { + NbfTwentyMillisecondsTicks = (20 * MILLISECONDS) / NbfTickIncrement; + } + + if (NbfTickIncrement > (SHORT_TIMER_DELTA)) { + NbfShortTimerDeltaTicks = 1; + } else { + NbfShortTimerDeltaTicks = (SHORT_TIMER_DELTA) / NbfTickIncrement; + } + + // + // MaximumIntervalTicks represents 60 seconds, unless the value + // when shifted out by the accuracy required is too big. + // + + if ((((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY+2)) > ((60 * SECONDS) / NbfTickIncrement)) { + NbfMaximumIntervalTicks = (60 * SECONDS) / NbfTickIncrement; + } else { + NbfMaximumIntervalTicks = ((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY + 2); + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + DeviceContext->ShortAbsoluteTime = 0x10000000; // initialize our timer click up-counter. + DeviceContext->LongAbsoluteTime = 0x10000000; // initialize our timer click up-counter. + + DeviceContext->AdaptivePurge = TIMER_PURGE_TICKS; + + DeviceContext->MulticastPacketCount = 0; + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + KeInitializeDpc( + &DeviceContext->ShortTimerSystemDpc, + ScanShortTimersDpc, + DeviceContext); + + KeInitializeDpc( + &DeviceContext->LongTimerSystemDpc, + ScanLongTimersDpc, + DeviceContext); + + KeInitializeTimer (&DeviceContext->ShortSystemTimer); + + KeInitializeTimer (&DeviceContext->LongSystemTimer); + + DueTime.HighPart = -1; + DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA); + + // + // One reference for the long timer. + // + + NbfReferenceDeviceContext ("Long timer active", DeviceContext, DCREF_SCAN_TIMER); + + KeSetTimer ( + &DeviceContext->LongSystemTimer, + DueTime, + &DeviceContext->LongTimerSystemDpc); + + DeviceContext->TimersInitialized = TRUE; + +} /* NbfInitializeTimerSystem */ + + +VOID +NbfStopTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine stops the lightweight timer system for the transport + provider. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // For now we ignore what happens if the timer is currently + // running when we do this. + // + + if (DeviceContext->TimersInitialized) { + + if (KeCancelTimer( + &DeviceContext->LongSystemTimer)) { + NbfDereferenceDeviceContext ("Long timer cancelled", DeviceContext, DCREF_SCAN_TIMER); + } + + if (KeCancelTimer( + &DeviceContext->ShortSystemTimer)) { + NbfDereferenceDeviceContext ("Short timer cancelled", DeviceContext, DCREF_SCAN_TIMER); + } + + } + +} diff --git a/private/ntos/tdi/nbf/uframes.c b/private/ntos/tdi/nbf/uframes.c new file mode 100644 index 000000000..654f92ff6 --- /dev/null +++ b/private/ntos/tdi/nbf/uframes.c @@ -0,0 +1,2974 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + uframes.c + +Abstract: + + This module contains a routine called NbfProcessUi, that gets control + from routines in DLC.C when a DLC UI frame is received. Here we + decode the encapsulated connectionless NetBIOS frame and dispatch + to the correct NetBIOS frame handler. + + The following frame types are cracked by routines in this module: + + o NBF_CMD_ADD_GROUP_NAME_QUERY + o NBF_CMD_ADD_NAME_QUERY + o NBF_CMD_NAME_IN_CONFLICT + o NBF_CMD_STATUS_QUERY + o NBF_CMD_TERMINATE_TRACE + o NBF_CMD_DATAGRAM + o NBF_CMD_DATAGRAM_BROADCAST + o NBF_CMD_NAME_QUERY + o NBF_CMD_ADD_NAME_RESPONSE + o NBF_CMD_NAME_RECOGNIZED + o NBF_CMD_STATUS_RESPONSE + o NBF_CMD_TERMINATE_TRACE2 + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbfListenTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the session setup after listening a connection occurs. This + will occur if the remote has discovered our name and we do not get a + connection started within some reasonable period of time. In this + routine we simply tear down the connection (and, most likely, the link + associated with it). + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_CONNECTION block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Connection = (PTP_CONNECTION)DeferredContext; + + // + // If this connection is being run down, then we can't do anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) == 0)) { + + // + // The connection is stopping, or the SESSION_INITIALIZE + // has already been processed. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx stopping.\n", + Connection); + } + + NbfDereferenceConnection ("Listen timeout, ignored", Connection, CREF_TIMER); + LEAVE_NBF; + return; + } + + // + // We connected to the link before sending the NAME_RECOGNIZED, + // so we disconnect from it now. + // + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "NbfListenTimeout disconnecting connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + + // + // BUBGUG: This is really ugly and I doubt it is correct. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_ACCEPTED) != 0) { + + // + // This connection is up, we stop it. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, accepted.\n", + Connection); + } + + // + // Set this so that the client will get a disconnect + // indication. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfStopConnection (Connection, STATUS_IO_TIMEOUT); + + } else if (Connection->Link != (PTP_LINK)NULL) { + + // + // This connection is from a listen...we want to + // silently reset the listen. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, listen restarted.\n", + Connection); + } + + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SI; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfDereferenceConnection ("Timeout", Connection, CREF_LINK); + (VOID)NbfDisconnectFromLink (Connection, FALSE); + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, link down.\n", + Connection); + } + + } + + + NbfDereferenceConnection("Listen Timeout", Connection, CREF_TIMER); + + LEAVE_NBF; + return; + +} /* ListenTimeout */ + + +NTSTATUS +ProcessAddGroupNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_GROUP_NAME_QUERY frame. Because + our caller has already verified that the destination name in the frame + matches the transport address passed to us, we must simply transmit an + ADD_NAME_RESPONSE frame and exit with STATUS_ABANDONED. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. + When we return any other status code, including STATUS_ABANDONED, the + caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_UI_FRAME RawFrame; // ptr to allocated connectionless frame. + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + UNREFERENCED_PARAMETER (SourceAddress); + UNREFERENCED_PARAMETER (Address); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessAddGroupNameQuery %lx: [%.16s].\n", Address, Header->DestinationName); + } + + // + // Allocate a UI frame from the pool. + // + + if (NbfCreateConnectionlessFrame (DeviceContext, &RawFrame) != STATUS_SUCCESS) { + return STATUS_ABANDONED; // no resources to do this. + } + + + // + // Build the MAC header. ADD_NAME_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructAddNameResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // type of name is UNIQUE. + RESPONSE_CORR(Header), // correlator from rec'd frame. + (PUCHAR)Header->SourceName); // NetBIOS name being responded to. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback. + + return STATUS_ABANDONED; // don't forward frame to other addr's. +} /* ProcessAddGroupNameQuery */ + + +NTSTATUS +ProcessAddNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_NAME_QUERY frame. Because + our caller has already verified that the destination name in the frame + matches the transport address passed to us, we must simply transmit an + ADD_NAME_RESPONSE frame and exit with STATUS_ABANDONED. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_UI_FRAME RawFrame; // ptr to allocated connectionless frame. + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + Address, SourceAddress; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessAddNameQuery %lx: [%.16s].\n", Address, Header->DestinationName); + } + + // + // Allocate a UI frame from the pool. + // + + if (NbfCreateConnectionlessFrame (DeviceContext, &RawFrame) != STATUS_SUCCESS) { + return STATUS_ABANDONED; // no resources to do this. + } + + + // + // Build the MAC header. ADD_NAME_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructAddNameResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // type of name is UNIQUE. + RESPONSE_CORR(Header), // correlator from received frame. + (PUCHAR)Header->SourceName); // NetBIOS name being responded to + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback. + + return STATUS_ABANDONED; // don't forward frame to other addr's. +} /* ProcessAddNameQuery */ + + +NTSTATUS +ProcessNameInConflict( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_IN_CONFLICT frame. + Although we can't disrupt any traffic on this address, it is considered + invalid and cannot be used for any new address files or new connections. + Therefore, we just mark the address as invalid. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + DeviceContext, Header, SourceAddress; // prevent compiler warnings + + + // + // Ignore this if we are registering/deregistering (the name will + // go away anyway) or if we have already marked this name as + // in conflict and logged an error. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameInConflict %lx: address marked [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameInConflict %lx: [%.16s].\n", Address, Header->SourceName); + } + +#if 0 + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + DbgPrint ("NBF: Name-in-conflict on <%.16s> from ", Header->DestinationName); + DbgPrint ("%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + SourceAddress->Address[0], + SourceAddress->Address[1], + SourceAddress->Address[2], + SourceAddress->Address[3], + SourceAddress->Address[4], + SourceAddress->Address[5]); +#endif + + NbfWriteGeneralErrorLog( + Address->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 2, + STATUS_DUPLICATE_NAME, + L"NAME_IN_CONFLICT", + 16/sizeof(ULONG), + (PULONG)(Header->DestinationName)); + + return STATUS_ABANDONED; + +} /* ProcessNameInConflict */ + + +NTSTATUS +NbfIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Dsdu, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine processes an incoming DATAGRAM or DATAGRAM_BROADCAST frame. + BROADCAST and normal datagrams have the same receive logic, except + for broadcast datagrams Address will be the broadcast address. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Dsdu - Pointer to a Mdl buffer that contains the received datagram. + The first byte of information in the buffer is the first byte in + the NetBIOS connectionless header, and it is already negotiated that + the data link layer will provide at least the entire NetBIOS header + as contiguous data. + + Length - The length of the MDL pointed to by Dsdu. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PLIST_ENTRY p, q; + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG IndicateBytesCopied, MdlBytesCopied, BytesToCopy; + TA_NETBIOS_ADDRESS SourceName; + TA_NETBIOS_ADDRESS DestinationName; + PTDI_CONNECTION_INFORMATION remoteInformation; + ULONG returnLength; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PNBF_HDR_CONNECTIONLESS Header = (PNBF_HDR_CONNECTIONLESS)Dsdu; + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Entered.\n"); + } + + // + // If this datagram wasn't big enough for a transport header, then don't + // let the caller look at any data. + // + + if (Length < sizeof(NBF_HDR_CONNECTIONLESS)) { + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Short datagram abandoned.\n"); + } + return STATUS_ABANDONED; + } + + // + // Update our statistics. + // + + ++DeviceContext->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DatagramBytesReceived, + Length - sizeof(NBF_HDR_CONNECTIONLESS)); + + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress ((PUCHAR)Header->SourceName, FALSE, &SourceName); + TdiBuildNetbiosAddress ((PUCHAR)Header->DestinationName, FALSE, &DestinationName); + + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Find the first open address file in the list. + // + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = addressFile->ReceiveDatagramQueue.Flink; + q != &addressFile->ReceiveDatagramQueue; + q = q->Flink) { + + irp = CONTAINING_RECORD (q, IRP, Tail.Overlay.ListEntry); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + &((IoGetCurrentIrpStackLocation(irp))-> + Parameters))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbfParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + Header->SourceName, + DatagramAddress->NetbiosName, + NETBIOS_NAME_LENGTH))) { + continue; + } + break; + } + + if (q != &addressFile->ReceiveDatagramQueue) { + KIRQL cancelIrql; + + + RemoveEntryList (q); + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Receive datagram request found, copying.\n"); + } + + // + // Copy the actual user data. + // + + MdlBytesCopied = 0; + + BytesToCopy = Length - sizeof(NBF_HDR_CONNECTIONLESS); + + if (BytesToCopy > 0) { + status = TdiCopyBufferToMdl ( + Dsdu, + sizeof(NBF_HDR_CONNECTIONLESS), // offset + BytesToCopy, // length + irp->MdlAddress, + 0, + &MdlBytesCopied); + } else { + status = STATUS_SUCCESS; + } + + // + // Copy the addressing information. + // + + irpSp = IoGetCurrentIrpStackLocation (irp); + remoteInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))-> + ReturnDatagramInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + sizeof (TA_NETBIOS_ADDRESS)); + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + remoteInformation->RemoteAddressLength); + + returnLength = remoteInformation->RemoteAddressLength; + remoteInformation->RemoteAddressLength = returnLength; + + } + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Receive DG done", Address, AREF_REQUEST); + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // no receive datagram requests; is there a kernel client? + // + + if (addressFile->RegisteredReceiveDatagramHandler) { + + IndicateBytesCopied = 0; + + status = (*addressFile->ReceiveDatagramHandler)( + addressFile->ReceiveDatagramHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + Length - sizeof(NBF_HDR_CONNECTIONLESS), // indicated + Length - sizeof(NBF_HDR_CONNECTIONLESS), // available + &IndicateBytesCopied, + Dsdu + sizeof(NBF_HDR_CONNECTIONLESS), + &irp); + + if (status == STATUS_SUCCESS) { + + // + // The client accepted the datagram and so we're done. + // + + } else if (status == STATUS_DATA_NOT_ACCEPTED) { + + // + // The client did not accept the datagram and we need to satisfy + // a TdiReceiveDatagram, if possible. + // + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Picking off a rcv datagram request from this address.\n"); + } + status = STATUS_MORE_PROCESSING_REQUIRED; + + } else if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client returned an IRP that we should queue up to the + // address to satisfy the request. + // + + irp->IoStatus.Status = STATUS_PENDING; // init status information. + irp->IoStatus.Information = 0; + irpSp = IoGetCurrentIrpStackLocation (irp); // get current stack loctn. + if ((irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) || + (irpSp->MinorFunction != TDI_RECEIVE_DATAGRAM)) { + irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + return status; + } + + // + // Now copy the actual user data. + // + + MdlBytesCopied = 0; + + BytesToCopy = Length - sizeof(NBF_HDR_CONNECTIONLESS) - IndicateBytesCopied; + + if (BytesToCopy > 0) { + status = TdiCopyBufferToMdl ( + Dsdu, + sizeof(NBF_HDR_CONNECTIONLESS) + IndicateBytesCopied, + BytesToCopy, + irp->MdlAddress, + 0, + &MdlBytesCopied); + } else { + status = STATUS_SUCCESS; + } + + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = status; + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + } + } + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now dereference the previous address file with + // the lock released. + // + + NbfDereferenceAddressFile (prevaddressFile); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + } // end of while loop + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + return status; // to dispatcher. +} /* NbfIndicateDatagram */ + + +NTSTATUS +ProcessNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_QUERY frame. There are two + types of NAME_QUERY frames, with basically the same layout. If the + session number in the frame is 0, then the frame is really a request + for information about the name, and not a request to establish a + session. If the session number is non-zero, then the frame is a + connection request that we use to satisfy a listen. + + With the new version of TDI, we now indicate the user that a request + for connection has been received, iff there is no outstanding listen. + If this does occur, the user can return a connection that is to be used + to accept the connection on. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_UI_FRAME RawFrame; + PTP_CONNECTION Connection; + PTP_LINK Link; + UCHAR NameType; + BOOLEAN ConnectIndicationBlocked = FALSE; + PLIST_ENTRY p; + UINT HeaderLength; + PUCHAR GeneralSR; + UINT GeneralSRLength; + BOOLEAN UsedListeningConnection = FALSE; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PIRP acceptIrp; + + CONNECTION_CONTEXT connectionContext; + TA_NETBIOS_ADDRESS RemoteAddress; + + // + // If we are just registering or deregistering this address, then don't + // allow state changes. Just throw the packet away, and let the frame + // distributor try the next address. + // + // Also drop it if the address is in conflict. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: address not stable [%.16s].\n", Address, Header->SourceName); + } + return STATUS_SUCCESS; + } + + // + // Process this differently depending on whether it is a find name + // request or an incoming connection. + // + + if (Header->Data2Low == 0) { + + // + // This is a find-name request. Respond with a NAME_RECOGNIZED frame. + // + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: find name [%.16s].\n", Address, Header->SourceName); + } + + NbfSendNameRecognized( + Address, + 0, // LSN 0 == FIND_NAME response + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + return STATUS_ABANDONED; // don't allow multiple responses. + + } else { // (if Data2Low is non-zero) + + // + // This is an incoming connection request. If we have a listening + // connection on this address, then continue with the connection setup. + // If there is no outstanding listen, then indicate any kernel mode + // clients that want to know about this frame. If a listen was posted, + // then a connection has already been set up for it. The LSN field of + // the connection is set to 0, so we look for the first 0 LSN in the + // database. + // + + // + // First, check if we already have an active connection with + // this remote on this address. If so, we resend the NAME_RECOGNIZED + // if we have not yet received the SESSION_INITIALIZE; otherwise + // we ignore the frame. + // + + // + // If successful this adds a reference of type CREF_LISTENING. + // + + if (Connection = NbfLookupRemoteName(Address, (PUCHAR)Header->SourceName, Header->Data2Low)) { + + // + // We have an active connection on this guy, see if he + // still appears to be waiting to a NAME_RECOGNIZED. + // + + if (((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) && + (Connection->Link != (PTP_LINK)NULL) && + (Connection->Link->State == LINK_STATE_ADM)) { + + // + // Yes, he must have dropped a previous NAME_RECOGNIZED + // so we send another one. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2("Dup NAME_QUERY found: %lx [%.16s]\n", Connection, Header->SourceName); + } + + NbfSendNameRecognized( + Address, + Connection->Lsn, + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2("Dup NAME_QUERY ignored: %lx [%.16s]\n", Connection, Header->SourceName); + } + + } + + NbfDereferenceConnection ("Lookup done", Connection, CREF_LISTENING); + + return STATUS_ABANDONED; + + } + + // If successful, this adds a reference which is removed before + // this function returns. + + Connection = NbfLookupListeningConnection (Address, (PUCHAR)Header->SourceName); + if (Connection == NULL) { + + // + // not having a listening connection is not reason to bail out here. + // we need to indicate to the user that a connect attempt occurred, + // and see if there is a desire to use this connection. We + // indicate in order to all address files that are + // using this address. + // + // If we already have an indication pending on this address, + // we ignore this frame (the NAME_QUERY may have come from + // a different address, but we can't know that). Also, if + // there is already an active connection on this remote + // name, then we ignore the frame. + // + + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if ((addressFile->RegisteredConnectionHandler == TRUE) && + (!addressFile->ConnectIndicationInProgress)) { + + + TdiBuildNetbiosAddress ( + (PUCHAR)Header->SourceName, + FALSE, + &RemoteAddress); + + addressFile->ConnectIndicationInProgress = TRUE; + + // + // we have a connection handler, now indicate that a connection + // attempt occurred. + // + + status = (addressFile->ConnectionHandler)( + addressFile->ConnectionHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &RemoteAddress, + 0, + NULL, + 0, + NULL, + &connectionContext, + &acceptIrp); + + if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // the user has connected a currently open connection, but + // we have to figure out which one it is. + // + + // + // If successful this adds a reference of type LISTENING + // (the same what NbfLookupListeningConnection adds). + // + + Connection = NbfLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + NbfPrint0("STATUS_MORE_PROCESSING, connection not found\n"); + addressFile->ConnectIndicationInProgress = FALSE; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + + } else { + + if (Connection->AddressFile->Address != Address) { + addressFile->ConnectIndicationInProgress = FALSE; + + NbfPrint0("STATUS_MORE_PROCESSING, address wrong\n"); + NbfStopConnection (Connection, STATUS_INVALID_ADDRESS); + NbfDereferenceConnection("Bad Address", Connection, CREF_LISTENING); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_ADDRESS; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + // + // OK, we have a valid connection. If the response to + // this connection was disconnect, we need to reject + // the connection request and return. If it was accept + // or not specified (to be done later), we simply + // fall through and continue processing on the U Frame. + // + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + +// Connection->Flags2 &= ~CONNECTION_FLAGS2_DISCONNECT; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfPrint0("STATUS_MORE_PROCESSING, disconnect\n"); + addressFile->ConnectIndicationInProgress = FALSE; + NbfDereferenceConnection("Disconnecting", Connection, CREF_LISTENING); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + } + + // + // Make a note that we have to set + // addressFile->ConnectIndicationInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint4 ("ProcessNameQuery %lx: indicate DONE, context %lx conn %lx [%.16s].\n", Address, connectionContext, Connection, Header->SourceName); + } + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint6 ("Link is %x-%x-%x-%x-%x-%x\n", + SourceAddress->Address[0], + SourceAddress->Address[1], + SourceAddress->Address[2], + SourceAddress->Address[3], + SourceAddress->Address[4], + SourceAddress->Address[5]); + } + + // + // Set up our flags...we turn on REQ_COMPLETED + // so that disconnect will be indicated if the + // connection goes down before a session init + // is received. + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_STOPPING; + Connection->Status = STATUS_PENDING; + Connection->Flags2 |= (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_REQ_COMPLETED); + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + ConnectIndicationBlocked = TRUE; + NbfDereferenceAddressFile (addressFile); + acceptIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + break; // exit the while + +#if 0 + } else if (status == STATUS_EVENT_PENDING) { + + // + // user has returned a connectionContext, use that for further + // processing of the connection. First validate it so + // we can know we won't just start a connection and never + // finish. + // + // + // If successful this adds a reference of type LISTENING + // (the same what NbfLookupListeningConnection adds). + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameQuery %lx: indicate PENDING, context %lx [%.16s].\n", Address, connectionContext, Header->SourceName); + } + + + Connection = NbfLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + NbfPrint0("STATUS_MORE_PROCESSING, but connection not found\n"); + addressFile->ConnectIndicationInProgress = FALSE; + + goto whileend; // try next address file. + + } else { + + if (Connection->AddressFile->Address != Address) { + addressFile->ConnectIndicationInProgress = FALSE; + NbfStopConnection (Connection, STATUS_INVALID_ADDRESS); + NbfDereferenceConnection("Bad Address", Connection, CREF_LISTENING); + Connection = NULL; + + goto whileend; // try next address file. + } + + } + + // + // Make a note that we have to set + // addressFile->ConnectionIndicatInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + ConnectIndicationBlocked = TRUE; + NbfDereferenceAddressFile (addressFile); + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + break; // exit the while +#endif + + } else if (status == STATUS_INSUFFICIENT_RESOURCES) { + + // + // we know the address, but can't create a connection to + // use on it. This gets passed to the network as a response + // saying I'm here, but can't help. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: indicate RESOURCES [%.16s].\n", Address, Header->SourceName); + } + + addressFile->ConnectIndicationInProgress = FALSE; + + // + // We should send a NR with LSN 0xff, indicating + // no resources, but LM 2.0 does not interpret + // that correctly. So, we send LSN 0 (no listens) + // instead. + // + + NbfSendNameRecognized( + Address, + 0, + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + NbfDereferenceAddressFile (addressFile); + return STATUS_ABANDONED; + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: indicate invalid [%.16s].\n", Address, Header->SourceName); + } + + addressFile->ConnectIndicationInProgress = FALSE; + + goto whileend; // try next address file + + } // end status ifs + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: no handler [%.16s].\n", Address, Header->SourceName); + } + + goto whileend; // try next address file + + } // end no indication handler + +whileend: + // + // Jumping here is like a continue, except that the + // addressFile pointer is advanced correctly. + // + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now dereference the previous address file with + // the lock released. + // + + NbfDereferenceAddressFile (prevaddressFile); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + } // end of loop through the address files. + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (Connection == NULL) { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: no connection [%.16s].\n", Address, Header->SourceName); + } + + // + // We still did not find a connection after looping + // through the address files. + // + + NbfSendNameRecognized( + Address, + 0, // LSN 0 == No listens + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + // + // We used to return MORE_PROCESSING_REQUIRED, but + // since we matched with this address, no other + // address is going to match, so abandon it. + // + + return STATUS_ABANDONED; + + } + + } else { // end connection == null + + UsedListeningConnection = TRUE; + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameQuery %lx: found listen %lx: [%.16s].\n", Address, Connection, Header->SourceName); + } + + } + + + // + // At this point the connection has a reference of type + // CREF_LISTENING. Allocate a UI frame from the pool. + // + + status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (status)) { // no resources to respond. + PANIC ("ProcessNameQuery: Can't get UI Frame, dropping query\n"); + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + if (UsedListeningConnection) { + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; + } else { + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + } + NbfDereferenceConnection("Can't get UI Frame", Connection, CREF_LISTENING); + return STATUS_ABANDONED; + } + + // + // Build the MAC header. NAME_RECOGNIZED frames go out as + // general-route source routing. + // + + MacReturnGeneralRouteSR( + &DeviceContext->MacInfo, + &GeneralSR, + &GeneralSRLength); + + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + GeneralSR, + GeneralSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Before we continue, store the remote guy's transport address + // into the TdiListen's TRANSPORT_CONNECTION buffer. This allows + // the client to determine who called him. + // + + Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + TdiCopyLookaheadData( + Connection->CalledAddress.NetbiosName, + Header->SourceName, + 16, + DeviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + RtlCopyMemory( Connection->RemoteName, Connection->CalledAddress.NetbiosName, 16 ); + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + + // + // Now formulate a reply. + // + + NameType = (UCHAR)((Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + // + // We have a listening connection on the address now. Create a link + // for it to be associated with and make that link ready. Respond to + // the sender with a name_recognized frame. then we will receive our + // first connection-oriented frame, SESSION_INITIALIZE, handled + // in IFRAMES.C. Then we respond with SESSION_CONFIRM, and then + // the TdiListen completes. + // + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink. It does NOT add a link reference. + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + LISTENER_LINK, // for loopback link + &Link); // resulting link. + + if (NT_SUCCESS (status)) { // link established. + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference + // which is removed in NbfDisconnectFromLink + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) && + ((status = NbfConnectToLink (Link, Connection)) == STATUS_SUCCESS)) { + + Connection->Flags |= CONNECTION_FLAGS_WAIT_SI; // wait for SI. + Connection->Retries = 1; + Connection->Rsn = Header->Data2Low; // save remote LSN. + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfWaitLink (Link); // start link going. + + ConstructNameRecognized ( // build a good response. + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of local name. + Connection->Lsn, // return our LSN. + RESPONSE_CORR(Header), // new xmit corr. + 0, // our response correlator (unused). + Header->DestinationName,// our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + // + // Now, to avoid problems with hanging listens, we'll start the + // connection timer and give a limited period for the connection + // to succeed. This avoids waiting forever for those first few + // frames to be exchanged. When the timeout occurs, the + // the dereference will cause the circuit to be torn down. + // + // The maximum delay we can accomodate on a link is + // NameQueryRetries * NameQueryTimeout (assuming the + // remote has the same timeous). There are three + // exchanges of packets until the SESSION_INITIALIZE + // shows up, to be safe we multiply by four. + // + + NbfStartConnectionTimer( + Connection, + NbfListenTimeout, + 4 * DeviceContext->NameQueryRetries * DeviceContext->NameQueryTimeout); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + TRUE); // loopback if needed. + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint2("Connection %lx on link %lx\n", Connection, Link); + } + + NbfDereferenceConnection("ProcessNameQuery", Connection, CREF_LISTENING); + return STATUS_ABANDONED; // successful! + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // We don't have a free LSN to allocate, so fall through to + // report "no resources". + // + + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("No more LSNS", Link, LREF_CONNECTION); + + ASSERT (Connection->Lsn == 0); + + } + + // + // If we fall through here, then we couldn't get resources to set + // up this connection, so just send him a "no resources" reply. + // + + if (UsedListeningConnection) { + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; // put this back. + + } else { + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + + } + + // + // We should send a NR with LSN 0xff, indicating + // no resources, but LM 2.0 does not interpret + // that correctly. So, we send LSN 0 (no listens) + // instead. + // + + ConstructNameRecognized ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, + 0, // LSN=0 means no listens + RESPONSE_CORR(Header), + 0, + Header->DestinationName, // our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + TRUE); // loopback if needed. + + NbfDereferenceConnection("ProcessNameQuery done", Connection, CREF_LISTENING); + } + + return STATUS_ABANDONED; + +} /* ProcessNameQuery */ + + +NTSTATUS +ProcessAddNameResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_NAME_RESPONSE frame. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN SendNameInConflict = FALSE; + UNREFERENCED_PARAMETER(DeviceContext); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // If we aren't trying to register this address, then the sender of + // this frame is bogus. We cannot allow our state to change based + // on the reception of a random frame. + // + + if (!(Address->Flags & ADDRESS_FLAGS_REGISTERING)) { + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint2("ProcessAddNameResponse %lx: not registering [%.16s]\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; // just destroy the packet. + } + + // + // Unfortunately, we are registering this address and another host + // on the network is also attempting to register the same NetBIOS + // name on the same network. Because he got to us first, we cannot + // register our name. Thus, the address must die. We set this flag + // and on the next timeout we will shut down. + // + + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + + if (Header->Data2Low == NETBIOS_NAME_TYPE_UNIQUE) { + + // + // If we have already gotten a response from someone saying + // this address is uniquely owned, then make sure any future + // responses come from the same MAC address. + // + + if ((*((LONG UNALIGNED *)Address->UniqueResponseAddress) == 0) && + (*((SHORT UNALIGNED *)(&Address->UniqueResponseAddress[4])) == 0)) { + + RtlMoveMemory(Address->UniqueResponseAddress, SourceAddress->Address, 6); + + } else if (!RtlEqualMemory( + Address->UniqueResponseAddress, + SourceAddress->Address, + 6)) { + + if (!Address->NameInConflictSent) { + SendNameInConflict = TRUE; + } + + } + + } else { + + // + // For group names, make sure nobody else decided that it was + // a unique address. + // + + if ((*((LONG UNALIGNED *)Address->UniqueResponseAddress) != 0) || + (*((SHORT UNALIGNED *)(&Address->UniqueResponseAddress[4])) != 0)) { + + if (!Address->NameInConflictSent) { + SendNameInConflict = TRUE; + } + + } + + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (SendNameInConflict) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->DestinationName); + + } + + + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint2("ProcessAddNameResponse %lx: stopping [%.16s]\n", Address, Header->SourceName); + } + + return STATUS_ABANDONED; // done with this frame. +} /* ProcessAddNameResponse */ + + +NTSTATUS +ProcessNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_RECOGNIZED frame. This frame + is received because we issued a NAME_QUERY frame to actively initiate + a connection with a remote host. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION Connection; + PTP_LINK Link; + BOOLEAN TimerCancelled; + + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameRecognized %lx: address not stable [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; // invalid address state, drop packet. + } + + // + // Find names and connections both require a TP_CONNECTION to work. + // In either case, the ConnectionId field of the TP_CONNECTION object + // was sent as the response correlator in the NAME_QUERY frame, so + // we should get the same correlator back in this frame in the + // transmit correlator. Because this number is unique across + // all the connections on an address, we can determine if the frame + // was for this address or not. + // + + // this causes a reference which is removed before this function returns. + + Connection = NbfLookupConnectionById ( + Address, + TRANSMIT_CORR(Header)); + + // + // has he been deleted while we were waiting? + // + + if (Connection == NULL) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameRecognized %lx: no connection [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; + } + + // + // This frame is a response to a NAME_QUERY frame that we previously + // sent to him. Either he's returning "insufficient resources", + // indicating that a session cannot be established, or he's initiated + // his side of the connection. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + + // + // Connection is stopping, don't process this. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx stopping [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection("Name Recognized, stopping", Connection, CREF_BY_ID); + + return STATUS_ABANDONED; + } + + if (Header->Data2Low == 0x00 || + (Header->Data2Low > 0x00 && (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN))) { // no listens, or FIND.NAME response. + + if (!(Connection->Flags2 & CONNECTION_FLAGS2_CONNECTOR)) { + + // + // This is just a find name request, we are not trying to + // establish a connection. Currently, there is no reason + // for this to occur, so just save some room to add this + // extra feature later to support NETBIOS find name. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx not connector [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection("Unexpected FN Response", Connection, CREF_BY_ID); + return STATUS_ABANDONED; // we processed the frame. + } + + // + // We're setting up a session. If we are waiting for the first NAME + // RECOGNIZED, then setup the link and send the second NAME_QUERY. + // If we're waiting for the second NAME_RECOGNIZED, then he didn't + // have an LSN to finish the connection, so tear it down. + // + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN) { + + // + // Now that we know the data link address of the remote host + // we're connecting to, we need to create a TP_LINK object to + // represent the data link between these two machines. If there + // is already a data link there, then the object will be reused. + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NR_FN; + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // The Netbios address we are connecting to is a + // unique name + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx send 2nd NQ [%.16s].\n", Address, Connection, Header->SourceName); + } + + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + CONNECTOR_LINK, // for loopback link + &Link); // resulting link. + + if (!NT_SUCCESS (status)) { // no resources. + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("No Resources for link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference which is + // removed in NbfDisconnectFromLink. It does NOT add a link ref. + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((status = NbfConnectToLink (Link, Connection)) != STATUS_SUCCESS)) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // Connection stopping or no LSN's available on this link. + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("Can't connect to link", Link, LREF_CONNECTION); // most likely destroys this. + + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("Cant connect to link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + (VOID)InterlockedIncrement(&Link->NumberOfConnectors); + + } else { + + // + // We are connecting to a group name; we have to + // assign an LSN now, but we don't connect to + // the link until we get a committed name response. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_GROUP_LSN; + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx send 2nd NQ GROUP [%.16s].\n", Address, Connection, Header->SourceName); + } + + if (NbfAssignGroupLsn(Connection) != STATUS_SUCCESS) { + + // + // Could not find an empty LSN; have to fail. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection("Can't get group LSN", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + + } + + } + + + // + // Send the second NAME_QUERY frame, committing our LSN to + // the remote guy. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NR; + Connection->Retries = (USHORT)DeviceContext->NameQueryRetries; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + DeviceContext->NameQueryTimeout); + + KeQueryTickCount (&Connection->ConnectStartTime); + + NbfSendNameQuery( + Connection, + TRUE); + + NbfDereferenceConnection ("Done with lookup", Connection, CREF_BY_ID); // release lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + if (Connection->Link) { + + if (RtlEqualMemory( + Connection->Link->HardwareAddress.Address, + SourceAddress->Address, + 6)) { + + // + // Unfortunately, he's telling us that he doesn't have resources + // to allocate an LSN. We set a flag to record this and + // ignore the frame. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_NO_LISTEN; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx no listens [%.16s].\n", Address, Connection, Header->SourceName); + } + + } else { + + // + // This response comes from a different remote from the + // last one. For unique names this indicates a duplicate + // name on the network. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + if (!Address->NameInConflictSent) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->SourceName); + + } + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx name in conflict [%.16s].\n", Address, Connection, Header->SourceName); + } + + } + + } else { + + // + // The response came back so fast the connection is + // not stable, ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + NbfDereferenceConnection ("No remote resources", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else { + + // + // Strange state. This should never happen, because we should be + // either waiting for a first or second name recognized frame. It + // is possible that the remote station received two frames because + // of our retransmits, and so he responded to both. Toss the frame. + // + + if (Connection->Link) { + + if (!RtlEqualMemory( + Connection->Link->HardwareAddress.Address, + SourceAddress->Address, + 6)) { + + // + // This response comes from a different remote from the + // last one. For unique names this indicates a duplicate + // name on the network. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + if (!Address->NameInConflictSent) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->SourceName); + + } + + } + + } else { + + // + // This is the same remote, just ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + } else { + + // + // The response came back so fast the connection is + // not stable, ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + } else if (Header->Data2Low == 0xff) { // no resources to complete connection. + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + // + // The recipient of our previously-sent NAME_QUERY frame that we sent + // to actively establish a connection has unfortunately run out of + // resources and cannot setup his side of the connection. We have to + // report "no resources" on the TdiConnect. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx no resources [%.16s].\n", Address, Connection, Header->SourceName); + } + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ProcessNameRecognized: No resources.\n"); + } + + NbfStopConnection (Connection, STATUS_REMOTE_RESOURCES); + NbfDereferenceConnection ("No Resources", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else { + + // + // We don't have a committed NAME_QUERY out there, so + // we ignore this frame. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected no resources [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + } else { // Data2Low is in the range 0x01-0xfe + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + // + // This is a successful response to a second NAME_QUERY we sent when + // we started processing a TdiConnect request. Clear the "waiting + // for Name Recognized" bit in the connection flags so that the + // connection timer doesn't blow us away when it times out. + // + // BUGBUG: What prevents the timeout routine from running while + // we're in here and destroying the connection/link by + // calling NbfStopConnection? + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NR; + + // + // Before we continue, store the remote guy's transport address + // into the TdiConnect's TRANSPORT_CONNECTION buffer. This allows + // the client to determine who responded to his TdiConnect. + // + // BUGBUG: this used to be done prior to sending the second + // Name Query, but since I fixed the Buffer2 problem, meaning + // that I really do overwrite the input buffer with the + // output buffer, that was screwing up the second query. + // Note that doing the copy after sending is probably unsafe + // in the case where the second Name Recognized arrives + // right away. + // + + Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + TdiCopyLookaheadData( + Connection->CalledAddress.NetbiosName, + Header->SourceName, + 16, + DeviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + RtlCopyMemory( Connection->RemoteName, Header->SourceName, 16 ); + + Connection->Rsn = Header->Data2Low; // save his remote LSN. + + // + // Save the correlator from the NR for eventual use in the + // SESSION_INITIALIZE frame. + // + + Connection->NetbiosHeader.TransmitCorrelator = RESPONSE_CORR(Header); + + // + // Cancel the timer; it would have no effect since WAIT_NR + // is not set, but there is no need for it to run. We cancel + // it with the lock held so it won't interfere with the + // timer's use when a connection is closing. + // + + TimerCancelled = KeCancelTimer (&Connection->Timer); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) != 0) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // The Netbios address we are connecting to is a + // group name; we need to connect to the link + // now that we have the committed session. + // + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + CONNECTOR_LINK, // for loopback link + &Link); // resulting link. + + if (!NT_SUCCESS (status)) { // no resources. + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("No Resources for link", Connection, CREF_BY_ID); + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + return STATUS_ABANDONED; + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference which is + // removed in NbfDisconnectFromLink. It does NOT add a link ref. + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((status = NbfConnectToLink (Link, Connection)) != STATUS_SUCCESS)) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + // Connection stopping or no LSN's available on this link. + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("Can't connect to link", Link, LREF_CONNECTION); // most likely destroys this. + + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("Cant connect to link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + (VOID)InterlockedIncrement(&Link->NumberOfConnectors); + + } else { + + // + // It's to a unique address, we set up the link + // before we sent out the committed NAME_QUERY. + // + + Link = Connection->Link; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx session up! [%.16s].\n", Address, Connection, Header->SourceName); + } + + // + // When we sent the committed NAME_QUERY, we stored that + // time in Connection->ConnectStartTime; we can now use + // that for a rough estimate of the link delay, if this + // is the first connection on the link. For async lines + // we do not do this because the delay introduced by the + // gateway messes up the timing. + // + + if (!DeviceContext->MacInfo.MediumAsync) { + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if (Link->State == LINK_STATE_ADM) { + + // + // HACK: Set the necessary variables in the link + // so that FakeUpdateBaseT1Timeout works. These + // variables are the same ones that FakeStartT1 sets. + // + + Link->CurrentPollSize = Link->HeaderLength + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + Link->CurrentTimerStart = Connection->ConnectStartTime; + FakeUpdateBaseT1Timeout (Link); + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + NbfActivateLink (Connection->Link); // start link going. + + // + // We'll get control again in LINK.C when the data link has either + // been established, denied, or destroyed. This happens at I/O + // completion time from NbfCreateLink's PdiConnect request. + // + + } else { + + // + // We don't have a committed NAME_QUERY out there, so + // we ignore this frame. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected session up! [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + + } + + NbfDereferenceConnection("ProcessNameRecognized lookup", Connection, CREF_BY_ID); + return STATUS_ABANDONED; // don't distribute packet. +} /* ProcessNameRecognized */ + + +NTSTATUS +NbfProcessUi( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR Header, + IN PUCHAR DlcHeader, + IN ULONG DlcLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ) + +/*++ + +Routine Description: + + This routine receives control from the data link provider as an + indication that a DLC UI-frame has been received on the data link. + Here we dispatch to the correct UI-frame handler. + + Part of this routine's job is to optionally distribute the frame to + every address that needs to look at it. + We accomplish this by lock-stepping through the address database, + and for each address that matches the address this frame is aimed at, + calling the frame handler. + +Arguments: + + DeviceContext - Pointer to our device context. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + Header - Points to the MAC header of the incoming packet. + + DlcHeader - Points to the DLC header of the incoming packet. + + DlcLength - Actual length in bytes of the packet, starting at the + DlcHeader. + + SourceRouting - Source routing information in the MAC header. + + SourceRoutingLength - The length of SourceRouting. + + DatagramAddress - If this function returns STATUS_MORE_PROCESSING_ + REQUIRED, this will be the address the datagram should be + indicated to. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS Address; + PNBF_HDR_CONNECTIONLESS UiFrame; + NTSTATUS status; + PLIST_ENTRY Flink; + UCHAR MatchType; + BOOLEAN MatchedAddress; + PUCHAR MatchName; + ULONG NetbiosLength = DlcLength - 3; + + UiFrame = (PNBF_HDR_CONNECTIONLESS)(DlcHeader + 3); + + // + // Verify that this frame is long enough to examine and that it + // has the proper signature. We can't test the signature as a + // 16-bit word as specified in the NetBIOS Formats and Protocols + // manual because this is processor-dependent. + // + + if ((NetbiosLength < sizeof (NBF_HDR_CONNECTIONLESS)) || + (HEADER_LENGTH(UiFrame) != sizeof (NBF_HDR_CONNECTIONLESS)) || + (HEADER_SIGNATURE(UiFrame) != NETBIOS_SIGNATURE)) { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUi: Bad size or NetBIOS signature.\n"); + } + return STATUS_ABANDONED; // frame too small or too large. + } + + // + // If this frame has a correlator with the high bit on, it was due + // to a FIND.NAME request; we don't handle those here since they + // are not per-address. + // + + if ((UiFrame->Command == NBF_CMD_NAME_RECOGNIZED) && + (TRANSMIT_CORR(UiFrame) & 0x8000)) { + + // + // Make sure the frame is sent to our reserved address; + // if not, drop it. + // + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return NbfProcessQueryNameRecognized( + DeviceContext, + Header, + UiFrame); + } else { + + return STATUS_ABANDONED; + + } + } + + // + // If this is a STATUS_RESPONSE, process that separately. + // + + if (UiFrame->Command == NBF_CMD_STATUS_RESPONSE) { + + // + // Make sure the frame is sent to our reserved address; + // if not, drop it. + // + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + return STATUS_ABANDONED; + + } + } + + // + // If this is a STATUS_QUERY, check if it is to our reserved + // address. If so, we process it. If not, we fall through to + // the normal checking. This ensures that queries to our + // reserved address are always processed, even if nobody + // has opened that address yet. + // + + if (UiFrame->Command == NBF_CMD_STATUS_QUERY) { + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return NbfProcessStatusQuery( + DeviceContext, + NULL, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + } + + } + + // + // We have a valid connectionless NetBIOS protocol frame that's not a + // datagram, so deliver it to every address which matches the destination + // name in the frame. Some frames + // (NAME_QUERY) cannot be delivered to multiple recipients. Therefore, + // if a frame handler returns STATUS_MORE_PROCESSING_REQUIRED, we continue + // through the remaining addresses. Otherwise simply get out and assume + // that the frame was eaten. Thus, STATUS_SUCCESS means that the handler + // ate the frame and that no other addresses can have it. + // + + // + // Determine what kind of lookup we want to do. + // + + switch (UiFrame->Command) { + + case NBF_CMD_NAME_QUERY: + case NBF_CMD_DATAGRAM: + case NBF_CMD_DATAGRAM_BROADCAST: + case NBF_CMD_ADD_NAME_QUERY: + case NBF_CMD_STATUS_QUERY: + case NBF_CMD_ADD_NAME_RESPONSE: + case NBF_CMD_NAME_RECOGNIZED: + + MatchType = NETBIOS_NAME_TYPE_EITHER; + break; + + case NBF_CMD_ADD_GROUP_NAME_QUERY: + case NBF_CMD_NAME_IN_CONFLICT: + + MatchType = NETBIOS_NAME_TYPE_UNIQUE; + break; + + default: + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("NbfProcessUi: Frame delivered; Unrecognized command %x.\n", + UiFrame->Command); + } + return STATUS_SUCCESS; + break; + + } + + if ((UiFrame->Command == NBF_CMD_ADD_GROUP_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_ADD_NAME_QUERY)) { + + MatchName = (PUCHAR)UiFrame->SourceName; + + } else if (UiFrame->Command == NBF_CMD_DATAGRAM_BROADCAST) { + + MatchName = NULL; + + } else { + + MatchName = (PUCHAR)UiFrame->DestinationName; + + } + + if (MatchName && DeviceContext->AddressCounts[MatchName[0]] == 0) { + status = STATUS_ABANDONED; + goto RasIndication; + } + + + MatchedAddress = FALSE; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (Flink = DeviceContext->AddressDatabase.Flink; + Flink != &DeviceContext->AddressDatabase; + Flink = Flink->Flink) { + + Address = CONTAINING_RECORD ( + Flink, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + if (NbfMatchNetbiosAddress (Address, + MatchType, + MatchName)) { + + NbfReferenceAddress ("UI Frame", Address, AREF_PROCESS_UI); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (MatchedAddress) { + + // + // If the datagram's destination name does not match the address's + // network name and TSAP components, then skip this address. Some + // frames have the source and destination names backwards for this + // algorithm, so we account for that here. Also, broadcast datagrams + // have no destination name in the frame, but get delivered to every + // address anyway. + // + +#if 0 + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + USHORT i; + NbfPrint0 ("NbfProcessUi: SourceName: "); + for (i=0;i<16;i++) { + NbfPrint1 ("%c",UiFrame->SourceName[i]); + } + NbfPrint0 (" Destination Name:"); + for (i=0;i<16;i++) { + NbfPrint1 ("%c",UiFrame->DestinationName[i]); + } + NbfPrint0 ("\n"); + } +#endif + + // + // Deliver the frame to the current address. + // + + switch (UiFrame->Command) { + + case NBF_CMD_NAME_QUERY: + + status = ProcessNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_DATAGRAM: + case NBF_CMD_DATAGRAM_BROADCAST: + + // + // Reference the datagram so it sticks around until the + // ReceiveComplete, when it is processed. + // + + if ((Address->Flags & ADDRESS_FLAGS_CONFLICT) == 0) { + NbfReferenceAddress ("Datagram indicated", Address, AREF_PROCESS_DATAGRAM); + *DatagramAddress = Address; + status = STATUS_MORE_PROCESSING_REQUIRED; + } else { + status = STATUS_ABANDONED; + } + break; + + case NBF_CMD_ADD_GROUP_NAME_QUERY: + + // + // did this frame originate with us? If so, we don't want to + // do any processing of it. + // + + if (RtlEqualMemory ( + SourceAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + if ((Address->Flags & ADDRESS_FLAGS_REGISTERING) != 0) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUI: loopback AddGroupNameQuery dropped\n"); + } + status = STATUS_ABANDONED; + break; + } + } + + status = ProcessAddGroupNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + break; + + case NBF_CMD_ADD_NAME_QUERY: + + // + // did this frame originate with us? If so, we don't want to + // do any processing of it. + // + + if (RtlEqualMemory ( + SourceAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + if ((Address->Flags & ADDRESS_FLAGS_REGISTERING) != 0) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUI: loopback AddNameQuery dropped\n"); + } + status = STATUS_ABANDONED; + break; + } + } + + status = ProcessAddNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + break; + + case NBF_CMD_NAME_IN_CONFLICT: + + status = ProcessNameInConflict ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_STATUS_QUERY: + + status = NbfProcessStatusQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_ADD_NAME_RESPONSE: + + status = ProcessAddNameResponse ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_NAME_RECOGNIZED: + + status = ProcessNameRecognized ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + default: + + ASSERT(FALSE); + + } /* switch on NetBIOS frame command code */ + + NbfDereferenceAddress ("Done", Address, AREF_PROCESS_UI); // done with previous address. + + } else { + + status = STATUS_ABANDONED; + + } + + +RasIndication:; + + // + // Let the RAS clients have a crack at this if they want + // + + if (DeviceContext->IndicationQueuesInUse) { + + // + // If RAS has datagram indications posted, and this is a + // datagram that nobody wanted, then receive it anyway. + // + + if ((UiFrame->Command == NBF_CMD_DATAGRAM) && + (status == STATUS_ABANDONED)) { + + *DatagramAddress = NULL; + status = STATUS_MORE_PROCESSING_REQUIRED; + + } else if ((UiFrame->Command == NBF_CMD_ADD_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_ADD_GROUP_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_NAME_QUERY)) { + + NbfActionQueryIndication( + DeviceContext, + UiFrame); + + } + } + + + return status; + +} /* NbfProcessUi */ + diff --git a/private/ntos/tdi/st/address.c b/private/ntos/tdi/st/address.c new file mode 100644 index 000000000..3a4434a95 --- /dev/null +++ b/private/ntos/tdi/st/address.c @@ -0,0 +1,2247 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the TP_ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +// +// Map all generic accesses to the same one. +// + +STATIC GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +VOID +StDestroyAddress( + IN PVOID Parameter + ); + + +NTSTATUS +StOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the ST transport. + + Irp - a pointer to the Irp used for the creation of the address. + + IrpSp - a pointer to the Irp stack location. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PST_NETBIOS_ADDRESS networkName; // Network name string. + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED *addressName; + TDI_ADDRESS_NETBIOS UNALIGNED *netbiosName; + ULONG DesiredShareAccess; + KIRQL oldirql; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // The network name is in the EA, passed in AssociatedIrp.SystemBuffer + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + if (ea == NULL) { + StPrint1("OpenAddress: IRP %lx has no EA\n", Irp); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + addressName = (PTA_ADDRESS)&name->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one. + // + + for (i=0;iTAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if (addressName->AddressLength != 0) { + netbiosName = (PTDI_ADDRESS_NETBIOS)&addressName->Address[0]; + networkName = (PST_NETBIOS_ADDRESS)ExAllocatePool ( + NonPagedPool, + sizeof (ST_NETBIOS_ADDRESS)); + if (networkName == NULL) { + PANIC ("StOpenAddress: PANIC! could not allocate networkName!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TA_NETBIOS_ADDRESS), 1); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get the name to local storage + // + + if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) || + (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; + } else { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + } + RtlCopyMemory (networkName->NetbiosName, netbiosName->NetbiosName, 16); + + found = TRUE; + } else { + networkName = NULL; + found = TRUE; + } + + break; + + } else { + + addressName = (PTA_ADDRESS)(addressName->Address + + addressName->AddressLength); + + } + + } + + if (!found) { + StPrint1("OpenAddress: IRP %lx has no NETBIOS address\n", Irp); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // get an address file structure to represent this address. + // + + status = StCreateAddressFile (DeviceContext, &addressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context AddressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&DeviceContext->AddressResource, TRUE); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + address = StLookupAddress (DeviceContext, networkName); + + if (address == NULL) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // This address doesn't exist. Create it, and start the process of + // registering it. + // + + status = StCreateAddress ( + DeviceContext, + networkName, + &address); + + if (NT_SUCCESS (status)) { + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped. BUGBUG: Need to synchronize Assign and Access). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + PagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &address->ShareAccess); + ExReleaseResource (&DeviceContext->AddressResource); + StDereferenceAddress ("Device context stopping", address); + StDereferenceAddressFile (addressFile); + return status; + + } + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (DeviceContext->State == DEVICECONTEXT_STATE_STOPPING) { + StDereferenceAddress ("Device context stopping", address); + StDereferenceAddressFile (addressFile); + status = STATUS_DEVICE_NOT_READY; + + } else { + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + addressFile->FileObject = IrpSp->FileObject; + addressFile->Irp = Irp; + addressFile->Address = address; + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + InsertTailList (&address->AddressFileDatabase, &addressFile->Linkage); + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + + // + // Begin address registration unless this is the broadcast + // address (which is a "fake" address with no corresponding + // Netbios address) or the reserved address, which we know + // is unique since it is based on the adapter address. + // + + if ((networkName != NULL) && + (!RtlEqualMemory (networkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + StRegisterAddress (address); // begin address registration. + status = STATUS_PENDING; + + } else { + + address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + // + + if (networkName != NULL) { + ExFreePool (networkName); + } + + StDereferenceAddressFile (addressFile); + + } + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + + if (AccessAllowed) { + + // + // Access was successful, make sure Status is right. + // + + status = STATUS_SUCCESS; + + // + // Check that the name is of the correct type (unique vs. group) + // We don't need to check this for the broadcast address. + // + + if (networkName != NULL) { + if (address->NetworkName->NetbiosNameType != + networkName->NetbiosNameType) { + + status = STATUS_DUPLICATE_NAME; + + } + } + + } + + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + StDereferenceAddressFile (addressFile); + + } else { + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->ShareAccess, + TRUE); + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + StDereferenceAddressFile (addressFile); + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // now, if the address registered, we simply return success after + // pointing the file object at the address file (which points to + // the address). If the address registration is pending, we mark + // the registration pending and let the registration completion + // routine complete the open. If the address is bad, we simply + // fail the open. + // + + if ((address->Flags & + (ADDRESS_FLAGS_CONFLICT | + ADDRESS_FLAGS_REGISTERING | + ADDRESS_FLAGS_DEREGISTERING | + ADDRESS_FLAGS_DUPLICATE_NAME | + ADDRESS_FLAGS_NEEDS_REG | + ADDRESS_FLAGS_STOPPING | + ADDRESS_FLAGS_BAD_ADDRESS | + ADDRESS_FLAGS_CLOSED)) == 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = NULL; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + addressFile->State = ADDRESSFILE_STATE_OPEN; + + StReferenceAddress("open ready", address); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + status = STATUS_SUCCESS; + + } else { + + // + // if the address is still registering, make the open pending. + // + + if ((address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_NEEDS_REG)) != 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = Irp; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + + StReferenceAddress("open registering", address); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + status = STATUS_PENDING; + + } else { + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + StDereferenceAddressFile (addressFile); + + status = STATUS_DRIVER_INTERNAL_ERROR; + + } + } + } + } + + // + // Remove the reference from StLookupAddress. + // + + StDereferenceAddress ("Done opening", address); + } + + return status; +} /* StOpenAddress */ + + +VOID +StAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport address. Some minimal + initialization is done on the address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. Returns NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_ADDRESS Address; + PSEND_PACKET_TAG SendTag; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate address: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 101); + *TransportAddress = NULL; + return; + } + + Address = (PTP_ADDRESS)ExAllocatePool (NonPagedPool, sizeof (TP_ADDRESS)); + if (Address == NULL) { + PANIC("ST: Could not allocate address: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 201); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address, sizeof(TP_ADDRESS)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS); + ++DeviceContext->AddressAllocated; + + StAllocateSendPacket (DeviceContext, &(Address->Packet)); + if (Address->Packet == NULL) { + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + --DeviceContext->PacketAllocated; // AllocatePacket added one + + // + // Need to modify the address packets to belong to + // the address, not the device context. + // + + SendTag = (PSEND_PACKET_TAG)(Address->Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_G_FRAME; + SendTag->Packet = Address->Packet; + SendTag->Owner = (PVOID)Address; + + + Address->Type = ST_ADDRESS_SIGNATURE; + Address->Size = sizeof (TP_ADDRESS); + + Address->Provider = DeviceContext; + KeInitializeSpinLock (&Address->SpinLock); + + InitializeListHead (&Address->ConnectionDatabase); + InitializeListHead (&Address->AddressFileDatabase); + InitializeListHead (&Address->SendDatagramQueue); + + // + // For each address, allocate a receive packet, a receive buffer, + // and a UI frame. + // + + StAddReceivePacket (DeviceContext); + StAddReceiveBuffer (DeviceContext); + + *TransportAddress = Address; + +} /* StAllocateAddress */ + + +VOID +StDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a transport address structure. + +Return Value: + + None. + +--*/ + +{ + + if (TransportAddress->NetworkName != NULL) { + ExFreePool (TransportAddress->NetworkName); + } + StDeallocateSendPacket (DeviceContext, TransportAddress->Packet); + ++DeviceContext->PacketAllocated; + + ExFreePool (TransportAddress); + --DeviceContext->AddressAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS); + + // + // Remove the resources which allocating this caused. + // + + StRemoveReceivePacket (DeviceContext); + StRemoveReceiveBuffer (DeviceContext); + +} /* StDeallocateAddress */ + + +NTSTATUS +StCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetworkName - Pointer to an ST_NETBIOS_ADDRESS type containing the network + name to be associated with this address, if any. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS pAddress; + PLIST_ENTRY p; + + + p = RemoveHeadList (&DeviceContext->AddressPool); + if (p == &DeviceContext->AddressPool) { + + if ((DeviceContext->AddressMaxAllocated == 0) || + (DeviceContext->AddressAllocated < DeviceContext->AddressMaxAllocated)) { + + StAllocateAddress (DeviceContext, &pAddress); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 401); + pAddress = NULL; + + } + + if (pAddress == NULL) { + ++DeviceContext->AddressExhausted; + PANIC ("StCreateConnection: Could not allocate address object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + pAddress = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + } + + ++DeviceContext->AddressInUse; + if (DeviceContext->AddressInUse > DeviceContext->AddressMaxInUse) { + ++DeviceContext->AddressMaxInUse; + } + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + + + // + // Initialize all of the static data for this address. + // + + pAddress->ReferenceCount = 1; + + pAddress->Flags = ADDRESS_FLAGS_NEEDS_REG; + InitializeListHead (&pAddress->AddressFileDatabase); + + ExInitializeWorkItem( + &pAddress->DestroyAddressQueueItem, + StDestroyAddress, + (PVOID)pAddress); + + pAddress->NetworkName = NetworkName; + if ((NetworkName != (PST_NETBIOS_ADDRESS)NULL) && + (NetworkName->NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP)) { + + pAddress->Flags |= ADDRESS_FLAGS_GROUP; + + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&DeviceContext->AddressDatabase, &pAddress->Linkage); + pAddress->Provider = DeviceContext; + StReferenceDeviceContext ("Create Address", DeviceContext); // count refs to the device context. + + *Address = pAddress; // return the address. + return STATUS_SUCCESS; // not finished yet. +} /* StCreateAddress */ + + +VOID +StRegisterAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process of the transport address + specified, if it has not already been started. + +Arguments: + + Address - Pointer to a transport address object to begin registering + on the network. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PIRP irp; + PTP_ADDRESS_FILE addressFile; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + if (!(Address->Flags & ADDRESS_FLAGS_NEEDS_REG)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return; + } + + Address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + Address->Flags |= ADDRESS_FLAGS_REGISTERING; + + // + // Keep a reference on this address until the registration process + // completes or is aborted. It will be aborted in UFRAMES.C, in + // either the NAME_IN_CONFLICT or ADD_NAME_RESPONSE frame handlers. + // + + StReferenceAddress ("start registration", Address); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Normally we would add the name on the network, then + // do the following in our timeout logic, but for ST + // we assume that all names are OK. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + Address->Flags &= ~ADDRESS_FLAGS_REGISTERING; + + p = Address->AddressFileDatabase.Flink; + + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + p = p->Flink; + + if (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_SUCCESS; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Dereference the address if we're all done. + // + + StDereferenceAddress ("Timer, registered", Address); + +} /* StRegisterAddress */ + + +NTSTATUS +StVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a TP_ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + PTP_ADDRESS address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (TP_ADDRESS_FILE)) && + (AddressFile->Type == ST_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + address = AddressFile->Address; + + if ((address->Size == sizeof (TP_ADDRESS)) && + (address->Type == ST_ADDRESS_SIGNATURE) ) { + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + StReferenceAddress ("verify", address); + + } else { + + StPrint1("StVerifyAddress: A %lx closing\n", address); + status = STATUS_INVALID_ADDRESS; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + } else { + + StPrint1("StVerifyAddress: A %lx bad signature\n", address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + StPrint1("StVerifyAddress: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + StPrint1("StVerifyAddress: AF %lx exception\n", address); + return GetExceptionCode(); + } + + return status; + +} + +VOID +StDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool or our lookaside list. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + The routine is called from a worker thread so that the security + descriptor can be accessed. + + This worked thread is only queued by StfDerefAddress. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_ADDRESS Address = (PTP_ADDRESS)Parameter; + + DeviceContext = Address->Provider; + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + RemoveEntryList (&Address->Linkage); + + // + // Now we can deallocate the transport address object. + // + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + --DeviceContext->AddressInUse; + + if ((DeviceContext->AddressAllocated - DeviceContext->AddressInUse) > + DeviceContext->AddressInitAllocated) { + StDeallocateAddress (DeviceContext, Address); + } else { + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + StDereferenceDeviceContext ("Destroy Address", DeviceContext); // just housekeeping. + +} /* StDestroyAddress */ + + +VOID +StRefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Address->ReferenceCount); + +} /* StRefAddress */ + + +VOID +StDerefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + StDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Address->ReferenceCount); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + // + // Defer the actual call to StDestroyAddress to a thread + // so the paged security descriptor can be accessed at IRQL 0. + // + + if (result == 0) { + ExQueueWorkItem(&Address->DestroyAddressQueueItem, DelayedWorkQueue); + } +} /* StDerefAddress */ + + + +VOID +StAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine allocates storage for an address file. Some + minimal initialization is done on the object. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a place where this routine will return + a pointer to a transport address file structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_ADDRESS_FILE AddressFile; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS_FILE)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate address file: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 102); + *TransportAddressFile = NULL; + return; + } + + AddressFile = (PTP_ADDRESS_FILE)ExAllocatePool (NonPagedPool, sizeof (TP_ADDRESS_FILE)); + if (AddressFile == NULL) { + PANIC("ST: Could not allocate address file: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 202); + *TransportAddressFile = NULL; + return; + } + RtlZeroMemory (AddressFile, sizeof(TP_ADDRESS_FILE)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS_FILE); + ++DeviceContext->AddressFileAllocated; + + AddressFile->Type = ST_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (TP_ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + *TransportAddressFile = AddressFile; + +} /* StAllocateAddressFile */ + + +VOID +StDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine frees storage for an address file. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a transport address file structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportAddressFile); + --DeviceContext->AddressFileAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS_FILE); + +} /* StDeallocateAddressFile */ + + +NTSTATUS +StCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AddressFile - Pointer to a place where this routine will return a pointer + to a transport address file structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PTP_ADDRESS_FILE addressFile; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->AddressFilePool); + if (p == &DeviceContext->AddressFilePool) { + + if ((DeviceContext->AddressFileMaxAllocated == 0) || + (DeviceContext->AddressFileAllocated < DeviceContext->AddressFileMaxAllocated)) { + + StAllocateAddressFile (DeviceContext, &addressFile); + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 402); + addressFile = NULL; + + } + + if (addressFile == NULL) { + ++DeviceContext->AddressFileExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: Could not allocate address file object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + } + + ++DeviceContext->AddressFileInUse; + if (DeviceContext->AddressFileInUse > DeviceContext->AddressFileMaxInUse) { + ++DeviceContext->AddressFileMaxInUse; + } + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + InitializeListHead (&addressFile->ConnectionDatabase); + addressFile->Address = NULL; + addressFile->FileObject = NULL; + addressFile->Provider = DeviceContext; + addressFile->State = ADDRESSFILE_STATE_OPENING; + addressFile->ConnectIndicationInProgress = FALSE; + addressFile->ReferenceCount = 1; + addressFile->CloseIrp = (PIRP)NULL; + + // + // Initialize the request handlers. + // + + addressFile->RegisteredConnectionHandler = FALSE; + addressFile->ConnectionHandler = TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + addressFile->DisconnectHandler = TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + addressFile->ReceiveHandler = TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + addressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + addressFile->ExpeditedDataHandler = TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + addressFile->ErrorHandler = TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + + + *AddressFile = addressFile; + return STATUS_SUCCESS; + +} /* StCreateAddress */ + + +NTSTATUS +StDestroyAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by StDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS address; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + + address = AddressFile->Address; + DeviceContext = AddressFile->Provider; + + if (address) { + + // + // This addressfile was associated with an address. + // + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (address->AddressFileDatabase.Flink == &address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + } + + AddressFile->Address = NULL; + + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + StDereferenceAddress ("Close", address); // remove the creation hold + + } + + // + // Save this for later completion. + // + + CloseIrp = AddressFile->CloseIrp; + + // + // return the addressFile to the pool of address files + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + --DeviceContext->AddressFileInUse; + + if ((DeviceContext->AddressFileAllocated - DeviceContext->AddressFileInUse) > + DeviceContext->AddressFileInitAllocated) { + StDeallocateAddressFile (DeviceContext, AddressFile); + } else { + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + if (CloseIrp != (PIRP)NULL) { + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + } + + return STATUS_SUCCESS; + +} /* StDestroyAddress */ + + +VOID +StReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&AddressFile->ReferenceCount); + +} /* StReferenceAddressFile */ + + +VOID +StDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + StDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&AddressFile->ReferenceCount); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + if (result == 0) { + StDestroyAddressFile (AddressFile); + } +} /* StDerefAddressFile */ + + +PTP_ADDRESS +StLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + TP_ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device object and its extension. + NetworkName - Pointer to an ST_NETBIOS_ADDRESS structure containing the + network name. + +Return Value: + + Pointer to the TP_ADDRESS object found, or NULL if not found. + +--*/ + +{ + PTP_ADDRESS address; + PLIST_ENTRY p; + ULONG i; + + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // If the network name is specified and the network names don't match, + // then the addresses don't match. + // + + i = NETBIOS_NAME_LENGTH; // length of a Netbios name + + if (address->NetworkName != NULL) { + if (NetworkName != NULL) { + if (!RtlEqualMemory ( + address->NetworkName->NetbiosName, + NetworkName->NetbiosName, + i)) { + continue; + } + } else { + continue; + } + + } else { + if (NetworkName != NULL) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + StReferenceAddress ("lookup", address); + return address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* StLookupAddress */ + + +PTP_CONNECTION +StLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connections associated with an + address, and determines if there is an connection + associated with the specific remote address. + +Arguments: + + Address - Pointer to the address. + + RemoteName - The 16-character Netbios name of the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION connection; + + + // + // Hold the spinlock so the connection database doesn't + // change. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if (((connection->Flags2 & CONNECTION_FLAGS2_REMOTE_VALID) != 0) && + ((connection->Flags & CONNECTION_FLAGS_READY) != 0)) { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + // + // If the remote names match, then return the + // connection. + // + + if (RtlEqualMemory(RemoteName, connection->RemoteName, NETBIOS_NAME_LENGTH)) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + StReferenceConnection ("Lookup found", connection); + return connection; + + } + + } else { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return (PTP_CONNECTION)NULL; + +} + + +BOOLEAN +StMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN PUCHAR NetBIOSName + ) + +/*++ + +Routine Description: + + This routine is called to compare the addressing information in a + TP_ADDRESS object with the 16-byte NetBIOS name in a frame header. + If they match, then this routine returns TRUE, else it returns FALSE. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + + NetBIOSName - Pointer to a 16-byte character string (non-terminated), + or NULL if this is a received broadcast address. + +Return Value: + + BOOLEAN, TRUE if match, FALSE if not. + +--*/ + +{ + + PULONG AddressNamePointer; + ULONG UNALIGNED * NetbiosNamePointer; + + // + // If this is address is the Netbios broadcast address, the comparison + // succeeds only if the passed in address is also NULL. + // + + if (Address->NetworkName == NULL) { + + if (NetBIOSName == NULL) { + return TRUE; + } else { + return FALSE; + } + + } else if (NetBIOSName == NULL) { + + return FALSE; + + } + + // + // Do a quick check of the first character in the names. + // + + if (Address->NetworkName->NetbiosName[0] != NetBIOSName[0]) { + return FALSE; + } + + // + // Now compare the 16-character Netbios names as ULONGs + // for speed. We know the one stored in the address + // structure is aligned. + // + + AddressNamePointer = (PULONG)(Address->NetworkName->NetbiosName); + NetbiosNamePointer = (ULONG UNALIGNED *)NetBIOSName; + + if ((AddressNamePointer[0] == NetbiosNamePointer[0]) && + (AddressNamePointer[1] == NetbiosNamePointer[1]) && + (AddressNamePointer[2] == NetbiosNamePointer[2]) && + (AddressNamePointer[3] == NetbiosNamePointer[3])) { + return TRUE; + } else { + return FALSE; + } + +} /* StMatchNetbiosAddress */ + + +VOID +StStopAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an address and + destroy the object. This is done in a graceful manner; i.e., all + outstanding addressfiles are removed from the addressfile database, and + all their activities are shut down. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS_FILE addressFile; + PLIST_ENTRY p; + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + // + // If we're already stopping this address, then don't try to do it again. + // + + if (!(Address->Flags & ADDRESS_FLAGS_STOPPING)) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + Address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + // + // Run down all addressfiles on this address. This + // will leave the address with no references + // potentially, but we don't need a temp one + // because every place that calls StStopAddress + // already has a temp reference. + // + + while (!IsListEmpty (&Address->AddressFileDatabase)) { + p = RemoveHeadList (&Address->AddressFileDatabase); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + addressFile->Address = NULL; + addressFile->FileObject->FsContext = NULL; + addressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Run-down this addressFile without the lock on. + // We don't care about removing ourselves from + // the address' ShareAccess because we are + // tearing it down. + // + + StStopAddressFile (addressFile, Address); + + // + // return the addressFile to the pool of address files + // + + StDereferenceAddressFile (addressFile); + + StDereferenceAddress ("stop address", Address); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return; + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); +} /* StStopAddress */ + + +NTSTATUS +StStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + KIRQL oldirql, oldirql1; + LIST_ENTRY localList; + PLIST_ENTRY p, pFlink; + PTP_REQUEST request; + PTP_CONNECTION connection; + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + InitializeListHead (&localList); + + // + // Run down all connections on this addressfile, and + // preform the equivalent of StDestroyAssociation + // on them. + // + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressFileList); + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + + // + // It is in the process of being disassociated already. + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags |= CONNECTION_FLAGS_DESTROY; // BUGBUG: Is this needed? + RemoveEntryList (&connection->AddressList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + StReferenceConnection ("Close AddressFile", connection); + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + StDereferenceConnection ("Close AddressFile", connection); + + StDereferenceAddress ("Destroy association", Address); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + // + // now remove all of the datagrams owned by this addressfile + // + + for (p = Address->SendDatagramQueue.Flink; + p != &Address->SendDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if ((PTP_ADDRESS_FILE)(request->Owner) == AddressFile) { + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localList, p); + } + + } + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localList, p); + } + + // + // and finally, signal failure if the address file was waiting for a + // registration to complete (Irp is set to NULL when this succeeds). + // + + if (AddressFile->Irp != NULL) { + PIRP irp=AddressFile->Irp; + AddressFile->Irp = NULL; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + // + // cancel all the datagrams on this address file + // + + while (!IsListEmpty (&localList)) { + + p = RemoveHeadList (&localList); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + StCompleteRequest (request, STATUS_NETWORK_NAME_DELETED, 0); + + } + + +} /* StStopAddressFile */ + + +NTSTATUS +StCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Irp - the Irp Address - Pointer to a TP_ADDRESS object. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + + addressFile = IrpSp->FileObject->FsContext; + addressFile->CloseIrp = Irp; + + // + // We assume that addressFile has already been verified + // at this point. + // + + address = addressFile->Address; + ASSERT (address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&addressFile->Provider->AddressResource, TRUE); + IoRemoveShareAccess (addressFile->FileObject, &address->ShareAccess); + ExReleaseResource (&addressFile->Provider->AddressResource); + + + StStopAddressFile (addressFile, address); + StDereferenceAddressFile (addressFile); + + // + // This removes a reference added by our caller. + // + + StDereferenceAddress ("IRP_MJ_CLOSE", address); + + return STATUS_PENDING; + +} /* StCloseAddress */ + + +NTSTATUS +StSendDatagramsOnAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine attempts to acquire a hold on the SendDatagramQueue of + the specified address, prepare the next datagram for shipment, and + call StSendUIMdlFrame to actually do the work. When StSendUIMdlFrame + is finished, it will cause an I/O completion routine in UFRAMES.C to + be called, at which time this routine is called again to handle the + next datagram in the pipeline. + +Arguments: + + Address - a pointer to the address object to send the datagram on. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PTA_NETBIOS_ADDRESS remoteTA; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PST_HEADER StHeader; + PSEND_PACKET_TAG SendTag; + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + StReferenceAddress ("Send datagram", Address); // keep it around + + if (!(Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS)) { + + // + // If the queue is empty, don't do anything. + // + + if (IsListEmpty (&Address->SendDatagramQueue)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + StDereferenceAddress ("Queue empty", Address); + return STATUS_SUCCESS; + } + + // + // Mark the address's send datagram queue as held so that the + // MDL and ST header will not be used for two requests at the + // same time. + // + + Address->Flags |= ADDRESS_FLAGS_SEND_IN_PROGRESS; + + // + // We own the hold, and we've released the spinlock. So pick off the + // next datagram to be sent, and ship it. + // + + p = Address->SendDatagramQueue.Flink; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + // + // If there is no remote Address specified (the Address specified has + // length 0), this is a broadcast datagram. If anything is specified, it + // will be used as a netbios address. + // + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + + remoteTA = ((PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters))-> + SendDatagramInformation->RemoteAddress; + + // + // Build the MAC header. DATAGRAM frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Address->Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER) + request->Buffer2Length, + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + // + // Build the header: 'G', dest, source + // + + StHeader = (PST_HEADER)(&Address->Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_DATAGRAM; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Source, Address->NetworkName->NetbiosName, 16); + + if (remoteTA->Address[0].AddressLength == 0) { + + // + // A broadcast datagram + // + + RtlZeroMemory (StHeader->Destination, 16); + StHeader->Flags |= ST_FLAGS_BROADCAST; + + } else { + + RtlCopyMemory (StHeader->Destination, remoteTA->Address[0].Address[0].NetbiosName, 16); + + } + + HeaderLength += sizeof(ST_HEADER); + + SendTag = (PSEND_PACKET_TAG)(Address->Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_G_FRAME; + SendTag->Packet = Address->Packet; + SendTag->Owner = (PVOID)Address; + + // + // Update our statistics for this datagram. + // + + ++DeviceContext->DatagramsSent; + ADD_TO_LARGE_INTEGER( + &DeviceContext->DatagramBytesSent, + request->Buffer2Length); + + + // + // Munge the packet length, append the data, and send it. + // + + StSetNdisPacketLength(Address->Packet->NdisPacket, HeaderLength); + + if (request->Buffer2) { + NdisChainBufferAtBack (Address->Packet->NdisPacket, (PNDIS_BUFFER)request->Buffer2); + } + + (VOID)StSendAddressFrame ( + Address); + + + // + // The hold will be released in the I/O completion handler. + // At that time, if there is another outstanding datagram + // to send, it will reset the hold and call this routine again. + // + + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + StDereferenceAddress ("Sent datagram", Address); // all done + + return STATUS_SUCCESS; + +} /* StSendDatagramsOnAddress */ diff --git a/private/ntos/tdi/st/connect.c b/private/ntos/tdi/st/connect.c new file mode 100644 index 000000000..8cb05b03b --- /dev/null +++ b/private/ntos/tdi/st/connect.c @@ -0,0 +1,1137 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAccept + o TdiListen + o TdiConnect + o TdiDisconnect + o TdiAssociateAddress + o TdiDisassociateAddress + o OpenConnection + o CloseConnection + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiAccept( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiAccept request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + KIRQL oldirql; + NTSTATUS status; + + // + // Get the connection this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // This adds a connection reference if successful. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // just set the connection flags to allow reads and writes to proceed. + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // Turn off the stopping flag for this connection. + // + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + + if (connection->AddressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + if ((connection->Flags2 & CONNECTION_FLAGS2_WAIT_ACCEPT) != 0) { + + // + // We previously completed a listen, now the user is + // coming back with an accept, Set this flag to allow + // the connection to proceed. + // + + connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (connection->Provider, OpenConnections); + + // + // Set this flag to enable disconnect indications; once + // the client has accepted he expects those. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_ACCEPT; + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Pended listen completed", connection); + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } else { + + // + // This accept is being called at some point before + // the link is up; directly from the connection handler + // or at some point slightly later. + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } + + StDereferenceConnection ("Temp TdiAccept", connection); + + return STATUS_SUCCESS; + +} /* StTdiAccept */ + + +NTSTATUS +StTdiAssociateAddress( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and the address for + the user. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS oldAddress; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; + PDEVICE_CONTEXT deviceContext; + + KIRQL oldirql, oldirql2; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + connection = irpSp->FileObject->FsContext; + status = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + // + // Make sure this connection is ready to be associated. + // + + oldAddress = (PTP_ADDRESS)NULL; + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + + // + // The connection is already associated with + // an active connection...bad! + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + StDereferenceConnection ("Temp Ref Associate", connection); + + return STATUS_INVALID_CONNECTION; + + } else { + + // + // See if there is an old association hanging around... + // this happens if the connection has been disassociated, + // but not closed. + // + + if (connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) { + + // + // Save this; since it is non-null this address + // will be dereferenced after the connection + // spinlock is released. + // + + oldAddress = connection->AddressFile->Address; + + // + // Remove the old association. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RemoveEntryList (&connection->AddressList); + RemoveEntryList (&connection->AddressFileList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + } + + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + + // + // If we removed an old association, dereference the + // address. + // + + if (oldAddress != (PTP_ADDRESS)NULL) { + + StDereferenceAddress("Removed old association", oldAddress); + + } + + + deviceContext = connection->Provider; + + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&irpSp->Parameters; + + // + // get a pointer to the address File Object, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + status = ObReferenceObjectByHandle ( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *) &fileObject, + NULL); + + if (NT_SUCCESS(status)) { + + // + // we might have one of our address objects; verify that. + // + + addressFile = fileObject->FsContext; + + if (NT_SUCCESS (StVerifyAddressObject (addressFile))) { + + // + // have an address and connection object. Add the connection to the + // address object database. Also add the connection to the address + // file object db (used primarily for cleaning up). Reference the + // address to account for one more reason for it staying open. + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + if ((addressFile->Address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + StReferenceAddress ( + "Connection associated", + addressFile->Address); + + InsertTailList ( + &addressFile->Address->ConnectionDatabase, + &connection->AddressList); + + InsertTailList ( + &addressFile->ConnectionDatabase, + &connection->AddressFileList); + + connection->AddressFile = addressFile; + connection->Flags2 |= CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 &= ~CONNECTION_FLAGS2_DISASSOCIATED; + + if (addressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + status = STATUS_SUCCESS; + + } else { + + // + // The connection is closing, stop the + // association. + // + + status = STATUS_INVALID_CONNECTION; + + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + StDereferenceAddress ("Temp associate", addressFile->Address); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + // + // Note that we don't keep a reference to this file object around. + // That's because the IO subsystem manages the object for us; we simply + // want to keep the association. We only use this association when the + // IO subsystem has asked us to close one of the file object, and then + // we simply remove the association. + // + + ObDereferenceObject (fileObject); + + } else { + status = STATUS_INVALID_HANDLE; + } + + StDereferenceConnection ("Temp Ref Associate", connection); + + return status; + +} /* TdiAssociateAddress */ + + +NTSTATUS +StTdiDisassociateAddress( + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine performs the disassociation of the connection and the address + for the user. If the connection has not been stopped, it will be stopped + here. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTP_CONNECTION connection; + NTSTATUS status; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // and now we disassociate the address. This only removes + // the appropriate reference for the connection, the + // actually disassociation will be done later. + // + // The DISASSOCIATED flag is used to make sure that + // only one person removes this reference. + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + StDereferenceConnection ("Temp use in Associate", connection); + + return STATUS_SUCCESS; + +} /* TdiDisassociateAddress */ + + +NTSTATUS +StTdiConnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + PSTRING GeneralBroadcastSourceRoute = NULL; // BUGBUG: define this later. + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + PTA_NETBIOS_ADDRESS RemoteAddress; + ULONG RemoteAddressLength; + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no connect request should take more + // than 15 seconds if there is someone out there. We'll assume that's + // what the user wanted if they specify -1 as the timer length. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_CONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Check that the remote is a Netbios address. + // + + RemoteAddress = (PTA_NETBIOS_ADDRESS) + (parameters->RequestConnectionInformation->RemoteAddress); + + if (RemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) { + + StDereferenceConnection ("Not Netbios", connection); + return STATUS_BAD_NETWORK_PATH; // don't even try to find it. + + } + + // + // copy the called address someplace we can use it. + // + + RemoteAddressLength = parameters->RequestConnectionInformation->RemoteAddressLength; + + if (RemoteAddressLength > sizeof(TA_NETBIOS_ADDRESS)) { + RemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS); + } + + RtlCopyMemory( + connection->CalledAddress.NetbiosName, + RemoteAddress->Address[0].Address[0].NetbiosName, + 16); + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { // couldn't make the request. + StDereferenceConnection ("Throw away", connection); + return status; // return with failure. + } else { + + // Reference the connection since StDestroyRequest derefs it. + + StReferenceConnection("For connect request", connection); + + tpRequest->Owner = ConnectionType; + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock (cancelirql); + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temporary Use 1", connection); + return STATUS_PENDING; + } else { + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + + connection->Flags |= CONNECTION_FLAGS_CONNECTOR; // we're the initiator. + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + IoReleaseCancelSpinLock(cancelirql); + + } + } + + status = StSendConnect ( + connection); + + if (!NT_SUCCESS(status)) { // can't send the name request + StStopConnection (connection, status); + StDereferenceConnection("Temporary Use 2", connection); + + // + // Note that this return status isn't really a lie. We are waiting + // for the connection to run down. + // + + return STATUS_PENDING; + } + + + StDereferenceConnection("Temporary Use 3", connection); + + return STATUS_PENDING; // things are started. + +} /* TdiConnect */ + + +NTSTATUS +StTdiDisconnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiDisconnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + LARGE_INTEGER timeout; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + KIRQL oldirql; + NTSTATUS status; + + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // if the connection is currently stopping, there's no reason to blow + // it away... + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection ("Ignoring disconnect", connection); // release our lookup reference. + return connection->Status; + + } else { + connection->Flags2 &= ~ (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_PRE_ACCEPT | + CONNECTION_FLAGS2_WAIT_ACCEPT); + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + + // + // Set this flag so the disconnect IRP is completed. + // + // BUGBUG: If the connection goes down before we can + // call StStopConnection with STATUS_LOCAL_DISCONNECT, + // the disconnect IRP won't get completed. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + connection->DisconnectIrp = Irp; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // fix up the timeout if required; no disconnect request should take very + // long. However, the user can cause the timeout to not happen if they + // desire that. + // + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no disconnect request should take more + // than 15 seconds. We'll assume that's what the user wanted if they + // specify -1 as the timer. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_DISCONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Now the reason for the disconnect + // + + if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_RELEASE) { + connection->Flags |= CONNECTION_FLAGS_DESTROY; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_ABORT) { + connection->Flags |= CONNECTION_FLAGS_ABORT; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_WAIT) { + connection->Flags |= CONNECTION_FLAGS_ORDREL; + } + + // + // This will get passed to IoCompleteRequest during TdiDestroyConnection + // + + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); // starts the abort sequence. + StDereferenceConnection ("Disconnecting", connection); // release our lookup reference. + + // + // This request will be completed by TdiDestroyConnection once + // the connection reference count drops to 0. + // + + return STATUS_PENDING; +} /* TdiDisconnect */ + + +NTSTATUS +StTdiListen( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiListen request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_LISTEN parameters; + + // + // validate this connection + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL_LISTEN)&irpSp->Parameters; + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, // timeout value (can be 0). + &tpRequest); + + + if (!NT_SUCCESS (status)) { // couldn't make the request. + + StDereferenceConnection ("For create", connection); + return status; // return with failure. + } + + // Reference the connection since StDestroyRequest derefs it. + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + tpRequest->Owner = ConnectionType; + + StReferenceConnection("For listen request", connection); + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temp create", connection); + return STATUS_PENDING; + + } else { + + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + connection->Flags |= CONNECTION_FLAGS_LISTENER | // we're the passive one. + CONNECTION_FLAGS_WAIT_LISTEN; // waiting for a connect + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + // + // If TDI_QUERY_ACCEPT is not set, then we set PRE_ACCEPT to + // indicate that when the listen completes we do not have to + // wait for a TDI_ACCEPT to continue. + // + + if ((parameters->RequestFlags & TDI_QUERY_ACCEPT) == 0) { + connection->Flags2 |= CONNECTION_FLAGS2_PRE_ACCEPT; + } + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + IoReleaseCancelSpinLock(cancelirql); + + } + + // + // Wait for an incoming NAME_QUERY frame. The remainder of the + // connectionless protocol to set up a connection is processed + // in the NAME_QUERY frame handler. + // + + StDereferenceConnection("Temp create", connection); + + return STATUS_PENDING; // things are started. +} /* TdiListen */ + + +NTSTATUS +StOpenConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_CONNECTION connection; + PFILE_FULL_EA_INFORMATION ea; + + UNREFERENCED_PARAMETER (Irp); + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful StCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + status = StCreateConnection (DeviceContext, &connection); + if (!NT_SUCCESS (status)) { + return status; // sorry, we couldn't make one. + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + IrpSp->FileObject->FsContext = (PVOID)connection; + IrpSp->FileObject->FsContext2 = (PVOID)TDI_CONNECTION_FILE; + connection->FileObject = IrpSp->FileObject; + + return status; + +} /* StOpenConnection */ + + +NTSTATUS +StCloseConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. There may be actions in + progress on this connection, so we note the closing IRP, mark the + connection as closing, and complete it somewhere down the road (when all + references have been removed). + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_CONNECTION connection; + + UNREFERENCED_PARAMETER (DeviceObject); + UNREFERENCED_PARAMETER (Irp); + + // + // is the file object a connection? + // + + connection = IrpSp->FileObject->FsContext; + + + // + // We duplicate the code from VerifyConnectionObject, + // although we don't actually call that since it does + // a reference, which we don't want (to avoid bouncing + // the reference count up from 0 if this is a dead + // link). + // + + try { + + if ((connection->Size == sizeof (TP_CONNECTION)) && + (connection->Type == ST_CONNECTION_SIGNATURE)) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + status = STATUS_SUCCESS; + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // We recognize it; is it closing already? + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection("Temp Close", connection); + return STATUS_INVALID_CONNECTION; + } + + connection->Flags2 |= CONNECTION_FLAGS2_CLOSING; + + // + // if there is activity on the connection, tear it down. + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + } + + // + // If the connection is still associated, disassociate it. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + + // + // Save this to complete the IRP later. + // + + connection->CloseIrp = Irp; + + // + // make it impossible to use this connection from the file object + // + + IrpSp->FileObject->FsContext = NULL; + IrpSp->FileObject->FsContext2 = NULL; + connection->FileObject = NULL; + + // + // dereference for the creation. Note that this dereference + // here won't have any effect until the regular reference count + // hits zero. + // + + StDereferenceConnectionSpecial (" Closing Connection", connection); + + return STATUS_PENDING; + +} /* StCloseConnection */ + diff --git a/private/ntos/tdi/st/connobj.c b/private/ntos/tdi/st/connobj.c new file mode 100644 index 000000000..27c28665a --- /dev/null +++ b/private/ntos/tdi/st/connobj.c @@ -0,0 +1,1375 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connobj.c + +Abstract: + + This module contains code which implements the TP_CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +VOID +StAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport connection. Some + minimal initialization is done. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. Returns + NULL if the storage cannot be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_CONNECTION Connection; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_CONNECTION)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate connection: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 103); + *TransportConnection = NULL; + return; + } + + Connection = (PTP_CONNECTION)ExAllocatePool (NonPagedPool, + sizeof (TP_CONNECTION)); + if (Connection == NULL) { + PANIC("ST: Could not allocate connection: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 203); + *TransportConnection = NULL; + return; + } + RtlZeroMemory (Connection, sizeof(TP_CONNECTION)); + + DeviceContext->MemoryUsage += sizeof(TP_CONNECTION); + ++DeviceContext->ConnectionAllocated; + + Connection->Type = ST_CONNECTION_SIGNATURE; + Connection->Size = sizeof (TP_CONNECTION); + + Connection->Provider = DeviceContext; + Connection->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Connection->SpinLock); + + InitializeListHead (&Connection->LinkList); + InitializeListHead (&Connection->AddressFileList); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->PacketWaitLinkage); + InitializeListHead (&Connection->PacketizeLinkage); + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + + StAddSendPacket (DeviceContext); + + *TransportConnection = Connection; + +} /* StAllocateConnection */ + + +VOID +StDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport connection. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a transport connection structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportConnection); + --DeviceContext->ConnectionAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_CONNECTION); + + StRemoveSendPacket (DeviceContext); + +} /* StDeallocateConnection */ + + +NTSTATUS +StCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine creates a transport connection. The reference count in the + connection is automatically set to 1, and the reference count in the + DeviceContext is incremented. + +Arguments: + + Address - the address for this connection to be associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION Connection; + KIRQL oldirql; + PLIST_ENTRY p; + UINT TempDataLen; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->ConnectionPool); + if (p == &DeviceContext->ConnectionPool) { + + if ((DeviceContext->ConnectionMaxAllocated == 0) || + (DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) { + + StAllocateConnection (DeviceContext, &Connection); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 403); + Connection = NULL; + + } + + if (Connection == NULL) { + ++DeviceContext->ConnectionExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: Could not allocate connection object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + } + + ++DeviceContext->ConnectionInUse; + if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) { + ++DeviceContext->ConnectionMaxInUse; + } + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // We have two references; one is for creation, and the + // other is a temporary one so that the connection won't + // go away before the creator has a chance to access it. + // + + Connection->SpecialRefCount = 1; + Connection->ReferenceCount = -1; // this is -1 based + + // + // Initialize the request queues & components of this connection. + // + + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->AddressFileList); + Connection->SpecialReceiveIrp = (PIRP)NULL; + Connection->Flags = 0; + Connection->Flags2 = 0; + Connection->MessageBytesReceived = (USHORT)0; // no data yet + Connection->MessageBytesAcked = (USHORT)0; + Connection->Context = NULL; // no context yet. + Connection->Status = STATUS_PENDING; // default StStopConnection status. + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->CurrentReceiveRequest = (PTP_REQUEST)NULL; + Connection->DisconnectIrp = (PIRP)NULL; + Connection->CloseIrp = (PIRP)NULL; + Connection->AddressFile = NULL; + Connection->IndicationInProgress = FALSE; + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + &TempDataLen); + Connection->MaximumDataSize = TempDataLen - sizeof(ST_HEADER); + + StReferenceDeviceContext ("Create Connection", DeviceContext); + + *TransportConnection = Connection; // return the connection. + + return STATUS_SUCCESS; +} /* StCreateConnection */ + + +NTSTATUS +StVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. + +Arguments: + + Connection - potential pointer to a TP_CONNECTION object. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + + // + // try to verify the connection signature. If the signature is valid, + // get the connection spinlock, check its state, and increment the + // reference count if it's ok to use it. Note that being in the stopping + // state is an OK place to be and reference the connection; we can + // disassociate the address while running down. + // + + try { + + if ((Connection->Size == sizeof (TP_CONNECTION)) && + (Connection->Type == ST_CONNECTION_SIGNATURE)) { + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + StReferenceConnection ("Verify Temp Use", Connection); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + return status; + +} + + +NTSTATUS +StDestroyAssociation( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys the association between a transport connection and + the address it was formerly associated with. The only action taken is + to disassociate the address and remove the connection from all address + queues. + + This routine is only called by StDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql2; + PTP_ADDRESS_FILE addressFile; + + + ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + return STATUS_SUCCESS; + } else { + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + addressFile = TransportConnection->AddressFile; + + // + // Delink this connection from its associated address connection + // database. To do this we must spin lock on the address object as + // well as on the connection, + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + RemoveEntryList (&TransportConnection->AddressFileList); + RemoveEntryList (&TransportConnection->AddressList); + + InitializeListHead (&TransportConnection->AddressList); + InitializeListHead (&TransportConnection->AddressFileList); + + // + // remove the association between the address and the connection. + // + + TransportConnection->AddressFile = NULL; + + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + // + // and remove a reference to the address + // + + StDereferenceAddress ("Destroy association", addressFile->Address); + + + return STATUS_SUCCESS; + +} /* StDestroyAssociation */ + + +NTSTATUS +StIndicateDisconnect( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine indicates a remote disconnection on this connection if it + is necessary to do so. No other action is taken here. + + This routine is only called by StDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS_FILE addressFile; + PDEVICE_CONTEXT DeviceContext; + ULONG DisconnectReason; + PIRP DisconnectIrp; + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + + if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) { + + // + // Turn off all but the still-relevant bits in the flags. + // + + ASSERT (TransportConnection->Flags & CONNECTION_FLAGS_STOPPING); + + TransportConnection->Flags = CONNECTION_FLAGS_STOPPING; + TransportConnection->Flags2 &= + (CONNECTION_FLAGS2_ASSOCIATED | + CONNECTION_FLAGS2_DISASSOCIATED | + CONNECTION_FLAGS2_CLOSING); + + // + // Clean up other stuff -- basically everything gets + // done here except for the flags and the status, since + // they are used to block other requests. When the connection + // is given back to us (in Accept, Connect, or Listen) + // they are cleared. + // + + TransportConnection->MessageBytesReceived = (USHORT)0; // no data yet + TransportConnection->MessageBytesAcked = (USHORT)0; + + TransportConnection->CurrentReceiveRequest = (PTP_REQUEST)NULL; + + DisconnectIrp = TransportConnection->DisconnectIrp; + TransportConnection->DisconnectIrp = (PIRP)NULL; + + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + + DeviceContext = TransportConnection->Provider; + addressFile = TransportConnection->AddressFile; + + + // + // If this connection was stopped by a call to TdiDisconnect, + // we have to complete that. We save the Irp so we can return + // the connection to the pool before we complete the request. + // + + + if (DisconnectIrp != (PIRP)NULL) { + + // + // Now complete the IRP if needed. This will be non-null + // only if TdiDisconnect was called, and we have not + // yet completed it. + // + + DisconnectIrp->IoStatus.Information = 0; + DisconnectIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (DisconnectIrp, IO_NETWORK_INCREMENT); + + } else if ((TransportConnection->Status != STATUS_LOCAL_DISCONNECT) && + (addressFile->RegisteredDisconnectHandler == TRUE)) { + + // + // This was a remotely spawned disconnect, so indicate that + // to our client. Note that in the comparison above we + // check the status first, since if it is LOCAL_DISCONNECT + // addressFile may be NULL (BUGBUG: This is sort of a hack + // for PDK2, we should really indicate the disconnect inside + // StStopConnection, where we know addressFile is valid). + // + + // + // if the disconnection was remotely spawned, then indicate + // disconnect. In the case that a disconnect was issued at + // the same time as the connection went down remotely, we + // won't do this because DisconnectIrp will be non-NULL. + // + + // + // Invoke the user's disconnection event handler, if any. We do this here + // so that any outstanding sends will complete before we tear down the + // connection. + // + + DisconnectReason = 0; + if (TransportConnection->Flags & CONNECTION_FLAGS_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (TransportConnection->Flags & CONNECTION_FLAGS_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + (*addressFile->DisconnectHandler)( + addressFile->DisconnectHandlerContext, + TransportConnection->Context, + 0, + NULL, + 0, + NULL, + DisconnectReason); + + } + + } else { + + // + // The client does not yet think that this connection + // is up...generally this happens due to request count + // fluctuation during connection setup. + // + + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + + return STATUS_SUCCESS; + +} /* StIndicateDisconnect */ + + +NTSTATUS +StDestroyConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to our lookaside list. It is assumed that the caller + has removed all IRPs from the connections's queues first. + + This routine is only called by StDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + + DeviceContext = TransportConnection->Provider; + + // + // Destroy any association that this connection has. + // + + StDestroyAssociation (TransportConnection); + + // + // Clear out any associated nasties hanging around the connection. Note + // that the current flags are set to STOPPING; this way anyone that may + // maliciously try to use the connection after it's dead and gone will + // just get ignored. + // + + TransportConnection->Flags = CONNECTION_FLAGS_STOPPING; + TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING; + TransportConnection->MessageBytesReceived = (USHORT)0; // no data yet + TransportConnection->MessageBytesAcked = (USHORT)0; + + + // + // Now complete the close IRP. This will be set to non-null + // when CloseConnection was called. + // + + CloseIrp = TransportConnection->CloseIrp; + + if (CloseIrp != (PIRP)NULL) { + + TransportConnection->CloseIrp = (PIRP)NULL; + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + + } + + // + // Return the connection to the provider's pool. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + --DeviceContext->ConnectionInUse; + + if ((DeviceContext->ConnectionAllocated - DeviceContext->ConnectionInUse) > + DeviceContext->ConnectionInitAllocated) { + StDeallocateConnection (DeviceContext, TransportConnection); + } else { + InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + StDereferenceDeviceContext ("Destroy Connection", DeviceContext); + + return STATUS_SUCCESS; + +} /* StDestroyConnection */ + + +VOID +StRefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedIncrement (&TransportConnection->ReferenceCount); + + if (result == 0) { + + // + // The first increment causes us to increment the + // "ref count is not zero" special ref. + // + + ExInterlockedAddUlong( + (PULONG)(&TransportConnection->SpecialRefCount), + 1, + TransportConnection->ProviderInterlock); + + } + + ASSERT (result >= 0); + +} /* StRefConnection */ + + +VOID +StDerefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + StDestroyConnection to remove it from the system. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&TransportConnection->ReferenceCount); + + // + // If all the normal references to this connection are gone, then + // we can remove the special reference that stood for + // "the regular ref count is non-zero". + // + + if (result < 0) { + + // + // If the refcount is -1, then we need to indicate + // disconnect. However, we need to + // do this before we actually do the special deref, since + // otherwise the connection might go away while we + // are doing that. + // + + StIndicateDisconnect (TransportConnection); + + // + // Now it is OK to let the connection go away. + // + + StDereferenceConnectionSpecial ("Regular ref gone", TransportConnection); + + } + +} /* StDerefConnection */ + + +VOID +StDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routines completes the dereferencing of a connection. + It may be called any time, but it does not do its work until + the regular reference count is also 0. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (TransportConnection->ProviderInterlock, &oldirql); + + --TransportConnection->SpecialRefCount; + + if ((TransportConnection->SpecialRefCount == 0) && + (TransportConnection->ReferenceCount == -1)) { + + // + // If we have deleted all references to this connection, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the connection any longer. + // + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + StDestroyConnection (TransportConnection); + + } else { + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + } + +} /* StDerefConnectionSpecial */ + + +PTP_CONNECTION +StFindConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR LocalName, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connections associated with a + device context, and determines if there is an connection + associated with the specific remote address on the + specific local address. + +Arguments: + + DeviceContext - Pointer to the device context. + + LocalName - The 16-character Netbios name of the local address. + + RemoteName - The 16-character Netbios name of the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY Flink; + PTP_ADDRESS Address; + BOOLEAN MatchedAddress = FALSE; + PTP_CONNECTION Connection; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (Flink = DeviceContext->AddressDatabase.Flink; + Flink != &DeviceContext->AddressDatabase; + Flink = Flink->Flink) { + + Address = CONTAINING_RECORD ( + Flink, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + if (StMatchNetbiosAddress (Address, LocalName)) { + + StReferenceAddress ("Looking for connection", Address); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (!MatchedAddress) { + return NULL; + } + + Connection = StLookupRemoteName (Address, RemoteName); + + StDereferenceAddress ("Looking for connection", Address); + + return Connection; + +} + + +PTP_CONNECTION +StLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine accepts a connection identifier and an address and + returns a pointer to the connection object, TP_CONNECTION. If the + connection identifier is not found on the address, then NULL is returned. + This routine automatically increments the reference count of the + TP_CONNECTION structure if it is found. It is assumed that the + TP_ADDRESS structure is already held with a reference count. + + BUGBUG: Should the ConnectionDatabase go in the address file? + +Arguments: + + Address - Pointer to a transport address object. + + ConnectionContext - Connection Context for this address. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION Connection; + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if ((Connection->Context == ConnectionContext) && + ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0)) { + // This reference is removed by the calling function + StReferenceConnection ("Lookup up for request", Connection); + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return Connection; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* StLookupConnectionByContext */ + + +PTP_CONNECTION +StLookupListeningConnection( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine scans the connection database on an address to find + a TP_CONNECTION object which has CONNECTION_FLAGS_WAIT_NQ + flag set. It returns a pointer to the found connection object (and + simultaneously resets the flag) or NULL if it could not be found. + The reference count is also incremented atomically on the connection. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + if (Connection->Flags & CONNECTION_FLAGS_WAIT_LISTEN) { + + // This reference is removed by the calling function + StReferenceConnection ("Found Listening", Connection); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_LISTEN; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return Connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* StLookupListeningConnection */ + + +VOID +StStopConnection( + IN PTP_CONNECTION Connection, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on a connection and + destroy the object. This is done in a graceful manner; i.e., all + outstanding requests are terminated by cancelling them, etc. It is + assumed that the caller has a reference to this connection object, + but this routine will do the dereference for the one issued at creation + time. + + Orderly release is a function of this routine, but it is not a provided + service of this transport provider, so there is no code to do it here. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Status - The status that caused us to stop the connection. This + will determine what status pending requests are aborted with, + and also how we proceed during the stop (whether to send a + session end, and whether to indicate disconnect). + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1, cancelirql; + PLIST_ENTRY p; + PIRP Irp; + PTP_REQUEST Request; + ULONG DisconnectReason; + PULONG StopCounter; + PDEVICE_CONTEXT DeviceContext; + BOOLEAN WasConnected; + + + DeviceContext = Connection->Provider; + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) { + + // + // We are stopping the connection, record statistics + // about it. + // + + if (Connection->Flags & CONNECTION_FLAGS_READY) { + + DECREMENT_COUNTER (DeviceContext, OpenConnections); + WasConnected = TRUE; + + } else { + + WasConnected = FALSE; + + } + + Connection->Flags &= ~CONNECTION_FLAGS_READY; // no longer open for business + Connection->Flags |= CONNECTION_FLAGS_STOPPING; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->Status = Status; + + // + // If this connection is waiting to packetize, + // remove it from the device context queue it is on. + // + // NOTE: If the connection is currently in the + // packetize queue, it will eventually go to get + // packetized and at that point it will get + // removed. + // + + if (Connection->Flags & CONNECTION_FLAGS_SUSPENDED) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + } + + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + + // + // Run down all TdiSend requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->SendQueue); + if (p == &Connection->SendQueue) { + break; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + Irp->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + StCompleteSendIrp (Irp, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + // + // Run down all TdiReceive requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->ReceiveQueue); + if (p == &Connection->ReceiveQueue) { + break; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest (Request, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + // + // Run down all TdiConnect/TdiDisconnect/TdiListen requests. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + break; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest (Request, Connection->Status, 0); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // If we aren't DESTROYing the link, then send a SESSION_END frame + // to the remote side. When the SESSION_END frame is acknowleged, + // we will decrement the connection's reference count by one, removing + // its creation reference. This will cause the connection object to + // be disposed of, and will begin running down the link. + // DGB: add logic to avoid blowing away link if one doesn't exist yet. + // + + DisconnectReason = 0; + if (Connection->Flags & CONNECTION_FLAGS_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (Connection->Flags & CONNECTION_FLAGS_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + // + // When this completes we will dereference the connection. + // + + if (WasConnected) { + StSendDisconnect (Connection); + } + + + switch (Status) { + + case STATUS_LOCAL_DISCONNECT: + StopCounter = &DeviceContext->LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + StopCounter = &DeviceContext->RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + StopCounter = &DeviceContext->LinkFailures; + break; + case STATUS_IO_TIMEOUT: + StopCounter = &DeviceContext->SessionTimeouts; + break; + case STATUS_CANCELLED: + StopCounter = &DeviceContext->CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + StopCounter = &DeviceContext->RemoteResourceFailures; + break; + case STATUS_INSUFFICIENT_RESOURCES: + StopCounter = &DeviceContext->LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + StopCounter = &DeviceContext->NotFoundFailures; + break; + case STATUS_REMOTE_NOT_LISTENING: + StopCounter = &DeviceContext->NoListenFailures; + break; + + default: + StopCounter = NULL; + break; + + } + + if (StopCounter != NULL) { + + *StopCounter = *StopCounter + 1; + + } + + + // + // Note that we've blocked all new requests being queued during the + // time we have been in this teardown code; StDestroyConnection also + // sets the connection flags to STOPPING when returning the + // connection to the queue. This avoids lingerers using non-existent + // connections. + // + + } else { + + // + // The connection was already stopping. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } + +} /* StStopConnection */ + + +VOID +StCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + or a listen. It is simple since there can only be one of these + active on a connection; we just stop the connection, the IRP + will get completed as part of normal session teardown. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_REQUEST Request; + PLIST_ENTRY p; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_CONNECT || IrpSp->MinorFunction == TDI_LISTEN)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + StReferenceConnection ("Cancelling Send", Connection); + + p = RemoveHeadList (&Connection->InProgressRequest); + ASSERT (p != &Connection->InProgressRequest); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + ASSERT (Request->IoRequestPacket == Irp); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + StCompleteRequest (Request, STATUS_CANCELLED, 0); + StStopConnection (Connection, STATUS_CANCELLED); + + StDereferenceConnection ("Cancel done", Connection); + +} + diff --git a/private/ntos/tdi/st/devctx.c b/private/ntos/tdi/st/devctx.c new file mode 100644 index 000000000..94cc2f385 --- /dev/null +++ b/private/ntos/tdi/st/devctx.c @@ -0,0 +1,256 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + devctx.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +VOID +StRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + ASSERT (DeviceContext->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&DeviceContext->ReferenceCount); + +} /* StRefDeviceContext */ + + +VOID +StDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&DeviceContext->ReferenceCount); + + ASSERT (result >= 0); + + if (result == 0) { + StDestroyDeviceContext (DeviceContext); + } + +} /* StDerefDeviceContext */ + + + +NTSTATUS +StCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + DeviceContext - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE_CONTEXT deviceContext; + USHORT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + // + + status = IoCreateDevice( + DriverObject, + sizeof (DEVICE_CONTEXT) - sizeof (DEVICE_OBJECT) + + (DeviceName->Length + sizeof(UNICODE_NULL)), + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + deviceContext = (PDEVICE_CONTEXT)deviceObject; + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)deviceContext) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE_CONTEXT) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + deviceContext->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + deviceContext->DeviceName = (PWCHAR)(deviceContext+1); + RtlCopyMemory( + deviceContext->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + deviceContext->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + deviceContext->ReferenceCount = 1; + + // + // initialize the various fields in the device context + // + + KeInitializeSpinLock (&deviceContext->Interlock); + KeInitializeSpinLock (&deviceContext->SpinLock); + + deviceContext->ControlChannelIdentifier = 1; + + InitializeListHead (&deviceContext->ConnectionPool); + InitializeListHead (&deviceContext->AddressPool); + InitializeListHead (&deviceContext->AddressFilePool); + InitializeListHead (&deviceContext->AddressDatabase); + InitializeListHead (&deviceContext->PacketWaitQueue); + InitializeListHead (&deviceContext->PacketizeQueue); + InitializeListHead (&deviceContext->RequestPool); + deviceContext->PacketPool.Next = NULL; + deviceContext->ReceivePacketPool.Next = NULL; + deviceContext->ReceiveBufferPool.Next = NULL; + InitializeListHead (&deviceContext->ReceiveInProgress); + InitializeListHead (&deviceContext->IrpCompletionQueue); + + + deviceContext->State = DEVICECONTEXT_STATE_CLOSED; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&deviceContext->AddressResource); + + // + // set the netbios multicast address for this network type + // + + for (i=0; iLocalAddress.Address [i] = 0; // set later + deviceContext->MulticastAddress.Address [i] = 0; + } + + deviceContext->Type = ST_DEVICE_CONTEXT_SIGNATURE; + deviceContext->Size - sizeof (DEVICE_CONTEXT); + + *DeviceContext = deviceContext; + return STATUS_SUCCESS; +} + + +VOID +StDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + DeviceContext - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&DeviceContext->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)DeviceContext); + return; +} diff --git a/private/ntos/tdi/st/event.c b/private/ntos/tdi/st/event.c new file mode 100644 index 000000000..9c98c9596 --- /dev/null +++ b/private/ntos/tdi/st/event.c @@ -0,0 +1,185 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiSetEventHandler( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Irp - Pointer to the IRP for this request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS rc=STATUS_SUCCESS; + KIRQL oldirql; + PTDI_REQUEST_KERNEL_SET_EVENT parameters; + PIO_STACK_LOCATION irpSp; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + NTSTATUS status; + + // + // Get the Address this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + addressFile = irpSp->FileObject->FsContext; + status = StVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)&irpSp->Parameters; + + switch (parameters->EventType) { + + case TDI_EVENT_RECEIVE: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + } else { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)parameters->EventHandler; + addressFile->ReceiveHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_EXPEDITED: + + if (parameters->EventHandler == NULL) { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + } else { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)parameters->EventHandler; + addressFile->ExpeditedDataHandlerContext = parameters->EventContext; + addressFile->RegisteredExpeditedDataHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)parameters->EventHandler; + addressFile->ReceiveDatagramHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (parameters->EventHandler == NULL) { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + } else { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)parameters->EventHandler; + addressFile->ErrorHandlerContext = parameters->EventContext; + addressFile->RegisteredErrorHandler = TRUE; + } + + break; + + case TDI_EVENT_DISCONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + } else { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)parameters->EventHandler; + addressFile->DisconnectHandlerContext = parameters->EventContext; + addressFile->RegisteredDisconnectHandler = TRUE; + } + + break; + + case TDI_EVENT_CONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredConnectionHandler = FALSE; + } else { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)parameters->EventHandler; + addressFile->ConnectionHandlerContext = parameters->EventContext; + addressFile->RegisteredConnectionHandler = TRUE; + } + break; + + default: + + rc = STATUS_INVALID_PARAMETER; + + } /* switch */ + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + StDereferenceAddress ("Set event handler", address); + + return rc; +} /* TdiSetEventHandler */ diff --git a/private/ntos/tdi/st/framesnd.c b/private/ntos/tdi/st/framesnd.c new file mode 100644 index 000000000..0d9f5cff7 --- /dev/null +++ b/private/ntos/tdi/st/framesnd.c @@ -0,0 +1,371 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + framesnd.c + +Abstract: + + This module contains routines which build and send Sample transport + frames for other modules. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +NTSTATUS +StSendConnect( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a CONNECT frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PSEND_PACKET_TAG SendTag; + PTP_PACKET Packet; + PST_HEADER StHeader; + + + DeviceContext = Connection->Provider; + + // + // Allocate a packet from the pool. + // + + Status = StCreatePacket (DeviceContext, &Packet); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_C_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)Connection; + + // + // Build the MAC header. + // + + // + // CONNECT frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the header: 'C', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_CONNECT; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StNdisSend (Packet); + + return STATUS_SUCCESS; +} /* StSendConnect */ + + +NTSTATUS +StSendDisconnect( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a DISCONNECT frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PSEND_PACKET_TAG SendTag; + PTP_PACKET Packet; + PST_HEADER StHeader; + + + DeviceContext = Connection->Provider; + + // + // Allocate a packet from the pool. + // + + Status = StCreatePacket (DeviceContext, &Packet); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_D_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)Connection; + + // + // Build the MAC header. + // + + // + // CONNECT frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the header: 'D', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_DISCONNECT; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StNdisSend (Packet); + + return STATUS_SUCCESS; + +} /* StSendDisconnect */ + + +NTSTATUS +StSendAddressFrame( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + It is intended that this routine be used for sending datagrams and + braodcast datagrams. + + The datagram to be sent is described in the NDIS packet contained + in the Address. When the send completes, the send completion handler + returns the NDIS buffer describing the datagram to the buffer pool and + marks the address ndis packet as usable again. Thus, all datagram + frames are sequenced through the address they are sent on. + +Arguments: + + Address - pointer to the address from which to send this datagram. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + + // + // Send the packet. + // + + DeviceContext = Address->Provider; + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + StNdisSend (Address->Packet); + + return STATUS_PENDING; +} /* StSendAddressFrame */ + + +VOID +StSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called as an I/O completion handler at the time a + StSendUIMdlFrame send request is completed. Because this handler is only + associated with StSendUIMdlFrame, and because StSendUIMdlFrame is only + used with datagrams and broadcast datagrams, we know that the I/O being + completed is a datagram. Here we complete the in-progress datagram, and + start-up the next one if there is one. + +Arguments: + + Address - Pointer to a transport address on which the datagram + is queued. + + NdisPacket - pointer to the NDIS packet describing this request. + +Return Value: + + none. + +--*/ + +{ + PTP_REQUEST Request; + PLIST_ENTRY p; + KIRQL oldirql; + PNDIS_BUFFER HeaderBuffer; + + UNREFERENCED_PARAMETER(NdisPacket); + + StReferenceAddress ("Complete datagram", Address); + + // + // Dequeue the current request and return it to the client. Release + // our hold on the send datagram queue. + // + // *** There may be no current request, if the one that was queued + // was aborted or timed out. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + p = RemoveHeadList (&Address->SendDatagramQueue); + + if (p != &Address->SendDatagramQueue) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Address->Packet->NdisPacket, &HeaderBuffer); + + // drop the rest of the packet + + NdisReinitializePacket (Address->Packet->NdisPacket); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Address->Packet->NdisPacket, HeaderBuffer); + + // + // Ignore NdisStatus; datagrams always "succeed". + // + + StCompleteRequest (Request, STATUS_SUCCESS, Request->Buffer2Length); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Send more datagrams on the Address if possible. + // + + StSendDatagramsOnAddress (Address); // do more datagrams. + + } else { + + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + } + + StDereferenceAddress ("Complete datagram", Address); + +} /* StSendDatagramCompletion */ diff --git a/private/ntos/tdi/st/iframes.c b/private/ntos/tdi/st/iframes.c new file mode 100644 index 000000000..59b171c92 --- /dev/null +++ b/private/ntos/tdi/st/iframes.c @@ -0,0 +1,808 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + iframes.c + +Abstract: + + This module contains routines called to handle i-frames received + from the NDIS driver. Most of these routines are called at receive + indication time. + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "st.h" +#if 27 +ULONG StNoisyReceives = 0; +ULONG StRcvLoc = 0; +ULONG StRcvs[10]; +#endif + + + +NTSTATUS +StProcessIIndicate( + IN PTP_CONNECTION Connection, + IN PST_HEADER StHeader, + IN UINT StIndicatedLength, + IN UINT StTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last + ) + +/*++ + +Routine Description: + + This routine processes a received I frame at indication time. It will do + all necessary verification processing of the frame and pass those frames + that are valid on to the proper handling routines. + +Arguments: + + Connection - The connection that the data is destined for. + + StHeader - A pointer to the start of the ST header in the packet. + + StIndicatedLength - The length of the packet indicated, starting at + StHeader. + + StTotalLength - The total length of the packet, starting at StHeader. + + ReceiveContext - A magic value for NDIS that indicates which packet we're + talking about, used for calling TransferData + + Last - TRUE if this is the last packet in a send. + +Return Value: + + STATUS_SUCCESS if we've consumed the packet, but + STATUS_MORE_PROCESSING_REQUIRED if we did so and also + activated a receive; this tells the caller not to + remove the connection refcount. + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status, tmpstatus; + KIRQL cancelirql; + PDEVICE_CONTEXT deviceContext; + NDIS_STATUS ndisStatus; + PNDIS_PACKET ndisPacket; + PSINGLE_LIST_ENTRY linkage; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PNDIS_BUFFER ndisBuffer; + ULONG destBytes; + ULONG bufferChainLength; + ULONG indicateBytesTransferred; + ULONG ndisBytesTransferred; + PUCHAR DataHeader; + ULONG DataTotalLength; + ULONG DataIndicatedLength; + UINT BytesToTransfer; + ULONG bytesIndicated; + PRECEIVE_PACKET_TAG receiveTag; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PMDL SavedCurrentMdl; + ULONG SavedCurrentByteOffset; + LARGE_INTEGER time; + ULONG DumpData[2]; + BOOLEAN CancelSpinLockHeld = FALSE; +#if 27 + if (StNoisyReceives) { + DbgPrint ("Indicate %d, Total %d\n", StIndicatedLength, StTotalLength); + } + if (StTotalLength > 1000) { + StRcvs[StRcvLoc] = StTotalLength; + StRcvLoc = (StRcvLoc + 1) % 10; + } +#endif + + + // + // copy this packet into our receive buffer. + // + + deviceContext = Connection->Provider; + addressFile = Connection->AddressFile; + address = addressFile->Address; + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // If we have a previous receive that is pending + // completion, then we need to ignore this frame. + // This may be common on MP. + // + + if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) { + + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; + } + + DataHeader = (PUCHAR)StHeader + sizeof(ST_HEADER); + DataTotalLength = StTotalLength - sizeof(ST_HEADER); + DataIndicatedLength = StIndicatedLength - sizeof(ST_HEADER); + + // + // Initialize this to zero, in case we do not indicate or + // the client does not fill it in. + // + + indicateBytesTransferred = 0; + + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + + // + // check first to see if there is a receive available. If there is, + // use it before doing an indication. + // + + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + + // + // Found a receive, so make it the active one and + // cycle around again. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + TP_REQUEST, Linkage); + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveRequest->Buffer2; + Connection->ReceiveLength = + Connection->CurrentReceiveRequest->Buffer2Length; + Connection->ReceiveByteOffset = 0; + status = STATUS_SUCCESS; + goto NormalReceive; + } + + // + // A receive is not active. Post a receive event. + // + + if (!addressFile->RegisteredReceiveHandler) { + + // + // There is no receive posted to the Connection, and + // no event handler. Set the RECEIVE_WAKEUP bit, so that when a + // receive does become available, it will restart the + // current send. Also send a NoReceive to tell the other + // guy he needs to resynch. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + } + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + // + // Indicate to the user. For BytesAvailable we + // always use DataTotalLength; for BytesIndicated we use + // MIN (DataIndicatedLength, DataTotalLength). + // + // To clarify BytesIndicated, on an Ethernet packet + // which is padded DataTotalLength will be shorter; on an + // Ethernet packet which is not padded and which is + // completely indicated, the two will be equal; and + // on a long Ethernet packet DataIndicatedLength + // will be shorter. + // + + bytesIndicated = DataIndicatedLength; + if (DataTotalLength < bytesIndicated) { + bytesIndicated = DataTotalLength; + } + + status = (*addressFile->ReceiveHandler)( + addressFile->ReceiveHandlerContext, + Connection->Context, + deviceContext->MacInfo.CopyLookahead ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0, // ReceiveFlags + bytesIndicated, + DataTotalLength, // BytesAvailable + &indicateBytesTransferred, + DataHeader, + &irp); + + if (status == STATUS_SUCCESS) { + + // + // The client has accepted some or all of the indicated data in + // the event handler. Update MessageBytesReceived variable in + // the Connection. + // + + Connection->MessageBytesReceived += indicateBytesTransferred; + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + + } else if (status == STATUS_DATA_NOT_ACCEPTED) { + + // + // Either there is no event handler installed (the default + // handler returns this code) or the event handler is not + // able to process the received data at this time. If there + // is a TdiReceive request outstanding on this Connection's + // ReceiveQueue, then we may use it to receive this data. + // If there is no request outstanding, then we must initiate + // flow control at the transport level. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) { + + // + // There is no receive posted to the Connection, and + // the event handler didn't want to accept the incoming + // data. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } else { + + // + // Found a receive, so make it the active one. This will cause + // an NdisTransferData below, so we don't dereference the + // Connection here. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + TP_REQUEST, Linkage); + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveRequest->Buffer2; + Connection->ReceiveLength = + Connection->CurrentReceiveRequest->Buffer2Length; + Connection->ReceiveByteOffset = 0; + } + + } else if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + PTP_REQUEST SpecialIrpRequest; + ULONG SpecialIrpLength; + + // + // The client's event handler has returned an IRP in the + // form of a TdiReceive that is to be associated with this + // data. The request will be installed at the front of the + // ReceiveQueue, and then made the active receive request. + // This request will be used to accept the incoming data, which + // will happen below. + // + + // + // Queueing a receive of any kind causes a Connection reference; + // that's what we've just done here, so make the Connection stick + // around. We create a request to keep a packets outstanding ref + // count for the current IRP; we queue this on the connection's + // receive queue so we can treat it like a normal receive. If + // we can't get a request to describe this irp, we can't keep it + // around hoping for better later; we simple fail it with + // insufficient resources. Note this is only likely to happen if + // we've completely run out of transport memory. + // + + irp->IoStatus.Information = 0; // byte transfer count. + irp->IoStatus.Status = STATUS_PENDING; + irpSp = IoGetCurrentIrpStackLocation (irp); + + ASSERT (irpSp->FileObject->FsContext == Connection); + + SpecialIrpLength = + ((PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters)->ReceiveLength; + + // + // The normal path, for longer receives. + // + + time.HighPart = 0; + time.LowPart = 0; + + status = StCreateRequest ( + irp, + Connection, + REQUEST_FLAGS_CONNECTION | REQUEST_FLAGS_SEND_RCV, + irp->MdlAddress, + ((PTDI_REQUEST_KERNEL_RECEIVE )&irpSp->Parameters)->ReceiveLength, + time, + &SpecialIrpRequest); + + if (!NT_SUCCESS (status)) { + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + Connection->ReceiveByteOffset = 0; + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Connection->IndicationInProgress = FALSE; + + irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If the Connection is stopping, abort this request. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + IoReleaseCancelSpinLock(cancelirql); + StCompleteRequest ( + SpecialIrpRequest, + Connection->Status, + 0); + return STATUS_SUCCESS; // we have consumed the packet + + } + + // + // Insert the request on the head of the connection's + // receive queue, so it can be handled like a normal + // receive. + // + + InsertHeadList (&Connection->ReceiveQueue, &SpecialIrpRequest->Linkage); + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = ((PTDI_REQUEST_KERNEL_RECEIVE )&irpSp->Parameters)->ReceiveLength; + Connection->MessageBytesReceived = indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = SpecialIrpRequest; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + Connection->CurrentReceiveRequest->Owner = ConnectionType; + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (irp->Cancel) { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock,oldirql); + irp->CancelIrql = cancelirql; + StCancelReceive((PDEVICE_OBJECT)deviceContext, irp); + + return STATUS_SUCCESS; + + } else { + + irp->CancelRoutine = StCancelReceive; + + status = STATUS_MORE_PROCESSING_REQUIRED; + + // + // Make a note so we know to release the cancel + // spinlock below. + // + + CancelSpinLockHeld = TRUE; + + } + + } else { + + // + // An unknown return code has been returned by the + // client's event handler. This is a client programming + // error. Because this can only occur when kernel-mode + // clients have been coded incorrectly, we should beat + // him with a stick. We have to do SOMETHING, or this + // Connection will HANG. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } + + } else { + + // + // A receive is active, set the status to show + // that so far. + // + + status = STATUS_SUCCESS; + + } + + +NormalReceive:; + + // + // NOTE: The connection spinlock is held here. + // + // We should only get through here if a receive is active + // and we have not released the lock since checking or + // making one active. + // + + ASSERT(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE); + + // + // The status should be SUCCESS (we found an active receive) + // or MORE_PROCESSING_REQUIRED (we made a new receive active). + // + + ASSERT ((status == STATUS_SUCCESS) || (status == STATUS_MORE_PROCESSING_REQUIRED)); + + destBytes = Connection->ReceiveLength - Connection->MessageBytesReceived; + StReferenceRequest ("Transfer Data", Connection->CurrentReceiveRequest); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + if (CancelSpinLockHeld) { + IoReleaseCancelSpinLock (cancelirql); + } + + // + // get a packet for the coming transfer + // + + linkage = ExInterlockedPopEntryList( + &deviceContext->ReceivePacketPool, + &deviceContext->Interlock); + + if (linkage != NULL) { + ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + (VOID)InterlockedIncrement((PLONG)&deviceContext->ReceivePacketExhausted); + + StDereferenceRequest ("No receive packet", Connection->CurrentReceiveRequest); + + // We could not get a receive packet. + // + + Connection->IndicationInProgress = FALSE; + return status; + } + + // + // Initialize the receive packet. + // + + receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved); + receiveTag->PacketType = TYPE_AT_INDICATE; + receiveTag->Connection = Connection; + receiveTag->TransferDataPended = TRUE; + + + // + // Determine how much data remains to be transferred. + // + + BytesToTransfer = DataTotalLength - indicateBytesTransferred; + ASSERT (BytesToTransfer >= 0); + + if (destBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + receiveTag->EndOfMessage = FALSE; + receiveTag->CompleteReceive = TRUE; + BytesToTransfer = destBytes; + + } else if (destBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + receiveTag->EndOfMessage = Last; + receiveTag->CompleteReceive = TRUE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + receiveTag->EndOfMessage = Last; + receiveTag->CompleteReceive = Last; + + } + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + Connection->IndicationInProgress = FALSE; + receiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + receiveTag->TransferDataPended = FALSE; + StTransferDataComplete ( + deviceContext, + ndisPacket, + NDIS_STATUS_SUCCESS, + 0); + + return status; + } + + // + // describe the right part of the user buffer to NDIS. If we can't get + // the mdl for the packet, drop it. Bump the request reference count + // so that we know we need to hold open receives until the NDIS transfer + // data requests complete. + // + + SavedCurrentMdl = Connection->CurrentReceiveMdl; + SavedCurrentByteOffset = Connection->ReceiveByteOffset; + + if ((Connection->ReceiveByteOffset == 0) && + (receiveTag->CompleteReceive)) { + + // + // If we are transferring into the beginning of + // the current MDL, and we will be completing the + // receive after the transfer, then we don't need to + // copy it. + // + + ndisBuffer = (PNDIS_BUFFER)Connection->CurrentReceiveMdl; + bufferChainLength = BytesToTransfer; + Connection->CurrentReceiveMdl = NULL; + Connection->ReceiveByteOffset = 0; + receiveTag->AllocatedNdisBuffer = FALSE; + tmpstatus = STATUS_SUCCESS; + + } else { + + tmpstatus = BuildBufferChainFromMdlChain ( + deviceContext->NdisBufferPoolHandle, + Connection->CurrentReceiveMdl, + Connection->ReceiveByteOffset, + BytesToTransfer, + &ndisBuffer, + &Connection->CurrentReceiveMdl, + &Connection->ReceiveByteOffset, + &bufferChainLength); + + receiveTag->AllocatedNdisBuffer = TRUE; + + } + + + if ((!NT_SUCCESS (tmpstatus)) || (bufferChainLength != BytesToTransfer)) { + + DumpData[0] = bufferChainLength; + DumpData[1] = BytesToTransfer; + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + tmpstatus, + NULL, + 2, + DumpData); + + StDereferenceRequest ("No MDL chain", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + + Connection->IndicationInProgress = FALSE; + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + return status; + } + + NdisChainBufferAtFront (ndisPacket, ndisBuffer); + + // + // set up async return status so we can tell when it has happened; + // can never get return of NDIS_STATUS_PENDING in synch completion routine + // for NdisTransferData, so we know it has completed when this status + // changes + // + + receiveTag->NdisStatus = NDIS_STATUS_PENDING; + + // + // update the number of bytes received; OK to do this + // unprotected since IndicationInProgress is still FALSE. + // + // + + Connection->MessageBytesReceived += BytesToTransfer; + + // + // We have now updated all the connection counters (BUG, + // assuming the TransferData will succeed) and this + // packet's location in the request is secured, so we + // can be reentered. + // + + Connection->IndicationInProgress = FALSE; + + NdisTransferData ( + &ndisStatus, + deviceContext->NdisBindingHandle, + ReceiveContext, + sizeof (ST_HEADER) + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + // + // handle the various completion codes + // + + switch (ndisStatus) { + + case NDIS_STATUS_SUCCESS: + + receiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + if (ndisBytesTransferred != BytesToTransfer) { // Did we get the entire packet? + + DumpData[0] = ndisBytesTransferred; + DumpData[1] = BytesToTransfer; + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + ndisStatus, + NULL, + 2, + DumpData); + + if (receiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + while (ndisBuffer != NULL) { + NdisFreeBuffer (ndisBuffer); + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + } + } else { + NdisReinitializePacket (ndisPacket); + } + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + StDereferenceRequest ("Bad byte count", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + Connection->MessageBytesReceived -= BytesToTransfer; + + return status; + } + + // + // deallocate the buffers and such that we've used if at indicate + // + + receiveTag->TransferDataPended = FALSE; + + StTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + + // + // Because TransferDataPended stays TRUE, this reference will + // be removed in TransferDataComplete. It is OK to do this + // now, even though TransferDataComplete may already have been + // called, because we also hold the ProcessIIndicate reference + // so there will be no "bounce". + // + + StReferenceConnection ("TransferData pended", Connection); + break; + + default: + + // + // Something broke; certainly we'll never get NdisTransferData + // asynch completion. + // + // BUGBUG: The driver should recover from this situation. + // + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + ndisStatus, + NULL, + 0, + NULL); + + if (receiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + while (ndisBuffer != NULL) { + NdisFreeBuffer (ndisBuffer); + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + } + } else { + NdisReinitializePacket (ndisPacket); + } + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + StDereferenceRequest ("TransferData failed", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + Connection->MessageBytesReceived -= BytesToTransfer; + + return status; + } // switch ndisStatus + + return status; // which only means we've dealt with the packet + +} /* ProcessIIndicate */ diff --git a/private/ntos/tdi/st/ind.c b/private/ntos/tdi/st/ind.c new file mode 100644 index 000000000..68dc3bffb --- /dev/null +++ b/private/ntos/tdi/st/ind.c @@ -0,0 +1,829 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ind.c + +Abstract: + + This module contains code which implements the indication handler + for the NT Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +NDIS_STATUS +StReceiveIndication ( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + PDEVICE_CONTEXT DeviceContext; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + UINT RealPacketSize; + PST_HEADER StHeader; + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + RealPacketSize = 0; + + // + // Obtain the packet length; this may optionally adjust + // the lookahead buffer forward if the header we wish + // to remove spills over into what the MAC considers + // data. If it determines that the header is not + // valid, it keeps RealPacketSize at 0. + // + + MacReturnPacketLength( + &DeviceContext->MacInfo, + HeaderBuffer, + HeaderBufferSize, + PacketSize, + &RealPacketSize + ); + + if (RealPacketSize < 2) { + return NDIS_STATUS_NOT_RECOGNIZED; + } + + // + // We've negotiated at least a contiguous DLC header passed back in the + // lookahead buffer. Check it to see if we want this packet. + // + + StHeader = (PST_HEADER)LookaheadBuffer; + + if (StHeader->Signature != ST_SIGNATURE) { + return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed. + } + + + // + // Check that the packet is not too long. + // + + if (PacketSize > DeviceContext->MaxReceivePacketSize) { +#if DBG + StPrint2("StReceiveIndication: Ignoring packet length %d, max %d\n", + PacketSize, DeviceContext->MaxReceivePacketSize); +#endif + return NDIS_STATUS_NOT_RECOGNIZED; + } + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceAddressBuffer, + &SourceAddress + ); + + + return StGeneralReceiveHandler( + DeviceContext, + ReceiveContext, + SourceAddress, + HeaderBuffer, // header + RealPacketSize, // total data length in packet + (PST_HEADER)LookaheadBuffer, // lookahead data + LookaheadBufferSize // lookahead data length + ); + +} + + +NDIS_STATUS +StGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PST_HEADER StHeader, + IN UINT StSize + ) + +/*++ + +Routine Description: + + This routine receives control from StReceiveIndication. + It continues the processing of indicated data. + + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + DeviceContext - The device context of this adapter. + + ReceiveContext - A magic cookie for the MAC. + + SourceAddress - The source address of the packet. + + HeaderBuffer - pointer to the packet header. + + PacketSize - Overall size of the packet (not including header). + + DlcHeader - Points to the DLC header of the packet. + + DlcSize - The length of the packet indicated, starting from DlcHeader. + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + + KIRQL oldirql; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + PSINGLE_LIST_ENTRY linkage; + UINT BytesTransferred; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PTP_ADDRESS DatagramAddress; + UINT NdisBufferLength; + PTP_CONNECTION Connection; + PVOID BufferPointer; + + + INCREMENT_COUNTER (DeviceContext, PacketsReceived); + + Status = STATUS_SUCCESS; // assume no further processing required + + + // + // See what type of frame this is. + // + + if ((StHeader->Command == ST_CMD_CONNECT) || + (StHeader->Command == ST_CMD_DATAGRAM)) { + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceRouting, + &SourceRoutingLength); + + Status = StProcessConnectionless ( + DeviceContext, + SourceAddress, + StHeader, + StSize, + SourceRouting, + SourceRoutingLength, + &DatagramAddress); + + } else if ((StHeader->Command == ST_CMD_INFORMATION) || + (StHeader->Command == ST_CMD_DISCONNECT)) { + + // + // If successful this adds a connection reference. + // + + if (!(Connection = StFindConnection(DeviceContext, StHeader->Destination, StHeader->Source))) { + return NDIS_STATUS_NOT_RECOGNIZED; + } + + + if (StHeader->Command == ST_CMD_INFORMATION) { + + Status = StProcessIIndicate ( + Connection, + StHeader, + StSize, + PacketSize, + ReceiveContext, + (BOOLEAN)((StHeader->Flags & ST_FLAGS_LAST) != 0) + ); + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + StDereferenceConnection ("Information done", Connection); + } else { + Status = STATUS_SUCCESS; + } + + } else { + + StStopConnection (Connection, STATUS_REMOTE_DISCONNECT); + StDereferenceConnection ("Disconnect done", Connection); + Status = STATUS_SUCCESS; + + } + + } else { + + // + // An unrecognized frame. + // + + Status = STATUS_SUCCESS; + + } + + + // + // If the above routines return success, the packet has been processed + // and can be discarded. If they return anything else, the packet needs + // to be copied to local storage for handling in a more lesurely + // fashion. + // + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + return NDIS_STATUS_SUCCESS; + } + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + (VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceivePacketExhausted); + + return NDIS_STATUS_RESOURCES; + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceiveBufferPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + } else { + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + (VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceiveBufferExhausted); + + return NDIS_STATUS_RESOURCES; + } + + NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize); + NdisChainBufferAtFront (NdisPacket, (PNDIS_BUFFER)BufferTag->NdisBuffer); + + // + // DatagramAddress has a reference added already. + // + + BufferTag->Address = DatagramAddress; + + // + // set up async return status so we can tell when it has happened; + // can never get return of NDIS_STATUS_PENDING in synch completion routine + // for NdisTransferData, so we know it has completed when this status + // changes + // + + ReceiveTag->NdisStatus = NDIS_STATUS_PENDING; + ReceiveTag->PacketType = TYPE_AT_COMPLETE; + + ExInterlockedInsertTailList( + &DeviceContext->ReceiveInProgress, + &ReceiveTag->Linkage, + &DeviceContext->SpinLock); + + // + // receive packet is mapped at initalize + // + + NdisTransferData ( + &NdisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + 0, + PacketSize, + NdisPacket, + &BytesTransferred); + + // + // handle the various error codes + // + + switch (NdisStatus) { + case NDIS_STATUS_SUCCESS: // received packet + ReceiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + if (BytesTransferred == PacketSize) { // Did we get the entire packet? + return NDIS_STATUS_SUCCESS; + } + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + return NDIS_STATUS_SUCCESS; + break; + + default: // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status... + break; + } + + // + // receive failed, for some reason; cleanup and fail return + // + + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + RemoveEntryList (&ReceiveTag->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD ( + BufferPointer, + BUFFER_TAG, + Buffer[0] + ); + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value + + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + &BufferTag->Linkage, + &DeviceContext->Interlock); + + if (DatagramAddress) { + StDereferenceAddress ("DG TransferData failed", DatagramAddress); + } + + return NDIS_STATUS_FAILURE; + +} // StReceiveIndication + + + +VOID +StTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to start stripping buffers from the receive queue. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PRECEIVE_PACKET_TAG ReceiveTag; + PTP_CONNECTION Connection; + PNDIS_BUFFER NdisBuffer; + KIRQL oldirql, cancelirql; + + // + // Put the NDIS status into a place we can use in packet processing. + // Note that this complete indication may be occuring during the call + // to NdisTransferData in the receive indication. + // + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + // + // note that the processing below depends on having only one packet + // transfer outstanding at a time. NDIS is supposed to guarentee this. + // + + switch (ReceiveTag->PacketType) { + + case TYPE_AT_COMPLETE: // normal handling + ReceiveTag->NdisStatus = NdisStatus; + break; + + case TYPE_AT_INDICATE: + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + Connection = ReceiveTag->Connection; + + // + // The transfer for this packet is complete. Was it successful?? + // + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ULONG DumpData[1]; + DumpData[0] = BytesTransferred; + + StWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 603, + NdisStatus, + NULL, + 1, + DumpData); + + // + // Drop the packet. BUGBUG: The driver should recover + // from this, but this transport has no way to cause + // the remote to resend. + // + + } + + // + // Now dereference the request to say we've got no more local + // references to the memory owned by it. + // + + Connection->CurrentReceiveRequest->IoRequestPacket->IoStatus.Information += BytesTransferred; + StDereferenceRequest ("TransferData complete", Connection->CurrentReceiveRequest); + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (ReceiveTag->CompleteReceive) { + + if (ReceiveTag->EndOfMessage) { + + // + // The messages has been completely received, ack it. + // + // We set DEFERRED_ACK and DEFERRED_NOT_Q here, which + // will cause an ack to be piggybacked if any data is + // sent during the call to CompleteReceive. If this + // does not happen, then we will call AcknowledgeDataOnlyLast + // which will will send a DATA ACK or queue a request for + // a piggyback ack. We do this *after* calling CompleteReceive + // so we know that we will complete the receive back to + // the client before we ack the data, to prevent the + // next receive from being sent before this one is + // completed. + // + + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + Connection->Flags2 |= CONNECTION_FLAGS2_RC_PENDING; + + } else { + + // + // If there is a receive posted, make it current and + // send a receive outstanding. + // + + ActivateReceive (Connection); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + } + + // + // NOTE: This releases the cancel and connection locks. + // + + CompleteReceive (Connection, ReceiveTag->EndOfMessage, oldirql, cancelirql); + + } + + // + // dereference the connection to say we've done the I frame processing. + // This reference was done before calling NdisTransferData. + // + + if (ReceiveTag->TransferDataPended) { + StDereferenceConnection("TransferData done", Connection); + } + + + // + // rip all of the NDIS_BUFFERs we've used off the chain and return them. + // + + if (ReceiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + } + } else { + NdisReinitializePacket (NdisPacket); + } + + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + default: + + break; + } + + return; + +} /* StTransferDataComplete */ + + +VOID +StReceiveComplete ( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + ST uses the DeviceContext for this parameter. + +Return Value: + + None + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + KIRQL oldirql, oldirql1; + PLIST_ENTRY linkage; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + UINT NdisBufferLength; + PVOID BufferPointer; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PTP_ADDRESS Address; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + + + DeviceContext = (PDEVICE_CONTEXT) BindingContext; + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) { + + linkage = ExInterlockedRemoveHeadList( + &DeviceContext->IrpCompletionQueue, + &DeviceContext->SpinLock); + + if (linkage != NULL) { + + Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) { + Connection->Flags2 &= ~CONNECTION_FLAGS2_RC_PENDING; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + StDereferenceConnection ("receive completed", Connection); + + } else { + + // + // ExInterlockedRemoveHeadList returned NULL, so don't + // bother looping back. + // + + break; + + } + + } + + + // + // Packetize all waiting connections + // + + if (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + PacketizeConnections (DeviceContext); + + } + + + // + // Get every waiting packet, in order... + // + + + if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress); + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0]); + + // + // NdisTransferData may have failed at async completion; check and + // see. If it did, then we discard this packet. If we're still waiting + // for transfer to complete, go back to sleep and hope (no guarantee!) + // we get waken up later. + // + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + if (ReceiveTag->NdisStatus == NDIS_STATUS_PENDING) { + InsertHeadList (&DeviceContext->ReceiveInProgress, linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (ReceiveTag->NdisStatus != NDIS_STATUS_SUCCESS) { + goto FreePacket; // skip the packet, continue with while loop + } + + NdisQueryPacket (NdisPacket, NULL, NULL, &NdisBuffer, NULL); + + // + // Have a packet. Since I allocated the storage for it, I know it's + // virtually contiguous and can treat it that way, which I will + // henceforth. + // + + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + + // + // Determine what address this is for, which is stored + // in the buffer tag header. + // + + BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]); + Address = BufferTag->Address; + + // + // Process the frame as a UI frame; only datagrams should + // be processed here. If Address is NULL then this datagram + // is not needed for any bound address and should be given + // to RAS only. + // + + ASSERT (Address != NULL); + + // + // Indicate it or complete posted datagrams. + // + + Status = StIndicateDatagram ( + DeviceContext, + Address, + BufferPointer, + NdisBufferLength); + + + // + // Dereference the address. + // + + StDereferenceAddress ("Datagram done", Address); + + // + // Finished with packet; return to pool. + // + +FreePacket:; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + &BufferTag->Linkage, + &DeviceContext->Interlock); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + } // if queue not empty + + return; + +} /* StReceiveComplete */ + diff --git a/private/ntos/tdi/st/info.c b/private/ntos/tdi/st/info.c new file mode 100644 index 000000000..2a2122753 --- /dev/null +++ b/private/ntos/tdi/st/info.c @@ -0,0 +1,865 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + info.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define StGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + +// +// Local functions used to satisfy various requests. +// + +VOID +StStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ); + +VOID +StStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ); + +VOID +StStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ); + + +NTSTATUS +StTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + PVOID adapterStatus; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PTA_NETBIOS_ADDRESS broadcastAddress; + PTDI_PROVIDER_STATISTICS ProviderStatistics; + PTDI_CONNECTION_INFO ConnectionInfo; + ULONG TargetBufferLength; + LARGE_INTEGER timeout = {0,0}; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS TaAddressBuffer; + } AddressInfo; + ULONG NamesWritten, TotalNameCount, BytesWritten; + PLIST_ENTRY p; + KIRQL oldirql; + BOOLEAN Truncated; + BOOLEAN UsedConnection; + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&irpSp->Parameters; + + switch (query->QueryType) { + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = irpSp->FileObject->FsContext; + + status = StVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + ConnectionInfo = ExAllocatePool ( + NonPagedPool, + sizeof (TDI_CONNECTION_INFO)); + + if (ConnectionInfo == NULL) { + + PANIC ("StQueryInfo: Cannot allocate connection info!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TDI_CONNECTION_INFO), 6); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + status = Connection->Status; + ExFreePool (ConnectionInfo); + + } else if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + status = STATUS_INVALID_CONNECTION; + ExFreePool (ConnectionInfo); + + } else { + + RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + + // + // Fill in connection information here. + // + + status = TdiCopyBufferToMdl ( + (PVOID)ConnectionInfo, + 0L, + sizeof(TDI_CONNECTION_INFO), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ConnectionInfo); + } + + StDereferenceConnection ("query connection info", Connection); + + break; + + case TDI_QUERY_ADDRESS_INFO: + + // + // Information about an address, can also be queried on a + // connection object to get information about its address. + // + + if (irpSp->FileObject->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = irpSp->FileObject->FsContext; + + status = StVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + + UsedConnection = FALSE; + + } else if (irpSp->FileObject->FsContext2 == (PVOID)TDI_CONNECTION_FILE) { + + Connection = irpSp->FileObject->FsContext; + + status = StVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + AddressFile = Connection->AddressFile; + + UsedConnection = TRUE; + + } else { + + return STATUS_INVALID_ADDRESS; + + } + + Address = AddressFile->Address; + + TdiBuildNetbiosAddress( + Address->NetworkName->NetbiosName, + (BOOLEAN)(Address->Flags & ADDRESS_FLAGS_GROUP ? TRUE : FALSE), + &AddressInfo.TaAddressBuffer); + + // + // Count the active addresses. + // + + AddressInfo.ActivityCount = 0; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + ++AddressInfo.ActivityCount; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + status = TdiCopyBufferToMdl ( + &AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (UsedConnection) { + + StDereferenceConnection ("query address info", Connection); + + } else { + + StDereferenceAddress ("query address info", Address); + + } + + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + broadcastAddress = ExAllocatePool ( + NonPagedPool, + sizeof (TA_NETBIOS_ADDRESS)); + if (broadcastAddress == NULL) { + PANIC ("StQueryInfo: Cannot allocate broadcast address!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TA_NETBIOS_ADDRESS), 2); + status = STATUS_INSUFFICIENT_RESOURCES; + } else { + + broadcastAddress->TAAddressCount = 1; + broadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + broadcastAddress->Address[0].AddressLength = 0; + + Irp->IoStatus.Information = + sizeof (broadcastAddress->TAAddressCount) + + sizeof (broadcastAddress->Address[0].AddressType) + + sizeof (broadcastAddress->Address[0].AddressLength); + + status = TdiCopyBufferToMdl ( + (PVOID)broadcastAddress, + 0L, + Irp->IoStatus.Information, + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (broadcastAddress); + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(DeviceContext->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + StGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + if (TargetBufferLength < sizeof(TDI_PROVIDER_STATISTICS) + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))) { + + Irp->IoStatus.Information = 0; + status = STATUS_BUFFER_OVERFLOW; + + } else { + + ProviderStatistics = ExAllocatePool( + NonPagedPool, + sizeof(TDI_PROVIDER_STATISTICS) + + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))); + + if (ProviderStatistics == NULL) { + + PANIC ("StQueryInfo: Cannot allocate provider statistics!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TDI_PROVIDER_STATISTICS), 7); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + StStoreProviderStatistics (DeviceContext, ProviderStatistics); + + status = TdiCopyBufferToMdl ( + (PVOID)ProviderStatistics, + 0L, + sizeof(TDI_PROVIDER_STATISTICS) + + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ProviderStatistics); + } + + } + + break; + + case TDI_QUERY_SESSION_STATUS: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADAPTER_STATUS: + + StGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + // + // Determine if this is a local or remote query. It is + // local if there is no remote address specific at all, + // or if it is equal to our reserved address. + // + + if ((query->RequestConnectionInformation != NULL) && + (!RtlEqualMemory( + ((PTA_NETBIOS_ADDRESS)(query->RequestConnectionInformation->RemoteAddress))-> + Address[0].Address[0].NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + // + // Remote, not supported here. + // + + status = STATUS_NOT_IMPLEMENTED; + + } else { + + // + // Local. + // + + adapterStatus = ExAllocatePool ( + NonPagedPool, + TargetBufferLength); + + if (adapterStatus == NULL) { + PANIC("StQueryInfo: PANIC! Could not allocate adapter status buffer\n"); + StWriteResourceErrorLog (DeviceContext, TargetBufferLength, 3); + return STATUS_INSUFFICIENT_RESOURCES; + } + + StStoreAdapterStatus ( + DeviceContext, + NULL, + 0, + adapterStatus); + + StStoreNameBuffers ( + DeviceContext, + (PUCHAR)adapterStatus + sizeof(ADAPTER_STATUS), + TargetBufferLength - sizeof(ADAPTER_STATUS), + 0, + &NamesWritten, + &TotalNameCount, + &Truncated); + + ((PADAPTER_STATUS)adapterStatus)->name_count = (WORD)TotalNameCount; + + BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); + + status = TdiCopyBufferToMdl ( + adapterStatus, + 0, + BytesWritten, + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (Truncated) { + status = STATUS_BUFFER_OVERFLOW; + } + + ExFreePool (adapterStatus); + + } + + break; + + case TDI_QUERY_FIND_NAME: + + // + // Find name, not supported here. + // + + status = STATUS_NOT_IMPLEMENTED; + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* StTdiQueryInformation */ + +// +// Quick macros, assumes DeviceContext and ProviderStatistics exist. +// + +#define STORE_RESOURCE_STATS_1(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## MaxInUse; \ + if (DeviceContext->_ResourceName ## Samples > 0) { \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Total / DeviceContext->_ResourceName ## Samples; \ + } else { \ + RStats->AverageResourceUsed = 0; \ + } \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + +#define STORE_RESOURCE_STATS_2(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + + +VOID +StStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ) + +/*++ + +Routine Description: + + This routine writes the TDI_PROVIDER_STATISTICS structure + from the device context into ProviderStatistics. + +Arguments: + + DeviceContext - a pointer to the device context. + + ProviderStatistics - The buffer that holds the result. It is assumed + that it is long enough. + +Return Value: + + None. + +--*/ + +{ + + ProviderStatistics->Version = 0x0100; + + // + // Copy all the statistics from OpenConnections to WastedSpace + // Packets in one move. + // + + RtlCopyMemory( + (PVOID)&(ProviderStatistics->OpenConnections), + (PVOID)&(DeviceContext->OpenConnections), + sizeof(TDI_PROVIDER_STATISTICS)); + + // + // Copy the resource statistics. + // + + ProviderStatistics->NumberOfResources = ST_TDI_RESOURCES; + + STORE_RESOURCE_STATS_1 (0, 12, Address); + STORE_RESOURCE_STATS_1 (1, 13, AddressFile); + STORE_RESOURCE_STATS_1 (2, 14, Connection); + STORE_RESOURCE_STATS_1 (3, 15, Request); + + STORE_RESOURCE_STATS_2 (4, 22, Packet); + STORE_RESOURCE_STATS_2 (5, 23, ReceivePacket); + STORE_RESOURCE_STATS_2 (6, 24, ReceiveBuffer); + +} /* StStoreProviderStatistics */ + + +VOID +StStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ) + +/*++ + +Routine Description: + + This routine writes the ADAPTER_STATUS structure for the + device context into StatusBuffer. The name_count field is + initialized to zero; StStoreNameBuffers is used to write + name buffers. + +Arguments: + + DeviceContext - a pointer to the device context. + + SourceRouting - If this is a remote request, the source + routing information from the frame. + + SourceRoutingLength - The length of SourceRouting. + + StatusBuffer - The buffer that holds the result. It is assumed + that it is at least sizeof(ADAPTER_STATUS) bytes long. + +Return Value: + + None. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus = (PADAPTER_STATUS)StatusBuffer; + UINT MaxUserData; + + RtlZeroMemory ((PVOID)AdapterStatus, sizeof(ADAPTER_STATUS)); + + RtlCopyMemory (AdapterStatus->adapter_address, DeviceContext->LocalAddress.Address, 6); + AdapterStatus->rev_major = 0x03; + + switch (DeviceContext->MacInfo.MediumType) { + case NdisMedium802_5: AdapterStatus->adapter_type = 0xff; break; + default: AdapterStatus->adapter_type = 0xfe; break; + } + + AdapterStatus->frmr_recv = 0; + AdapterStatus->frmr_xmit = 0; + + AdapterStatus->recv_buff_unavail = (WORD)(DeviceContext->ReceivePacketExhausted + DeviceContext->ReceiveBufferExhausted); + AdapterStatus->xmit_buf_unavail = (WORD)DeviceContext->PacketExhausted; + + AdapterStatus->xmit_success = (WORD)(DeviceContext->IFramesSent - DeviceContext->IFramesResent); + AdapterStatus->recv_success = (WORD)DeviceContext->IFramesReceived; + AdapterStatus->iframe_recv_err = (WORD)DeviceContext->IFramesRejected; + AdapterStatus->iframe_xmit_err = (WORD)DeviceContext->IFramesResent; + + AdapterStatus->t1_timeouts = 0; + AdapterStatus->ti_timeouts = 0; + AdapterStatus->xmit_aborts = 0; + + + AdapterStatus->free_ncbs = 0xffff; + AdapterStatus->max_cfg_ncbs = 0xffff; + AdapterStatus->max_ncbs = 0xffff; + AdapterStatus->pending_sess = (WORD)DeviceContext->OpenConnections; + AdapterStatus->max_cfg_sess = 0xffff; + AdapterStatus->max_sess = 0xffff; + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->MaxSendPacketSize, + &MaxUserData); + + AdapterStatus->max_dgram_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); + AdapterStatus->max_sess_pkt_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); + + return; + +} /* StStoreAdapterStatus */ + + +VOID +StStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ) + +/*++ + +Routine Description: + + This routine writes NAME_BUFFER structures for the + device context into NameBuffer. It can skip a specified + number of names at the beginning, and returns the number + of names written into NameBuffer. If a name will only + partially fit, it is not written. + +Arguments: + + DeviceContext - a pointer to the device context. + + NameBuffer - The buffer to write the names into. + + NameBufferLength - The length of NameBuffer. + + NamesToSkip - The number of names to skip. + + NamesWritten - Returns the number of names written. + + TotalNameCount - Returns the total number of names available, + if specified. + + Truncated - More names are available than were written. + +Return Value: + + None. + +--*/ + +{ + + ULONG NameCount = 0; + ULONG BytesWritten = 0; + KIRQL oldirql; + PLIST_ENTRY p; + PNAME_BUFFER NameBuffer = (PNAME_BUFFER)Buffer; + PTP_ADDRESS address; + + + // + // Spin through the address list for this device context. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + // + // Check if we are still skipping. + // + + if (NameCount < NamesToSkip) { + ++NameCount; + continue; + } + + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > BufferLength) { + break; + } + + RtlCopyMemory( + NameBuffer->name, + address->NetworkName->NetbiosName, + NETBIOS_NAME_LENGTH); + + ++NameCount; + NameBuffer->name_num = (UCHAR)NameCount; + + NameBuffer->name_flags = REGISTERED; + if (address->Flags & ADDRESS_FLAGS_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // BUGBUG: name_flags should be done more accurately. + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + *NamesWritten = NameBuffer - (PNAME_BUFFER)Buffer; + + if (p == &DeviceContext->AddressDatabase) { + + *Truncated = FALSE; + if (ARGUMENT_PRESENT(TotalNameCount)) { + *TotalNameCount = NameCount; + } + + } else { + + *Truncated = TRUE; + + // + // If requested, continue through the list and count + // all the addresses. + // + + if (ARGUMENT_PRESENT(TotalNameCount)) { + + for ( ; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address, since we count it no matter what. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + ++NameCount; + + } + + *TotalNameCount = NameCount; + + } + + } + + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + return; + +} /* StStoreNameBuffers */ + + +NTSTATUS +StTdiSetInformation( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Irp); + + return STATUS_NOT_IMPLEMENTED; + +} /* StTdiQueryInformation */ + diff --git a/private/ntos/tdi/st/makefile b/private/ntos/tdi/st/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/st/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/st/oemnxpts.inf b/private/ntos/tdi/st/oemnxpts.inf new file mode 100644 index 000000000..da0cdaab7 --- /dev/null +++ b/private/ntos/tdi/st/oemnxpts.inf @@ -0,0 +1,446 @@ +[Identification] + OptionType = NetTransport +[Options] + ST +[FileConstants] +UtilityInf = "UTILITY.INF" +subroutineinf = "SUBROUTN.INF" +SoftwareType = "transport" +Exit_Code = 0 +NetEventDLL = "%SystemRoot%\System32\netevent.dll" +Manufacturer = "Microsoft" +ProductMajorVersion = "3" +ProductMinorVersion = "1" +ProductVersion = $(ProductMajorVersion)"."$(ProductMinorVersion) +ProductSoftwareName = "St" +ProductSoftwareImagePath = "\SystemRoot\System32\drivers\st.sys" +NetRuleSoftwareType = "st netBiosTransport" +NetRuleSoftwareClass = {"netBiosTransport"} +NetRuleSoftwareUse = $(SoftwareType)" yes yes" +NetRuleSoftwareBindForm = """St"" yes yes simple" +ProductKeyName = $(!NTN_SoftwareBase)"\"$(Manufacturer)"\"$(ProductSoftwareName)"\CurrentVersion" +ParamKeyName = $(!NTN_ServiceBase)"\"$(ProductSoftwareName)"\Parameters" +[GeneralConstants] +from = "" +to = "" +ExitCodeOk = 0 +ExitCodeCancel = 1 +ExitCodeFatal = 2 +KeyNull = "" +MAXIMUM_ALLOWED = 33554432 +RegistryErrorIndex = NO_ERROR +KeyProduct = "" +KeyParameters = "" +TRUE = 1 +FALSE = 0 +NoTitle = 0 +ExitState = "Active" +OldVersionExisted = $(FALSE) +DriverPath = $(!STF_NTPATH)\drivers +[date] + Now = {} ? $(!LIBHANDLE) GetSystemDate +[Identify] + read-syms Identification + set Status = STATUS_SUCCESSFUL + set Identifier = $(OptionType) + set Media = #("Source Media Descriptions", 1, 1) + Return $(Status) $(Identifier) $(Media) +[ReturnOptions] + set Status = STATUS_FAILED + set OptionList = {} + set OptionTextList = {} + set LanguageList = ^(LanguagesSupported, 1) + Ifcontains(i) $($0) in $(LanguageList) + goto returnoptions + else + set Status = STATUS_NOLANGUAGE + goto finish_ReturnOptions + endif +returnoptions = + + set OptionList = ^(Options, 1) + set OptionTextList = ^(OptionsText$($0), 1) + set Status = STATUS_SUCCESSFUL +finish_ReturnOptions = + + Return $(Status) $(OptionList) $(OptionTextList) +[InstallOption] + set Option = $($1) + set SrcDir = $($2) + set AddCopy = $($3) + set DoCopy = $($4) + set DoConfig = $($5) + set LanguageList = ^(LanguagesSupported, 1) + Ifcontains(i) $($0) NOT-IN $(LanguageList) + Return STATUS_NOLANGUAGE + endif + Debug-Output "OEMNXPNB.INF: STF_CWDDIR is: "$(!STF_CWDDIR) + Debug-Output "OEMNXPNB.INF: STF_LANGUAGE is: "$(!STF_LANGUAGE) + set-subst LF = "\n" + read-syms GeneralConstants + read-syms FileConstants + read-syms DialogConstants$(!STF_LANGUAGE) + ifstr(i) $(!NTN_Origination) == "NCPA" + set Continue = $(OK) + endif + read-syms FileConstants$(!STF_LANGUAGE) + detect date + set-title $(FunctionTitle) + set to = Begin + set from = Begin + set CommonStatus = STATUS_SUCCESSFUL + EndWait +Begin = + + Ifstr(i) $(!NTN_InstallMode) == deinstall + set StartLabel = removeadapter + else-Ifstr(i) $(!NTN_InstallMode) == Update + set StartLabel = UpgradeSoftware + else-Ifstr(i) $(!NTN_InstallMode) == bind + set StartLabel = bindingadapter + else-Ifstr(i) $(!NTN_InstallMode) == configure + Shell $(UtilityInf),RegistryErrorString,CANNOT_CONFIGURE_SOFTWARE + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "OEMNXPNB.INF: ShellCode error: cannot get an error string." + goto ShellCodeError + endif + set Error = $($R0) + set from = end + set to = end + goto nonfatalinfo + else + set StartLabel = installadapter + endif + set RadioDefault = 2 + set RadioIn = {$(RadioDefault)} + set Size = 2 + set from = $(fatal) + set to = $(fatal) + goto $(StartLabel) +installadapter = + + OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct + Ifstr $(KeyProduct) != $(KeyNull) + CloseRegKey $(KeyProduct) + Shell $(UtilityInf), VerExistedDlg, $(ProductSoftwareTitle),+ + $(ProductVersion) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error: cannot get an error string." + goto ShellCodeError + endif + goto end + endif + CloseRegKey $(KeyProduct) + goto installproduct +installproduct = + + StartWait + ifint $(OldVersionExisted) == $(FALSE) + Ifstr(i) $(DoCopy) == "YES" + Shell $(UtilityInf), DoAskSource, $(!STF_CWDDIR), $(SrcDir) YES + Ifint $($ShellCode) != $(!SHELL_CODE_OK) + Goto ShellCodeError + Else-Ifstr(i) $($R0) == STATUS_FAILED + Shell $(UtilityInf) RegistryErrorString "ASK_SOURCE_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + Goto fatal + Else-Ifstr(i) $($R0) == STATUS_USERCANCEL + Goto successful + Endif + Set SrcDir = $($R1) + Endif + install "Install-Option" + ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS + Shell $(UtilityInf) RegistryErrorString "UNABLE_COPY_FILE" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + goto fatal + endif + set OEM_ABANDON_ON = TRUE + Shell $(UtilityInf), AddSoftwareComponent, $(Manufacturer), + + $(ProductSoftwareName), + + $(ProductSoftwareName), + + $(ProductSoftwareDisplayName), $(STF_CONTEXTINFNAME), + + $(ProductSoftwareImagePath), "kernel", "TDI", {}, "",+ + $(NetEventDLL) + set RegistryErrorIndex = $($R0) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + EndWait + CloseRegKey $($R1) + CloseRegKey $($R2) + CloseRegKey $($R3) + CloseRegKey $($R4) + CloseRegKey $($R5) + goto fatalRegistry + endif + Set SoftProductKey = $($R1) + Set SoftNetRuleKey = $($R2) + Set SoftServiceKey = $($R3) + set KeyParameters = $($R4) + Set SoftLinkageKey = $($R5) + set NewValueList = {{SoftwareType,$(NoTitle),$(!REG_VT_SZ),$(SoftwareType)},+ + {MajorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMajorVersion)},+ + {MinorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMinorVersion)},+ + {Title,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareTitle)},+ + {Description,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareDescription)},+ + {ServiceName,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareName)},+ + {InstallDate,$(NoTitle),$(!REG_VT_DWORD),*($(Now),1)}} + Shell $(UtilityInf), AddValueList, $(SoftProductKey), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + set NewValueList = {{type ,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareType)}, + + {use ,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareUse)}, + + {class,$(NoTitle),$(!REG_VT_MULTI_SZ),$(NetRuleSoftwareClass)}, + + {bindform,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareBindForm)}, + + {InfOption,$(NoTitle),$(!REG_VT_SZ),$(Option)}} + Shell $(UtilityInf), AddValueList, $(SoftNetRuleKey), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + Set NewValueList = {{NbProvider,$(NoTitle),$(!REG_VT_SZ),"_nb"}} + Shell $(UtilityInf), AddValueList, $(KeyParameters), $(NewValueList) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + goto fatalRegistry + endif + CreateRegKey $(SoftServiceKey) {"Performance",$(NoTitle),GenericClass} "" + + $(MAXIMUM_ALLOWED) "" KeyPerformance + set NewValueList = {{Library,$(NoTitle),$(!REG_VT_SZ),"Perfctrs.dll"},+ + {Open,$(NoTitle),$(!REG_VT_SZ),"OpenNbfPerformanceData"},+ + {Collect,$(NoTitle),$(!REG_VT_SZ),"CollectNbfPerformanceData"},+ + {Close,$(NoTitle),$(!REG_VT_SZ),"CloseNbfPerformanceData"}} + Shell $(UtilityInf), AddValueList, $(KeyPerformance), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + CloseRegKey $(KeyPerformance) + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + endif + goto writeparameters +writeparameters = + + set NewValueList = {{Size,$(NoTitle),$(!REG_VT_DWORD),$(Size)}} + ifint $(OldVersionExisted) == $(TRUE) + OpenRegKey $(!REG_H_LOCAL) "" $(ParamKeyName) $(MAXIMUM_ALLOWED) KeyParameters + endif + Shell $(UtilityInf), AddValueList, $(KeyParameters), $(NewValueList) + set RegistryErrorIndex = $($R0) + CloseRegKey $(KeyParameters) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + goto fatalRegistry + endif + EndWait + goto successful +bindingadapter =+ + set Error = "Binding: Sorry, not yet implemented." + goto fatal +removeadapter = + + Shell $(UtilityInf), RemoveSoftwareComponent, $(Manufacturer), + + $(ProductSoftwareName) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error" + goto ShellCodeError + endif + set RegistryErrorIndex = $($R0) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + goto fatalregistry + endif + goto end +UpgradeSoftware = + + ifstr(i) $(ProductKeyName) == $(!NTN_RegBase) + OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct + Ifstr $(KeyProduct) != $(KeyNull) + GetRegValue $(KeyProduct),"MajorVersion", VersionInfo + set Version = *($(VersionInfo), 4) + Shell $(UtilityInf), GetInfFileNameFromRegistry, $(KeyProduct) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error" + goto ShellCodeError + endif + set !UG_Filename = $($R0) + ifstr(i) $(!UG_Filename) != "" + install "Install-Update" + ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS + goto fatal + endif + endif + SetRegValue $(KeyProduct) {MajorVersion,$(NoTitle),$(!REG_VT_SZ),$(ProductMajorVersion)} + SetRegValue $(KeyProduct) {MinorVersion,$(NoTitle),$(!REG_VT_SZ),$(ProductMinorVersion)} + ifint $(Version) != $(ProductVersion) + endif + CloseRegKey $(KeyProduct) + else + goto fatalregistry + endif + endif + goto end +successful = + + goto end +warning = + + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "WARNING", $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + ifstr(i) $($R1) == "OK" + goto $(to) + else-ifstr(i) $($R1) == "CANCEL" + goto $(from) + else + goto "end" + endif +nonfatalinfo = + + Set CommonStatus = STATUS_USERCANCEL + Set Severity = STATUS + goto nonfatalmsg +nonfatal = + + Set Severity = NONFATAL + goto nonfatalmsg +nonfatalmsg = + + ifstr(i) $(Error) == "" + Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + endif + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), $(Severity), $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + ifstr(i) $($R1) == "OK" + goto $(from) + else + goto "end" + endif +fatalregistry = + + Shell $(UtilityInf) RegistryErrorString $(RegistryErrorIndex) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + goto fatal +fatal = + + ifstr(i) $(Error) == "" + Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + endif + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "FATAL", $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + goto setfailed +ShellCodeError = + + set DlgType = "MessageBox" + set STF_MB_TITLE = $(ShellCodeErrorTitle) + set STF_MB_TEXT = $(ShellCodeErrorText) + set STF_MB_TYPE = 1 + set STF_MB_ICON = 3 + set STF_MB_DEF = 1 + ui start "Error Message" + goto setfailed +setfailed = + + set CommonStatus = STATUS_FAILED + ifstr(i) $(OEM_ABANDON_ON) == TRUE + set OEM_ABANDON_ON = FALSE + goto removeadapter + endif + goto end +end = + + goto term +term = + + Return $(CommonStatus) +[Install-Option] + set STF_VITAL = "" + ifstr(i) $(AddCopy) == "YES" + AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers + endif + ifstr(i) $(DoCopy) == "YES" + set !STF_NCPA_FLUSH_COPYLIST = TRUE + CopyFilesInCopyList + endif + ifstr(i) $(DoConfig) == "YES" + endif + Exit +[Install-Update] + set STF_VITAL = "" + set STF_OVERWRITE = "VERIFYSOURCEOLDER" + AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers + AddSectionFilesToCopyList Files-Inf $(SrcDir) $(!STF_WINDOWSSYSPATH) + set !STF_NCPA_FLUSH_COPYLIST = TRUE + CopyFilesInCopyList + exit +[Source Media Descriptions] + 1 = "Windows NT Setup Disk #1" , TAGFILE = disk1 + 2 = "Windows NT Setup CD-ROM Disk" , TAGFILE = disk2 +[ProductType] +STF_PRODUCT = Winnt +STF_PLATFORM = Mips +[Files-Inf] +2, oemsetup.inf, SIZE=1000, RENAME=$(!UG_Filename) +[Files-ST] +2,ST.SYS , SIZE=88888 +[LanguagesSupported] + ENG +[OptionsTextENG] + ST = "Sample Transport" +[FileConstantsENG] +ProCaption = "Windows NT Setup" +ProCancel = "Cancel" +ProCancelMsg = "Windows NT Networking is not correctly installed. "+ + "Are you sure you want to cancel copying files?" +ProCancelCap = "Network Setup Message" +ProText1 = "Copying:" +ProText2 = "To:" +FunctionTitle = "Sample Transport" +ProductSoftwareDescription = "Microsoft Sample TDI Transport" +ProductSoftwareDisplayName = "Sample Transport" +ProductSoftwareTitle = "Sample Transport" +ShellCodeErrorTitle = "Error: "$(FunctionTitle) +ShellCodeErrorText = "Shell Code Error." +[DialogConstantsENG] +Help = "&Help" +Exit = "Cancel" +OK = "OK" +HelpContext = "" +Continue = "Continue" +Cancel = "Cancel" +[FileDependentDlgENG] +GroupLabel = "Optimization:" +Radio1 = "&Minimize Memory Used" +Radio2 = "&Balance" +Radio3 = "M&aximize Throughput && Connections" +DlgType = "Radio" +DlgTemplate = "ST" +Caption = $(FunctionTitle) +OptionsGreyed = {} +HelpContext = $(!IDH_DB_OEMNXPNB_INS) + + + diff --git a/private/ntos/tdi/st/packet.c b/private/ntos/tdi/st/packet.c new file mode 100644 index 000000000..29e9fe1b8 --- /dev/null +++ b/private/ntos/tdi/st/packet.c @@ -0,0 +1,597 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the TP_PACKET object, which + describes an NDIS packet. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + +// +// This is temporary; this is the quota that we charge for a receive +// packet for now, until we fix the problem with token-ring needing +// big packets and using all the memory. The number is the actual +// value for Ethernet. +// + +#if 1 +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) 1533 +#else +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) (_DeviceContext)->ReceiveBufferLength +#endif + + + + +VOID +StAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a send packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_PACKET Packet; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PSEND_PACKET_TAG SendTag; + PNDIS_BUFFER NdisBuffer; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate send packet: limit\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 107); + *TransportSendPacket = NULL; + return; + } + + Packet = (PTP_PACKET)ExAllocatePool (NonPagedPool, DeviceContext->PacketLength); + if (Packet == NULL) { + PANIC("ST: Could not allocate send packet: no pool\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 207); + *TransportSendPacket = NULL; + return; + } + RtlZeroMemory (Packet, DeviceContext->PacketLength); + + DeviceContext->MemoryUsage += DeviceContext->PacketLength; + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->SendPacketPoolHandle); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (Packet); + StWriteResourceErrorLog (DeviceContext, 0, 307); + *TransportSendPacket = NULL; + return; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPoolHandle, + Packet->Header, + DeviceContext->PacketHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisFreePacket (NdisPacket); + ExFreePool (Packet); + *TransportSendPacket = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + Packet->NdisPacket = NdisPacket; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_I_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = NULL; + + Packet->Type = ST_PACKET_SIGNATURE; + Packet->Size = sizeof (TP_PACKET); + Packet->Provider = DeviceContext; + + ++DeviceContext->PacketAllocated; + + *TransportSendPacket = Packet; + +} /* StAllocateSendPacket */ + + +VOID +StDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a send packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - A pointer to the send packet. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET NdisPacket = TransportSendPacket->NdisPacket; + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + + NdisFreePacket (NdisPacket); + ExFreePool (TransportSendPacket); + + --DeviceContext->PacketAllocated; + DeviceContext->MemoryUsage -= DeviceContext->PacketLength; + +} /* StDeallocateSendPacket */ + + +VOID +StAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + + // + // This does not count in DeviceContext->MemoryUsage because + // the storage is allocated when we allocate the packet pool. + // + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->ReceivePacketPoolHandle); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StWriteResourceErrorLog (DeviceContext, 0, 309); + *TransportReceivePacket = NULL; + return; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ++DeviceContext->ReceivePacketAllocated; + + *TransportReceivePacket = NdisPacket; + +} /* StAllocateReceivePacket */ + + +VOID +StDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - A pointer to the packet. + +Return Value: + + None. + +--*/ + +{ + + NdisFreePacket (TransportReceivePacket); + + --DeviceContext->ReceivePacketAllocated; + +} /* StDeallocateReceivePacket */ + + +VOID +StAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive buffer. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - Returns a pointer to the buffer, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PBUFFER_TAG BufferTag; + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + RECEIVE_BUFFER_QUOTA(DeviceContext)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate receive buffer: limit\n"); + StWriteResourceErrorLog (DeviceContext, RECEIVE_BUFFER_QUOTA(DeviceContext), 108); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag = (PBUFFER_TAG)ExAllocatePool ( + NonPagedPoolCacheAligned, + DeviceContext->ReceiveBufferLength); + + if (BufferTag == NULL) { + PANIC("ST: Could not allocate receive buffer: no pool\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->ReceiveBufferLength, 208); + *TransportReceiveBuffer = NULL; + return; + } + + DeviceContext->MemoryUsage += RECEIVE_BUFFER_QUOTA(DeviceContext); + + // + // point to the buffer for NDIS + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPoolHandle, + BufferTag->Buffer, + DeviceContext->MaxReceivePacketSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (BufferTag); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag->Length = DeviceContext->MaxReceivePacketSize; + BufferTag->NdisBuffer = NdisBuffer; + + ++DeviceContext->ReceiveBufferAllocated; + + *TransportReceiveBuffer = BufferTag; + +} /* StAllocateReceiveBuffer */ + + +VOID +StDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive buffer. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - A pointer to the buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisFreeBuffer (TransportReceiveBuffer->NdisBuffer); + ExFreePool (TransportReceiveBuffer); + + --DeviceContext->ReceiveBufferAllocated; + DeviceContext->MemoryUsage -= RECEIVE_BUFFER_QUOTA(DeviceContext); + +} /* StDeallocateReceiveBuffer */ + + +NTSTATUS +StCreatePacket( + PDEVICE_CONTEXT DeviceContext, + PTP_PACKET *Packet + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool, + and prepares the MAC and DLC headers for use by the connection. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + Packet - Pointer to a place where we will return a pointer to the + allocated packet. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PSINGLE_LIST_ENTRY s; + PTP_PACKET ThePacket; + + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); + + if (s == NULL) { + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->PacketExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + ThePacket->Provider = DeviceContext; // who owns this packet + ThePacket->PacketSent = FALSE; + ThePacket->PacketNoNdisBuffer = FALSE; + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* StCreatePacket */ + + +VOID +StDestroyPacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine destroys a packet, thereby returning it to the pool. If + it is determined that there is at least one connection waiting for a + packet to become available (and it just has), then the connection is + removed from the device context's list and AdvanceSend is called to + prep the connection further. + +Arguments: + + Packet - Pointer to a packet to be returned to the pool. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql1; + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &HeaderBuffer); + + // data buffers get thrown away + + if (Packet->PacketNoNdisBuffer) { + + // + // If the NDIS_BUFFER chain is not ours, then we can't + // start unchaining since that would mess up the queue; + // instead we just drop the rest of the chain. + // + + NdisReinitializePacket (Packet->NdisPacket); + + } else { + + // + // Return all the NDIS_BUFFERs to the system. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + } + + } + + ASSERT (HeaderBuffer != NULL); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + + NdisChainBufferAtFront (Packet->NdisPacket, HeaderBuffer); + + + // + // Put the packet back for use again. + // + + DeviceContext = Packet->Provider; + + ExInterlockedPushEntryList ( + &DeviceContext->PacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); + + // + // If there is a connection waiting to ship out more packets, then + // wake it up and start packetizing again. + // + // We do a quick check without the lock; there is a small + // window where we may not take someone off, but this + // window exists anyway and we assume that more packets + // will be freed in the future. + // + + if (IsListEmpty (&DeviceContext->PacketWaitQueue)) { + return; + } + + p = ExInterlockedRemoveHeadList( + &DeviceContext->PacketWaitQueue, + &DeviceContext->SpinLock); + + if (p != NULL) { + + // + // Remove a connection from the "packet starved" queue. + // + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketWaitLinkage); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags &= ~CONNECTION_FLAGS_SUSPENDED; + + // + // Place the connection on the packetize queue and start + // packetizing the next connection to be serviced. If he + // is already on the packetize queue for some reason, then + // don't do this. + // + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + StReferenceConnection ("Packet available", Connection); + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + PacketizeConnections (DeviceContext); + + } + +} /* StDestroyPacket */ + diff --git a/private/ntos/tdi/st/rcv.c b/private/ntos/tdi/st/rcv.c new file mode 100644 index 000000000..b2f0e779d --- /dev/null +++ b/private/ntos/tdi/st/rcv.c @@ -0,0 +1,247 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rcv.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceive + o TdiReceiveDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiReceive( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceive request for the transport provider. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + LARGE_INTEGER timeout = {0,0}; + PIO_STACK_LOCATION irpSp; + PMDL ReceiveBuffer; + ULONG ReceiveBufferLength; + PTDI_REQUEST_KERNEL_RECEIVE parameters; + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // Initialize bytes transferred here. + // + + Irp->IoStatus.Information = 0; // reset byte transfer count. + + + parameters = (PTDI_REQUEST_KERNEL_RECEIVE)(&irpSp->Parameters); + ReceiveBuffer = Irp->MdlAddress; + ReceiveBufferLength =parameters->ReceiveLength; + + // + // Queue up this receive to the connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + ReceiveBuffer, + ReceiveBufferLength, + timeout, + &tpRequest); + + // + // We have a request, now queue it. If the connection has gone south on us + // while we were getting things going, we will avoid actually queing the + // request. + // + + if (NT_SUCCESS (status)) { + + // This reference is removed by StDestroyRequest. + + StReferenceConnection("TdiReceive request", connection); + tpRequest->Owner = ConnectionType; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + status = STATUS_PENDING; + } else { + + // + // Insert onto the receive queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->ReceiveQueue,&tpRequest->Linkage); + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelReceive((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelReceive; + IoReleaseCancelSpinLock(cancelirql); + + AwakenReceive (connection); // awaken if sleeping. + + status = STATUS_PENDING; + } + } + + StDereferenceConnection("temp TdiReceive", connection); + + return status; +} /* TdiReceive */ + + +NTSTATUS +StTdiReceiveDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PTP_REQUEST tpRequest; + LARGE_INTEGER timeout = {0,0}; + PIO_STACK_LOCATION irpSp; + PMDL ReceiveBuffer; + ULONG ReceiveBufferLength; + + // + // verify that the operation is taking place on an address. At the same + // time we do this, we reference the address. This ensures it does not + // get removed out from under us. Note also that we do the address + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = StVerifyAddressObject (addressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + + ReceiveBuffer = Irp->MdlAddress; + ReceiveBufferLength = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))->ReceiveLength; + + status = StCreateRequest ( + Irp, // IRP for this request. + address, // context + REQUEST_FLAGS_ADDRESS, // partial flags. + ReceiveBuffer, + ReceiveBufferLength, + timeout, + &tpRequest); + + if (NT_SUCCESS (status)) { + StReferenceAddress ("Receive datagram", address); + tpRequest->Owner = AddressType; + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + StCompleteRequest (tpRequest, STATUS_NETWORK_NAME_DELETED, 0); + status = STATUS_PENDING; + } else { + InsertTailList (&addressFile->ReceiveDatagramQueue,&tpRequest->Linkage); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + } + + status = STATUS_PENDING; + } + + StDereferenceAddress ("Temp rcv datagram", address); + + return status; +} /* TdiReceiveDatagram */ + diff --git a/private/ntos/tdi/st/rcveng.c b/private/ntos/tdi/st/rcveng.c new file mode 100644 index 000000000..8e104a954 --- /dev/null +++ b/private/ntos/tdi/st/rcveng.c @@ -0,0 +1,383 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rcveng.c + +Abstract: + + This module contains code that implements the receive engine for the + Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +VOID +ActivateReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine activates the next TdiReceive request on the specified + connection object if there is no active request on that connection + already. This allows the request to accept data on the connection. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + + // + // The ACTIVE_RECEIVE bitflag will be set on the connection if + // the receive-fields in the CONNECTION object are valid. If + // this flag is cleared, then we try to make the next TdiReceive + // request in the ReceiveQueue the active request. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + if (!IsListEmpty (&Connection->ReceiveQueue)) { + + // + // Found a receive, so make it the active one. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + + Request = CONTAINING_RECORD ( + Connection->ReceiveQueue.Flink, + TP_REQUEST, + Linkage); + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = Request; + Connection->CurrentReceiveMdl = Request->Buffer2; + Connection->ReceiveLength = Request->Buffer2Length; + Connection->ReceiveByteOffset = 0; + } + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + +} /* ActivateReceive */ + + +VOID +AwakenReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to reactivate a sleeping connection with the + RECEIVE_WAKEUP bitflag set because data arrived for which no receive + was available. The caller has made a receive available at the connection, + so here we activate the next receive, and send the appropriate protocol + to restart the message at the first byte offset past the one received + by the last receive. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + // + // If the RECEIVE_WAKEUP bitflag is set, then awaken the connection. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + if (Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) { + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + Connection->Flags &= ~CONNECTION_FLAGS_RECEIVE_WAKEUP; + + // + // Found a receive, so turn off the wakeup flag, and activate + // the next receive. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + ActivateReceive (Connection); + + return; + } + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); +} /* AwakenReceive */ + + +VOID +CompleteReceive( + PTP_CONNECTION Connection, + BOOLEAN EndOfRecord, + KIRQL ConnectionIrql, + KIRQL CancelIrql + ) + +/*++ + +Routine Description: + + This routine is called by ProcessIncomingData when the current receive + must be completed. Depending on whether the current frame being + processed is a DATA_FIRST_MIDDLE or DATA_ONLY_LAST, and also whether + all of the data was processed, the EndOfRecord flag will be set accordingly + by the caller to indicate that a message boundary was received. + + NOTE: This function is called with the connection and cancel + IRQLs held, and returns with them released. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + EndOfRecord - BOOLEAN set to true if TDI_END_OF_RECORD should be reported. + + ConnectionIrql - The IRQL at which the connection spinlock was acquired. + + CancelIrql - The IRQL at which the cancel spinlock was acquired. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_REQUEST Request; + KIRQL oldirql = ConnectionIrql; + KIRQL cancelirql = CancelIrql; + ULONG BytesReceived; + + + if (IsListEmpty (&Connection->ReceiveQueue)) { + + ASSERT ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + return; + } + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + BytesReceived = Connection->MessageBytesReceived; + + + // + // Complete the TdiReceive request at the head of the + // connection's ReceiveQueue. + // + + p = RemoveHeadList (&Connection->ReceiveQueue); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + + Request->Flags |= REQUEST_FLAGS_DELAY; + + StCompleteRequest( + Request, + EndOfRecord ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + BytesReceived); + +} /* CompleteReceive */ + + +VOID +StCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The receive is found on the connection's receive queue; if it + is the current request it is cancelled and the connection + goes into "cancelled receive" mode, otherwise it is cancelled + silently. + + In "cancelled receive" mode the connection makes it appear to + the remote the data is being received, but in fact it is not + indicated to the transport or buffered on our end + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_REQUEST Request; + PLIST_ENTRY p; + ULONG BytesReceived; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_RECEIVE)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + // + // See if this is the IRP for the current receive request. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + BytesReceived = Connection->MessageBytesReceived; + + p = Connection->ReceiveQueue.Flink; + + // + // If there is a receive active, then see if this is it. + // + + if ((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->IoRequestPacket == Irp) { + + // + // yes, it is the active receive. Turn on the RCV_CANCELLED + // bit instructing the connection to drop the rest of the + // data received (until the DOL comes in). + // + + Connection->Flags2 |= CONNECTION_FLAGS2_RCV_CANCELLED; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + (VOID)RemoveHeadList (&Connection->ReceiveQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + StCompleteRequest (Request, STATUS_CANCELLED, 0); + return; + + } + + } + + + // + // If we fall through to here, the IRP was not the active receive. + // Scan through the list, looking for this IRP. + // + + Found = FALSE; + + while (p != &Connection->ReceiveQueue) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if (Request->IoRequestPacket == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + Found = TRUE; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + StCompleteRequest (Request, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + +} diff --git a/private/ntos/tdi/st/request.c b/private/ntos/tdi/st/request.c new file mode 100644 index 000000000..de553c0cc --- /dev/null +++ b/private/ntos/tdi/st/request.c @@ -0,0 +1,801 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + This module contains code which implements the TP_REQUEST object. + Routines are provided to create, destroy, reference, and dereference, + transport request objects. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" + + +VOID +StTdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when a request + such as TdiSend, TdiReceive, TdiSendDatagram, TdiReceiveDatagram, etc., + encounters a timeout. This routine cleans up the activity and cancels it. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_REQUEST block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + PTP_CONNECTION Connection; + PIO_STACK_LOCATION IrpSp; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + Request = (PTP_REQUEST)DeferredContext; + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + Request->Flags &= ~REQUEST_FLAGS_TIMER; + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + + // + // find reason for timeout + // + + IrpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + if (IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) { + switch (IrpSp->MinorFunction) { + + // + // none of these should time out. + // + + case TDI_SEND: + case TDI_ACCEPT: + case TDI_SET_INFORMATION: + case TDI_SET_EVENT_HANDLER: + case TDI_SEND_DATAGRAM: + case TDI_RECEIVE_DATAGRAM: + case TDI_RECEIVE: + + ASSERT (FALSE); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StCompleteRequest (Request, STATUS_IO_TIMEOUT, 0); + break; + + + case TDI_LISTEN: + case TDI_CONNECT: + + Connection = (PTP_CONNECTION)(Request->Context); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Since these requests are part of the connection + // itself, we just stop the connection and the + // request will get torn down then. If we get the + // situation where the request times out before + // it is queued to the connection, then the code + // that is about to queue it will check the STOPPING + // flag and complete it then. + // + + StStopConnection (Connection, STATUS_IO_TIMEOUT); + break; + + case TDI_DISCONNECT: + + // + // We don't create requests for TDI_DISCONNECT any more. + // + + ASSERT(FALSE); + break; + + default: + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + break; + + } // end of switch + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + + StDereferenceRequest ("Timeout", Request); // for the timeout + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StDereferenceRequest ("Timeout: stopping", Request); // for the timeout + + } + + return; + +} /* RequestTimeoutHandler */ + + +VOID +StAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ) + +/*++ + +Routine Description: + + This routine allocates a request packet from nonpaged pool and initializes + it to a known state. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a place where this routine will return + a pointer to a transport request structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_REQUEST Request; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_REQUEST)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate request: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 104); + *TransportRequest = NULL; + return; + } + + Request = (PTP_REQUEST)ExAllocatePool (NonPagedPool, sizeof (TP_REQUEST)); + if (Request == NULL) { + PANIC("ST: Could not allocate request: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 204); + *TransportRequest = NULL; + return; + } + RtlZeroMemory (Request, sizeof(TP_REQUEST)); + + DeviceContext->MemoryUsage += sizeof(TP_REQUEST); + ++DeviceContext->RequestAllocated; + + Request->Type = ST_REQUEST_SIGNATURE; + Request->Size = sizeof (TP_REQUEST); + + Request->Provider = DeviceContext; + Request->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Request->SpinLock); + KeInitializeDpc (&Request->Dpc, StTdiRequestTimeoutHandler, (PVOID)Request); + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + + *TransportRequest = Request; + +} /* StAllocateRequest */ + + +VOID +StDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ) + +/*++ + +Routine Description: + + This routine frees a request packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a transport request structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportRequest); + --DeviceContext->RequestAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_REQUEST); + +} /* StDeallocateRequest */ + + +NTSTATUS +StCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ) + +/*++ + +Routine Description: + + This routine creates a transport request and associates it with the + specified IRP, context, and queue. All major requests, including + TdiSend, TdiSendDatagram, TdiReceive, and TdiReceiveDatagram requests, + are composed in this manner. + +Arguments: + + Irp - Pointer to an IRP which was received by the transport for this + request. + + Context - Pointer to anything to associate this request with. This + value is not interpreted except at request cancelation time. + + Flags - A set of bitflags indicating the disposition of this request. + + Timeout - Timeout value (if non-zero) to start a timer for this request. + If zero, then no timer is activated for the request. + + TpRequest - If the function returns STATUS_SUCCESS, this will return + pointer to the TP_REQUEST structure allocated. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PLIST_ENTRY p; + PIO_STACK_LOCATION irpSp; + + + irpSp = IoGetCurrentIrpStackLocation(Irp); + DeviceContext = (PDEVICE_CONTEXT)irpSp->FileObject->DeviceObject; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->RequestPool); + if (p == &DeviceContext->RequestPool) { + + if ((DeviceContext->RequestMaxAllocated == 0) || + (DeviceContext->RequestAllocated < DeviceContext->RequestMaxAllocated)) { + + StAllocateRequest (DeviceContext, &Request); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 404); + Request = NULL; + + } + + if (Request == NULL) { + ++DeviceContext->RequestExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: Could not allocate request object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + } + + ++DeviceContext->RequestInUse; + if (DeviceContext->RequestInUse > DeviceContext->RequestMaxInUse) { + ++DeviceContext->RequestMaxInUse; + } + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // fill out the request. + // + + // Request->Provider = DeviceContext; + Request->IoRequestPacket = Irp; + Request->Buffer2 = Buffer2; + Request->Buffer2Length = Buffer2Length; + Request->Flags = Flags; + Request->Context = Context; + Request->ReferenceCount = 1; // initialize reference count. + + if ((Timeout.LowPart == 0) && (Timeout.HighPart == 0)) { + + // no timeout + } else { + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + StReferenceRequest ("Create: timer", Request); // one for the timer + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + } + + *TpRequest = Request; + + return STATUS_SUCCESS; +} /* StCreateRequest */ + + +VOID +StDestroyRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine returns a request block to the free pool. + +Arguments: + + Request - Pointer to a TP_REQUEST block to return to the free pool. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + + // + // Return the request to the caller with whatever status is in the IRP. + // + + // + // Now dereference the owner of this request so that we are safe when + // we finally tear down the {connection, address}. The problem we're + // facing here is that we can't allow the user to assume semantics; + // the end of life for a connection must truly be the real end of life. + // for that to occur, we reference the owning object when the request is + // created and we dereference it just before we return it to the pool. + // + + switch (Request->Owner) { + case ConnectionType: + if (!(Request->Flags & REQUEST_FLAGS_DELAY)) { + StDereferenceConnection ("Removing Connection",((PTP_CONNECTION)Request->Context)); + } + break; + + case AddressType: + StDereferenceAddress ("Removing Address", ((PTP_ADDRESS)Request->Context)); + break; + + case DeviceContextType: + StDereferenceDeviceContext ("Removing Address", ((PDEVICE_CONTEXT)Request->Context)); + break; + } + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + DeviceContext = Request->Provider; + + if (Request->Flags & REQUEST_FLAGS_DELAY) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + InsertTailList( + &DeviceContext->IrpCompletionQueue, + &Request->IoRequestPacket->Tail.Overlay.ListEntry); + + } else { + + IoCompleteRequest (Request->IoRequestPacket, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + } + + // + // Put the request back on the free list. NOTE: we have the + // lock held here. + // + + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + --DeviceContext->RequestInUse; + + if ((DeviceContext->RequestAllocated - DeviceContext->RequestInUse) > + DeviceContext->RequestInitAllocated) { + StDeallocateRequest (DeviceContext, Request); + } else { + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} /* StDestroyRequest */ + + +VOID +StRefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport request. + +Arguments: + + Request - Pointer to a TP_REQUEST block. + +Return Value: + + none. + +--*/ + +{ + ASSERT (Request->ReferenceCount > 0); + + InterlockedIncrement (&Request->ReferenceCount); + +} /* StRefRequest */ + + +VOID +StDerefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine dereferences a transport request by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + StDestroyRequest to remove it from the system. + +Arguments: + + Request - Pointer to a transport request object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Request->ReferenceCount); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + StDestroyRequest (Request); + } + +} /* StDerefRequest */ + + +VOID +StCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport request object, completing the I/O, + stopping the timeout, and freeing up the request object itself. + +Arguments: + + Request - Pointer to a transport request object. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIRP Irp; + NTSTATUS FinalStatus = Status; + BOOLEAN TimerWasSet; + + ASSERT (Status != STATUS_PENDING); + + if (Request->Flags & REQUEST_FLAGS_SEND_RCV) { + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Request->Flags |= REQUEST_FLAGS_STOPPING; + Irp = Request->IoRequestPacket; + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + StDereferenceRequest ("Complete", Request); // remove creation reference. + return; + } + + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + Request->Flags |= REQUEST_FLAGS_STOPPING; + + // + // Cancel the pending timeout on this request. Not all requests + // have their timer set. If this request has the TIMER bit set, + // then the timer needs to be cancelled. If it cannot be cancelled, + // then the timer routine will be run, so we just return and let + // the timer routine worry about cleaning up this request. + // + + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + TimerWasSet = KeCancelTimer (&Request->Timer); + + if (TimerWasSet) { + StDereferenceRequest ("Complete: stop timer", Request); + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Irp = Request->IoRequestPacket; + + + // + // Install the return code in the IRP so that when we call StDestroyRequest, + // it will get completed with the proper return status. + // + + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + // + // The entire transport is done with this request. + // + + StDereferenceRequest ("Complete", Request); // remove creation reference. + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + +} /* StCompleteRequest */ + + +VOID +StRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a send IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ASSERT (IRP_REFCOUNT(IrpSp) > 0); + + InterlockedIncrement (&IRP_REFCOUNT(IrpSp)); + +} /* StRefSendIrp */ + + +VOID +StDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport send IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + + NOTE: This assume that IRP_CONNECTION(IrpSp) has been changed + to point to the IRP instead of the connection. + +Arguments: + + Request - Pointer to a transport send IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&IRP_REFCOUNT(IrpSp)); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + + PIRP Irp = (PIRP)IRP_CONNECTION(IrpSp); + + IRP_REFCOUNT(IrpSp) = 0; + IRP_CONNECTION (IrpSp) = NULL; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + +} /* StDerefSendIrp */ + + +VOID +StCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport send IRP. + +Arguments: + + Irp - Pointer to a send IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_CONNECTION(IrpSp); + + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + IRP_CONNECTION(IrpSp) = Irp; + + StDereferenceSendIrp ("Complete", IrpSp); // remove creation reference. + + StDereferenceConnection ("Removing Connection", Connection); + +} /* StCompleteSendIrp */ diff --git a/private/ntos/tdi/st/send.c b/private/ntos/tdi/st/send.c new file mode 100644 index 000000000..0e6a2b9f6 --- /dev/null +++ b/private/ntos/tdi/st/send.c @@ -0,0 +1,385 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSend + o TdiSendDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiSend( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSend request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, cancelirql; + NTSTATUS status; + PTP_CONNECTION connection; + PMDL SendBuffer; + ULONG SendBufferLength; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SEND parameters; + PIRP TempIrp; + + // + // Determine which connection this send belongs on. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // Check that this is really a connection. + // + + if ((connection->Size != sizeof (TP_CONNECTION)) || + (connection->Type != ST_CONNECTION_SIGNATURE)) { + return STATUS_INVALID_CONNECTION; + } + + // + // Now map the data in to SVA space. + // + + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + SendBuffer = Irp->MdlAddress; + SendBufferLength = parameters->SendLength; + + // + // Interpret send options. + // + + // + // Now we have a reference on the connection object. Queue up this + // send to the connection object. + // + + + // This reference is removed by TdiDestroyRequest + + StReferenceConnection("TdiSend", connection); + + IRP_CONNECTION(irpSp) = connection; + IRP_REFCOUNT(irpSp) = 1; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + StCompleteSendIrp( + Irp, + connection->Status, + 0); + status = STATUS_PENDING; + } else { + + StReferenceConnection ("Verify Temp Use", connection); + + // + // Insert onto the send queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry); + + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + Irp->CancelIrql = cancelirql; + StCancelSend((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelSend; + + // + // If this connection is waiting for an EOR to appear because a non-EOR + // send failed at some point in the past, fail this send. Clear the + // flag that causes this if this request has the EOR set. + // + // BUGBUG: Should the FailSend status be clearer here? + // + + if ((connection->Flags & CONNECTION_FLAGS_FAILING_TO_EOR) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // BUGBUG: Should we save status from real failure? + // + + FailSend (connection, STATUS_LINK_FAILED, TRUE); + + if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR; + } + + StDereferenceConnection ("Failing to EOR", connection); // release lookup hold. + return STATUS_PENDING; + } + + + // + // If the send state is either IDLE or W_EOR, then we should + // begin packetizing this send. Otherwise, some other event + // will cause it to be packetized. + // + + // + // NOTE: If we call StartPacketizingConnection, we make + // sure that it is the last operation we do on this + // connection. This allows us to "hand off" the reference + // we have to that function, which converts it into + // a reference for being on the packetize queue. + // + + switch (connection->SendState) { + + case CONNECTION_SENDSTATE_IDLE: + + InitializeSend (connection); // sets state to PACKETIZE + + // + // If we can, packetize right now. + // + + if ((!(connection->Flags & CONNECTION_FLAGS_PACKETIZE)) && + (!(connection->Flags & CONNECTION_FLAGS_STOPPING))) { + + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + PacketizeSend (connection); + + } else { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StDereferenceConnection ("Stopping or already packetizing", connection); // release lookup hold. + + } + + break; + + case CONNECTION_SENDSTATE_W_EOR: + connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + // + // Adjust the send variables on the connection so that + // they correctly point to this new send. We can't call + // InitializeSend to do that, because we need to keep + // track of the other outstanding sends on this connection + // which have been sent but are a part of this message. + // + + TempIrp = CONTAINING_RECORD( + connection->SendQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + + connection->sp.CurrentSendIrp = TempIrp; + connection->sp.CurrentSendMdl = TempIrp->MdlAddress; + connection->sp.SendByteOffset = 0; + connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(TempIrp)); + + StartPacketizingConnection (connection, TRUE, oldirql, cancelirql); + break; + + default: + + // + // The connection is in another state (such as + // W_ACK or W_LINK), we just need to make sure + // to call InitializeSend if the new one is + // the first one on the list. + // + + // + // BUGBUG: Currently InitializeSend sets SendState, + // we should fix this. + // + + if (connection->SendQueue.Flink == &Irp->Tail.Overlay.ListEntry) { + ULONG SavedSendState; + SavedSendState = connection->SendState; + InitializeSend (connection); + connection->SendState = SavedSendState; + } + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StDereferenceConnection("temp TdiSend", connection); + + } + + } + + status = STATUS_PENDING; + + + return status; +} /* TdiSend */ + + +NTSTATUS +StTdiSendDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_REQUEST tpRequest; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PMDL SendBuffer; + ULONG SendBufferLength; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SENDDG parameters; + LARGE_INTEGER timeout = {0,0}; + UINT MaxUserData; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = StVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters); + SendBuffer = Irp->MdlAddress; + SendBufferLength = parameters->SendLength; + + // + // Check that the length is short enough. + // + + MacReturnMaxDataSize( + &address->Provider->MacInfo, + NULL, + 0, + address->Provider->MaxSendPacketSize, + &MaxUserData); + + if (SendBufferLength > + (MaxUserData - sizeof(ST_HEADER))) { + + return STATUS_INVALID_PARAMETER; + + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the address object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + address, // context. + REQUEST_FLAGS_ADDRESS, // partial flags. + SendBuffer, // the data to be sent. + SendBufferLength, // length of the data. + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { + StDereferenceAddress ("no send request", address); + return status; // if we couldn't queue the request. + } + + StReferenceAddress ("Send datagram", address); + tpRequest->Owner = AddressType; + + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + StCompleteRequest (tpRequest, STATUS_NETWORK_NAME_DELETED, 0); + return STATUS_PENDING; + } else { + InsertTailList ( + &address->SendDatagramQueue, + &tpRequest->Linkage); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + } + + // + // The request is queued. Ship the next request at the head of the queue, + // provided the completion handler is not active. We serialize this so + // that only one MDL and ST datagram header needs to be statically + // allocated for reuse by all send datagram requests. + // + + (VOID)StSendDatagramsOnAddress (address); + + StDereferenceAddress("tmp send datagram", address); + + return STATUS_PENDING; + +} /* StTdiSendDatagram */ diff --git a/private/ntos/tdi/st/sendeng.c b/private/ntos/tdi/st/sendeng.c new file mode 100644 index 000000000..4019291de --- /dev/null +++ b/private/ntos/tdi/st/sendeng.c @@ -0,0 +1,1505 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sendeng.c + +Abstract: + + This module contains code that implements the send engine for the + Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" +#if 27 +ULONG StNoisySend = 0; +ULONG StSndLoc = 0; +ULONG StSnds[10]; +#endif + + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate, + IN KIRQL ConnectionIrql, + IN KIRQL CancelIrql + ) + +/*++ + +Routine Description: + + This routine is called to place a connection on the PacketizeQueue + of its device context object. Then this routine starts packetizing + the first connection on that queue. + + *** The Connection spin lock must be held on entry to this routine. + Optionally, the cancel spin lock may be held. If so, the cancel + spin lock must have been acquired first. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Immediate - TRUE if the connection should be packetized + immediately; FALSE if the connection should be queued + up for later packetizing (implies that ReceiveComplete + will be called in the future, which packetizes always). + + NOTE: If this is TRUE, it also implies that we have + a connection reference. + + ConnectionIrql - The OldIrql value when the connection spin lock + was acquired. + + CancelIrql - The OldIrql value when the cancel spin lock was + acquired. -1 means that the cancel spin lock isn't held. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = Connection->Provider; + + // + // If this connection's SendState is set to PACKETIZE and if + // we are not already on the PacketizeQueue, then go ahead and + // append us to the end of that queue, and remember that we're + // on it by setting the CONNECTION_FLAGS_PACKETIZE bitflag. + // + // Also don't queue it if the connection is stopping. + // + + if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) && + !(Connection->Flags & + (CONNECTION_FLAGS_PACKETIZE | CONNECTION_FLAGS_STOPPING))) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + if (!Immediate) { + StReferenceConnection ("Packetize", Connection); + } + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql); + + } else { + + RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql); + if (Immediate) { + StDereferenceConnection("temp TdiSend", Connection); + } + } + + if (CancelIrql != (KIRQL)-1) { + IoReleaseCancelSpinLock (CancelIrql); + } + + if (Immediate) { + PacketizeConnections (DeviceContext); + } + +} /* StartPacketizingConnection */ + + +VOID +PacketizeConnections( + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine attempts to packetize all connections waiting on the + PacketizeQueue of the DeviceContext. + + +Arguments: + + DeviceContext - Pointer to a DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_CONNECTION Connection; + + // + // Pick connections off of the device context's packetization queue + // until there are no more left to pick off. For each one, we call + // PacketizeSend. Note this routine can be executed concurrently + // on multiple processors and it doesn't matter; multiple connections + // may be packetized concurrently. + // + + while (TRUE) { + + p = ExInterlockedRemoveHeadList( + &DeviceContext->PacketizeQueue, + &DeviceContext->SpinLock); + + if (p == NULL) { + break; + } + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage); + PacketizeSend (Connection); + } + +} /* PacketizeConnections */ + + +VOID +PacketizeSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine packetizes the current TdiSend request on the specified + connection as much as limits will permit. A given here is that there + is an active send on the connection that needs further packetization. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + ULONG MaxFrameSize, FrameSize; + ULONG PacketBytes; + TP_SEND_POINTER SavedSendPointer; + PNDIS_BUFFER PacketDescriptor; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PDEVICE_CONTEXT DeviceContext; + PTP_PACKET Packet; + NTSTATUS Status; + PST_HEADER StHeader; + UINT HeaderLength; + PIO_STACK_LOCATION IrpSp; + PSEND_PACKET_TAG SendTag; + ULONG LastPacketLength; + + DeviceContext = Connection->Provider; + + // + // Just loop until one of three events happens: (1) we run out of + // packets from StCreatePacket, (2) we completely packetize the send. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + StDereferenceConnection ("No longer packetizing", Connection); + return; + } + + MaxFrameSize = Connection->MaximumDataSize; + + // + // It is possible for a frame to arrive during the middle of this loop + // (such as a NO_RECEIVE) that will put us into a new state (such as + // W_RCVCONT). For this reason, we have to check the state every time + // (at the end of the loop). + // + + do { + + if (!NT_SUCCESS (StCreatePacket (DeviceContext, &Packet))) { + + // + // We need a packet to finish packetizing the current send, but + // there are no more packets available in the pool right now. + // Set our send state to W_PACKET, and put this connection on + // the PacketWaitQueue of the device context object. Then, + // when StDestroyPacket frees up a packet, it will check this + // queue for starved connections, and if it finds one, it will + // take a connection off the list and set its send state to + // SENDSTATE_PACKETIZE and put it on the PacketizeQueue. + // + + Connection->SendState = CONNECTION_SENDSTATE_W_PACKET; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + // Don't queue him if the connection is stopping. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) { + Connection->Flags |= CONNECTION_FLAGS_SUSPENDED; + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + InsertTailList (&DeviceContext->PacketWaitQueue, &Connection->PacketWaitLinkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + StDereferenceConnection ("No packet", Connection); + return; + + } + + // + // Add a reference count to the IRP, and keep track of + // which request it is. Send completion will remove the + // reference. + + IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp); + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_I_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)IrpSp; + + Packet->CompleteSend = FALSE; + + + // + // Build the MAC header. All frames go out as + // single-route source routing. + // + + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + // + // Build the header: 'I', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_INFORMATION; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StReferenceSendIrp ("Packetize", IrpSp); + + // + // Save our complex send pointer in case we have to restore it + // because StNdisSend fails. + // + + SavedSendPointer = Connection->sp; + + // + // build an NDIS_BUFFER chain that describes the buffer we're using, and + // thread it off the NdisBuffer. This chain may not complete the + // packet, as the remaining part of the MDL chain may be shorter than + // the packet. + // + + FrameSize = MaxFrameSize; + + // + // Check if we have less than FrameSize left to send. + // + + if (Connection->sp.MessageBytesSent + FrameSize > Connection->CurrentSendLength) { + + FrameSize = Connection->CurrentSendLength - Connection->sp.MessageBytesSent; + + } +#if 27 + if (StNoisySend) { + DbgPrint ("Send %d of %d\n", FrameSize, Connection->CurrentSendLength); + } + if (FrameSize > 1000) { + StSnds[StSndLoc] = FrameSize; + StSndLoc = (StSndLoc + 1) % 10; + } +#endif + + + // + // Make a copy of the MDL chain for this send, unless + // there are zero bytes left. + // + + if (FrameSize != 0) { + + // + // If the whole send will fit inside one packet, + // then there is no need to duplicate the MDL + // (note that this may include multi-MDL sends). + // + + if ((Connection->sp.SendByteOffset == 0) && + (Connection->CurrentSendLength == FrameSize)) { + + PacketDescriptor = (PNDIS_BUFFER)Connection->sp.CurrentSendMdl; + PacketBytes = FrameSize; + Connection->sp.CurrentSendMdl = NULL; + Connection->sp.SendByteOffset = FrameSize; + Packet->PacketNoNdisBuffer = TRUE; + Status = STATUS_SUCCESS; + + } else { + + Status = BuildBufferChainFromMdlChain ( + DeviceContext->NdisBufferPoolHandle, + Connection->sp.CurrentSendMdl, + Connection->sp.SendByteOffset, + FrameSize, + &PacketDescriptor, + &Connection->sp.CurrentSendMdl, + &Connection->sp.SendByteOffset, + &PacketBytes); + + } + + } else { + + PacketBytes = 0; + Connection->sp.CurrentSendMdl = NULL; + Status = STATUS_SUCCESS; + + } + + if (NT_SUCCESS (Status)) { + + Connection->sp.MessageBytesSent += PacketBytes; + + // + // Chain the buffers to the packet, unless there + // are zero bytes of data. + // + + if (FrameSize != 0) { + NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor); + } + + + // + // Have we run out of Mdl Chain in this request? + // + + if ((PacketBytes < FrameSize) || + (Connection->sp.CurrentSendMdl == NULL) || + (Connection->CurrentSendLength <= Connection->sp.MessageBytesSent)) { + + // + // Yep. We know that we've exhausted the current request's buffer + // here, so see if there's another request without EOF set that we + // can build start throwing into this packet. + // + + + if (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) { + + // + // We are sending the last packet in a message. Change + // the packet type to a "last" frame. + // + + StHeader->Flags |= ST_FLAGS_LAST; + Packet->CompleteSend = TRUE; + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + } else { + + // + // We are sending the last packet in this request. If there + // are more requests in the connection's SendQueue, then + // advance complex send pointer to point to the next one + // in line. Otherwise, if there aren't any more requests + // ready to packetize, then we enter the W_EOR state and + // stop packetizing. Note that we're waiting here for the TDI + // client to come up with data to send; we're just hanging out + // until then. + // + + if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) { + + Connection->SendState = CONNECTION_SENDSTATE_W_EOR; + + } else { + + Connection->sp.CurrentSendIrp = + CONTAINING_RECORD ( + Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink, + IRP, + Tail.Overlay.ListEntry); + Connection->sp.CurrentSendMdl = + Connection->sp.CurrentSendIrp->MdlAddress; + Connection->sp.SendByteOffset = 0; + Connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + } + } + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + LastPacketLength = sizeof(ST_HEADER) + PacketBytes; + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + LastPacketLength); + + Packet->NdisIFrameLength = LastPacketLength; + + StNdisSend (Packet); + + // + // Update our counters (this is done unprotected by a lock). + // + + ADD_TO_LARGE_INTEGER( + &DeviceContext->IFrameBytesSent, + PacketBytes); + ++DeviceContext->IFramesSent; + + } else { + + // + // BuildBufferChainFromMdlChain failed; we need to + // release the lock since the long if() above + // exits with it released. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } + + // + // Note that we may have fallen out of the BuildBuffer... if above with + // Status set to STATUS_INSUFFICIENT_RESOURCES. if we have, we'll just + // stick this connection back onto the packetize queue and hope the + // system gets more resources later. + // + + + if (!NT_SUCCESS (Status)) { + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // Restore old complex send pointer. + // + + Connection->sp = SavedSendPointer; + + + // + // Indicate we're waiting on favorable link conditions. + // + + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + StDestroyPacket (Packet); + StDereferenceSendIrp("Send failed", IrpSp); + StDereferenceConnection ("Send failed", Connection); + + return; + } + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // It is probable that a frame arrived while we released + // the connection's spin lock, so our state has probably changed. + // When we cycle around this loop again, we will have the lock + // again, so we can test the connection's send state. + // + + } while (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE); + + // + // Clear the PACKETIZE flag, indicating that we're no longer on the + // PacketizeQueue or actively packetizing. The flag was set by + // StartPacketizingConnection to indicate that the connection was + // already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + StDereferenceConnection ("PacketizeSend done", Connection); + +} /* PacketizeSend */ + + +VOID +CompleteSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to complete a TDI send back to the + caller. In the sample transport we assume that all sends + complete successfully, but in other transports we would + wait for a response from the remote. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, cancelirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + + // + // Pick off TP_REQUEST objects from the connection's SendQueue until + // we find one with an END_OF_RECORD mark embedded in it. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + while (TRUE) { + + // + // We know for a fact that we wouldn't be calling this routine if + // we hadn't completed sending an entire message, since we + // only set the ST_LAST bit in that case. Therefore, we + // know that we will run into a request with the END_OF_RECORD + // mark set BEFORE we will run out of requests on that queue, + // so there is no reason to check to see if we ran off the end. + // Note that it's possible that the send has been failed and the + // connection not yet torn down; if this has happened, we could be + // removing from an empty queue here. Make sure that doesn't happen. + // + + if (Connection->SendQueue.Flink == &Connection->SendQueue) { + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // no requests to complete, things must have failed; just get out. + // + + break; + } + + p = RemoveHeadList (&Connection->SendQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + + Irp->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + // + // Complete the send. Note that this may not actually call + // IoCompleteRequest for the Irp until sometime later, if the + // in-progress LLC resending going on below us needs to complete. + // + + StCompleteSendIrp ( + Irp, + STATUS_SUCCESS, + IRP_SEND_LENGTH(IrpSp)); + + if (EndOfRecord) { + break; + } + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + }; + + // + // Acquire the lock that we will return with. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // We've finished processing the current send. Update our state. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + // + // If there is another send pending on the connection, then initialize + // it and start packetizing it. + // + + if (!(IsListEmpty (&Connection->SendQueue))) { + + InitializeSend (Connection); + + // + // This code is similar to calling StartPacketizingConnection + // with the second parameter FALSE. + // + + if ((!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) && + (!(Connection->Flags & CONNECTION_FLAGS_STOPPING))) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + StReferenceConnection ("Packetize", Connection); + + ExInterlockedInsertTailList( + &Connection->Provider->PacketizeQueue, + &Connection->PacketizeLinkage, + &Connection->Provider->SpinLock); + + } + + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + +} /* CompleteSend */ + + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ) + +/*++ + +Routine Description: + + This routine is called because something on the link caused this send to be + unable to complete. There are a number of possible reasons for this to have + happened, but all will fail with the common error STATUS_LINK_FAILED. + or NO_RECEIVE response where the number of bytes specified exactly + Here we retire all of the TdiSends on the connection's SendQueue up to + and including the current one, which is the one that failed. + + Later - Actually, a send failing is cause for the entire circuit to wave + goodbye to this life. We now simply tear down the connection completly. + Any future sends on this connection will be blown away. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, cancelirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + BOOLEAN GotCurrent = FALSE; + + + // + // Pick off IRP objects from the connection's SendQueue until + // we get to this one. If this one does NOT have an EOF mark set, we'll + // need to keep going until we hit one that does have EOF set. Note that + // this may cause us to continue failing sends that have not yet been + // queued. (We do all this because ST does not provide stream mode sends.) + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + StReferenceConnection ("Failing Send", Connection); + + do { + if (IsListEmpty (&Connection->SendQueue)) { + + // + // got an empty list, so we've run out of send requests to fail + // without running into an EOR. Set the connection flag that will + // cause all further sends to be failed up to an EOR and get out + // of here. + // + + Connection->Flags |= CONNECTION_FLAGS_FAILING_TO_EOR; + break; + } + p = RemoveHeadList (&Connection->SendQueue); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + if (Irp == Connection->sp.CurrentSendIrp) { + GotCurrent = TRUE; + } + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + StCompleteSendIrp (Irp, RequestStatus, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } while (!EndOfRecord & !GotCurrent); + + // + // We've finished processing the current send. Update our state. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->sp.CurrentSendIrp = NULL; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + + if (StopConnection) { + StStopConnection (Connection, STATUS_LINK_FAILED); + } + + StDereferenceConnection ("FailSend", Connection); + +} /* FailSend */ + + +VOID +InitializeSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called whenever the next send on a connection should + be initialized; that is, all of the fields associated with the state + of the current send are set to refer to the first send on the SendQueue. + + WARNING: This routine is executed with the Connection lock acquired + since it must be atomically executed with the caller's setup. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + if (Connection->SendQueue.Flink != &Connection->SendQueue) { + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + Connection->FirstSendIrp = + CONTAINING_RECORD (Connection->SendQueue.Flink, IRP, Tail.Overlay.ListEntry); + Connection->FirstSendMdl = Connection->FirstSendIrp->MdlAddress; + Connection->FirstSendByteOffset = 0; + Connection->sp.MessageBytesSent = 0; + Connection->sp.CurrentSendIrp = Connection->FirstSendIrp; + Connection->sp.CurrentSendMdl = Connection->FirstSendMdl; + Connection->sp.SendByteOffset = Connection->FirstSendByteOffset; + Connection->CurrentSendLength = + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + + } +} /* InitializeSend */ + + +VOID +StCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send. + The send is found on the connection's send queue; if it is the + current request it is cancelled and the connection is torn down, + otherwise it is silently cancelled. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PIRP SendIrp; + PLIST_ENTRY p; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (DeviceObject); + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (IrpSp->MinorFunction == TDI_SEND)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + // + // See if this is the IRP for the current send request. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + StReferenceConnection ("Cancelling Send", Connection); + + p = Connection->SendQueue.Flink; + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + if (SendIrp == Irp) { + + // + // yes, it is the first one on the send queue, so + // trash the send/connection. + // + + p = RemoveHeadList (&Connection->SendQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + + // + // Since we are cancelling the current send, blow away + // the connection. + // + + StStopConnection (Connection, STATUS_CANCELLED); + + } else { + + // + // Scan through the list, looking for this IRP. + // + + Found = FALSE; + p = p->Flink; + while (p != &Connection->SendQueue) { + + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (SendIrp == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + + } + + StDereferenceConnection ("Cancelling Send", Connection); + +} + + + +VOID +StSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + NdisContext - the value associated with the adapter binding at adapter + open time (which adapter we're talking on). + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSEND_PACKET_TAG SendContext; + PTP_PACKET Packet; + KIRQL oldirql, cancelirql; + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PIO_STACK_LOCATION IrpSp; + PTP_REQUEST request; + TA_NETBIOS_ADDRESS TempAddress; + ULONG returnLength; + NTSTATUS status; + PTDI_CONNECTION_INFORMATION remoteInformation; + + UNREFERENCED_PARAMETER(ProtocolBindingContext); + + SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0]; + Packet = SendContext->Packet; + + DeviceContext = Packet->Provider; + + Packet->PacketSent = TRUE; + + switch (SendContext->Type) { + + case TYPE_I_FRAME: + + // + // Dereference the IRP that this packet was sent for. + // + + IrpSp = (PIO_STACK_LOCATION)(SendContext->Owner); + + if (Packet->CompleteSend) { + CompleteSend(IRP_CONNECTION(IrpSp)); + } + StDereferenceSendIrp("Destroy packet", IrpSp); + break; + + case TYPE_D_FRAME: + + // + // Finish tearing down the connection. + // + + StDereferenceConnection("Disconnect completed", (PTP_CONNECTION)(SendContext->Owner)); + break; + + case TYPE_G_FRAME: + + // + // Addresses get their own frames; let the address know it's ok to + // use the frame again, and exit to avoid normal packet completion. + // + + StSendDatagramCompletion ((PTP_ADDRESS)(SendContext->Owner), + NdisPacket, + NdisStatus); + return; + + case TYPE_C_FRAME: + + // + // Complete the TdiConnect request; note that he better + // have accepted it quickly since we will immediately + // start sending data if required. + // + + Connection = (PTP_CONNECTION)(SendContext->Owner); + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + p = RemoveHeadList (&Connection->InProgressRequest); + + // + // Turn off the connection request timer if there is one, and set + // this connection's state to READY. + // + + Connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + // + // Record that the connect request has been successfully + // completed by TpCompleteRequest. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + // + // Now complete the request and get out. + // + + if (p == &Connection->InProgressRequest) { + Connection->IndicationInProgress = FALSE; + PANIC ("ProcessSessionConfirm: TdiConnect evaporated!\n"); + IoReleaseCancelSpinLock (cancelirql); + break; + } + + // + // We have a completed connection with a queued connect. Complete + // the connect. + // + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + IrpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&IrpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + RtlCopyMemory( Connection->RemoteName, Connection->CalledAddress.NetbiosName, 16 ); + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + // + // Reference the connection so it stays around after + // the request is completed. + // + + StReferenceConnection("Connect completed", Connection); + + StCompleteRequest (request, status, returnLength); + + break; + + } + + StDestroyPacket(Packet); + +} /* StSendCompletionHandler */ + + +VOID +StNdisSend( + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine sends an NDIS packet + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of NdisSend + and after assigning the receive sequence number it locks out other + sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: This routine is called with the link spinlock held, + and it returns with it released. + +Arguments: + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + + NdisSend ( + &NdisStatus, + ((PDEVICE_CONTEXT)(Packet->Provider))->NdisBindingHandle, + Packet->NdisPacket); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + StSendCompletionHandler( + Packet->Provider, + Packet->NdisPacket, + NdisStatus); + } + +} /* StNdisSend */ + + + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source Mdl chain and + offset into it. We assume we don't know the length of the source Mdl chain, + and we must allocate the NDIS_BUFFERs for the destination chain, which + we do from the NDIS buffer pool. + + The NDIS_BUFFERs that are returned are mapped and locked. (Actually, the pages in + them are in the same state as those in the source MDLs.) + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. TrueLength is set to 0. + +Environment: + + Kernel Mode, Source Mdls locked. It is recommended, although not required, + that the source Mdls be mapped and locked prior to calling this routine. + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentMdl - Points to the start of the Mdl chain from which to draw the + packet. + + ByteOffset - Offset within this MDL to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + Destination - returned pointer to the NDIS_BUFFER chain describing the packet. + + NewCurrentMdl - returned pointer to the Mdl that would be used for the next + byte of packet. NULL if the source Mdl chain was exhausted. + + NewByteOffset - returned offset into the NewCurrentMdl for the next byte of + packet. NULL if the source Mdl chain was exhausted. + + TrueLength - The actual length of the returned NDIS_BUFFER Chain. If less than + DesiredLength, the source Mdl chain was exhausted. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded (even if + shorter than the desired chain). + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while building the + destination chain. + +--*/ +{ + ULONG AvailableBytes; + PMDL OldMdl; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + + AvailableBytes = MmGetMdlByteCount (CurrentMdl) - ByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + OldMdl = CurrentMdl; + *NewCurrentMdl = OldMdl; + *NewByteOffset = ByteOffset + AvailableBytes; + *TrueLength = AvailableBytes; + + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + BufferPoolHandle, + OldMdl, + ByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *Destination = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *Destination = NewNdisBuffer; + + // + // Was the first NDIS_BUFFER enough data, or are we out of Mdls? + // + + if ((AvailableBytes == DesiredLength) || (OldMdl->Next == NULL)) { + if (*NewByteOffset >= MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + while (OldMdl != NULL) { + AvailableBytes = DesiredLength - *TrueLength; + if (AvailableBytes > MmGetMdlByteCount (OldMdl)) { + AvailableBytes = MmGetMdlByteCount (OldMdl); + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + BufferPoolHandle, + OldMdl, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*Destination != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*Destination); + NdisFreeBuffer (*Destination); + *Destination = NewNdisBuffer; + } + + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *NewCurrentMdl = CurrentMdl; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + *TrueLength += AvailableBytes; + *NewByteOffset = AvailableBytes; + + if (*TrueLength == DesiredLength) { + if (*NewByteOffset == MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + } // while (mdl chain exists) + + *NewCurrentMdl = NULL; + *NewByteOffset = 0; + return STATUS_SUCCESS; + +} // BuildBufferChainFromMdlChain + diff --git a/private/ntos/tdi/st/sources b/private/ntos/tdi/st/sources new file mode 100644 index 000000000..f47518792 --- /dev/null +++ b/private/ntos/tdi/st/sources @@ -0,0 +1,62 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=st + +TARGETNAME=st +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc;..\..\..\inc + +SOURCES=\ + address.c \ + connect.c \ + connobj.c \ + devctx.c \ + event.c \ + framesnd.c \ + iframes.c \ + ind.c \ + info.c \ + packet.c \ + rcv.c \ + rcveng.c \ + request.c \ + send.c \ + sendeng.c \ + st.rc \ + stcnfg.c \ + stdrvr.c \ + stmac.c \ + stndis.c \ + uframes.c + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF diff --git a/private/ntos/tdi/st/st.h b/private/ntos/tdi/st/st.h new file mode 100644 index 000000000..590ccac89 --- /dev/null +++ b/private/ntos/tdi/st/st.h @@ -0,0 +1,46 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + st.h + +Abstract: + + Private include file for the NT Sample transport provider. + +Revision History: + +--*/ + +#ifndef _ST_ +#define _ST_ + +#include + +#include // these two are needed by info.c +#include + +#include // Transport Driver Interface. +#include // Network Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "stconst.h" // private constants. +#include "stmac.h" // mac-specific definitions +#include "sthdrs.h" // private protocol headers. +#include "sttypes.h" // private types. +#include "stcnfg.h" // configuration information. +#include "stprocs.h" // private function prototypes. + + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) + + +#endif // def _ST_ diff --git a/private/ntos/tdi/st/st.rc b/private/ntos/tdi/st/st.rc new file mode 100644 index 000000000..591897aa9 --- /dev/null +++ b/private/ntos/tdi/st/st.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "Sample TDI 3.0 Transport Driver" +#define VER_INTERNALNAME_STR "st.sys" +#define VER_ORIGINALFILENAME_STR "st.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/st/stcnfg.c b/private/ntos/tdi/st/stcnfg.c new file mode 100644 index 000000000..40438eedb --- /dev/null +++ b/private/ntos/tdi/st/stcnfg.c @@ -0,0 +1,1277 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stcnfg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of ST. + +Revision History: + +--*/ + +#include "st.h" + + +// +// Local functions used to access the registry. +// + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ); + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +StOpenParametersKey( + IN HANDLE StConfigHandle, + OUT PHANDLE ParametersHandle + ); + +VOID +StCloseParametersKey( + IN HANDLE ParametersHandle + ); + +NTSTATUS +StCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +StAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +StAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +VOID +StReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ); + +UINT +StReadSizeInformation( + IN HANDLE ParametersHandle + ); + +ULONG +StReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ); + +VOID +StWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ); + +VOID +StSaveConfigInRegistry( + IN HANDLE ParametersHandle, + IN PCONFIG_DATA ConfigurationInfo + ); + +UINT +StWstrLength( + IN PWSTR Wstr + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,StWstrLength) +#pragma alloc_text(INIT,StConfigureTransport) +#pragma alloc_text(INIT,StFreeConfigurationInfo) +#pragma alloc_text(INIT,StOpenParametersKey) +#pragma alloc_text(INIT,StCloseParametersKey) +#pragma alloc_text(INIT,StCountEntries) +#pragma alloc_text(INIT,StAddBind) +#pragma alloc_text(INIT,StAddExport) +#pragma alloc_text(INIT,StReadLinkageInformation) +#pragma alloc_text(INIT,StReadSingleParameter) +#pragma alloc_text(INIT,StWriteSingleParameter) +#pragma alloc_text(INIT,StSaveConfigInRegistry) +#endif + + +UINT +StWstrLength( + IN PWSTR Wstr + ) +{ + UINT Length = 0; + while (*Wstr++) { + Length += sizeof(WCHAR); + } + return Length; +} + +#define InsertAdapter(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = StWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePool(NonPagedPool, _L); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[Subscript], _S); \ + } \ +} + +#define InsertDevice(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = StWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePool(NonPagedPool, _L); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript], _S); \ + } \ +} + + +#define RemoveAdapter(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[Subscript].Buffer) + +#define RemoveDevice(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript].Buffer) + + + +// +// These strings are used in various places by the registry. +// + +#define DECLARE_STRING(_str_) STATIC WCHAR Str ## _str_[] = L#_str_ + +DECLARE_STRING(Large); +DECLARE_STRING(Medium); +DECLARE_STRING(Small); + +DECLARE_STRING(InitRequests); +DECLARE_STRING(InitConnections); +DECLARE_STRING(InitAddressFiles); +DECLARE_STRING(InitAddresses); + +DECLARE_STRING(MaxRequests); +DECLARE_STRING(MaxConnections); +DECLARE_STRING(MaxAddressFiles); +DECLARE_STRING(MaxAddresses); + +DECLARE_STRING(InitPackets); +DECLARE_STRING(InitReceivePackets); +DECLARE_STRING(InitReceiveBuffers); + +DECLARE_STRING(SendPacketPoolSize); +DECLARE_STRING(ReceivePacketPoolSize); +DECLARE_STRING(MaxMemoryUsage); + + +#define READ_HIDDEN_CONFIG(_Field) \ +{ \ + ConfigurationInfo->_Field = \ + StReadSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + +#define WRITE_HIDDEN_CONFIG(_Field) \ +{ \ + StWriteSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + + + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ) +/*++ + +Routine Description: + + This routine is called by ST to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in nbfcnfg.h file. + +Arguments: + + RegistryPath - The name of ST's node in the registry. + + ConfigurationInfoPtr - A pointer to the configuration information structure. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + + NTSTATUS OpenStatus; + HANDLE ParametersHandle; + UINT StSize; + HANDLE StConfigHandle; + NTSTATUS Status; + ULONG Disposition; + PWSTR RegistryPathBuffer; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PCONFIG_DATA ConfigurationInfo; + + + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &StConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) { + StPrint1("ST: Could not open/create ST key: %lx\n", Status); + return Status; + } + + + OpenStatus = StOpenParametersKey (StConfigHandle, &ParametersHandle); + + if (OpenStatus != STATUS_SUCCESS) { + return OpenStatus; + } + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // StReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)ExAllocatePool( + NonPagedPool, + RegistryPath->Length + sizeof(WCHAR)); + if (RegistryPathBuffer == NULL) { + StCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + StReadLinkageInformation (RegistryPathBuffer, ConfigurationInfoPtr); + + if (*ConfigurationInfoPtr == NULL) { + ExFreePool (RegistryPathBuffer); + StCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + ConfigurationInfo = *ConfigurationInfoPtr; + + + // + // Read the size parameter; this returns 0 if none is + // present, or 1 (Small), 2 (Medium) and 3 (Large). + // + + StSize = StReadSizeInformation (ParametersHandle); + + switch (StSize) { + + case 0: + case 1: + + // + // Default is Small. + // + + // + // These are the initial value used; the comment after + // each one shows the expected maximum (if every resource + // is at the expected maximum, ST should be very close + // to being out of memory). + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 5; // 30 + ConfigurationInfo->InitConnections = 1; // 10 + ConfigurationInfo->InitAddressFiles = 0; // 10 + ConfigurationInfo->InitAddresses = 0; // 10 + + // + // These are the initial values; remember that the + // resources above also allocate some of these each + // time they are allocated (shown in the comment). + // + + ConfigurationInfo->InitPackets = 30; // + link + conn (40) + ConfigurationInfo->InitReceivePackets = 10; // + link + addr (30) + ConfigurationInfo->InitReceiveBuffers = 5; // + addr (15) + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 100; + ConfigurationInfo->ReceivePacketPoolSize = 30; + ConfigurationInfo->MaxMemoryUsage = 100000; + + break; + + case 2: + + // + // Medium ST. + // + + // + // These are the initial value used; the comment after + // each one shows the expected maximum (if every resource + // is at the expected maximum, ST should be very close + // to being out of memory). + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 10; // 100 + ConfigurationInfo->InitConnections = 2; // 64 + ConfigurationInfo->InitAddressFiles = 1; // 20 + ConfigurationInfo->InitAddresses = 1; // 20 + + // + // These are the initial values; remember that the + // resources above also allocate some of these each + // time they are allocated (shown in the comment). + // + + ConfigurationInfo->InitPackets = 50; // + link + conn (150) + ConfigurationInfo->InitReceivePackets = 15; // + link + addr (100) + ConfigurationInfo->InitReceiveBuffers = 10; // + addr (30) + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 250; + ConfigurationInfo->ReceivePacketPoolSize = 100; + ConfigurationInfo->MaxMemoryUsage = 250000; + + break; + + case 3: + + // + // Big ST. + // + + // + // These are the initial value used. + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 15; + ConfigurationInfo->InitConnections = 3; + ConfigurationInfo->InitAddressFiles = 2; + ConfigurationInfo->InitAddresses = 2; + + // + // These are the initial values; remember that the + // resources above also allocate some of these each + // time they are allocated (shown in the comment). + // + + ConfigurationInfo->InitPackets = 75; // + link + conn + ConfigurationInfo->InitReceivePackets = 25; // + link + addr + ConfigurationInfo->InitReceiveBuffers = 20; // + addr + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 500; + ConfigurationInfo->ReceivePacketPoolSize = 200; + ConfigurationInfo->MaxMemoryUsage = 0; // no limit + + break; + + default: + + ASSERT(FALSE); + break; + + } + + + // + // Now read the optional "hidden" parameters; if these do + // not exist then the current values are used. Note that + // the current values will be 0 unless they have been + // explicitly initialized above. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + READ_HIDDEN_CONFIG (InitRequests); + READ_HIDDEN_CONFIG (InitConnections); + READ_HIDDEN_CONFIG (InitAddressFiles); + READ_HIDDEN_CONFIG (InitAddresses); + + READ_HIDDEN_CONFIG (MaxRequests); + READ_HIDDEN_CONFIG (MaxConnections); + READ_HIDDEN_CONFIG (MaxAddressFiles); + READ_HIDDEN_CONFIG (MaxAddresses); + + READ_HIDDEN_CONFIG (InitPackets); + READ_HIDDEN_CONFIG (InitReceivePackets); + READ_HIDDEN_CONFIG (InitReceiveBuffers); + + READ_HIDDEN_CONFIG (SendPacketPoolSize); + READ_HIDDEN_CONFIG (ReceivePacketPoolSize); + READ_HIDDEN_CONFIG (MaxMemoryUsage); + + + // + // Now that we are completely configured, save the information + // in the registry. + // + + StSaveConfigInRegistry (ParametersHandle, ConfigurationInfo); + + ExFreePool (RegistryPathBuffer); + StCloseParametersKey (ParametersHandle); + ZwClose (StConfigHandle); + + return STATUS_SUCCESS; + +} /* StConfigureTransport */ + + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST to get free any storage that was allocated + by StConfigureTransport in producing the specified CONFIG_DATA structure. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + UINT i; + + for (i=0; iNumAdapters; i++) { + RemoveAdapter (ConfigurationInfo, i); + RemoveDevice (ConfigurationInfo, i); + } + ExFreePool (ConfigurationInfo); + +} /* StFreeConfigurationInfo */ + + +NTSTATUS +StOpenParametersKey( + IN HANDLE StConfigHandle, + OUT PHANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST to open the ST "Parameters" key. + +Arguments: + + ParametersHandle - Returns the handle used to read parameters. + +Return Value: + + The status of the request. + +--*/ +{ + + NTSTATUS Status; + HANDLE ParamHandle; + PWSTR ParametersString = L"Parameters"; + UNICODE_STRING ParametersKeyName; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + // + // Open the ST parameters key. + // + + RtlInitUnicodeString (&ParametersKeyName, ParametersString); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &ParametersKeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + StConfigHandle, // root + NULL // security descriptor + ); + + + Status = ZwOpenKey( + &ParamHandle, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + StPrint1("Could not open parameters key: %lx\n", Status); + return Status; + + } + + *ParametersHandle = ParamHandle; + + + // + // All keys successfully opened or created. + // + + return STATUS_SUCCESS; + +} /* StOpenParametersKey */ + +VOID +StCloseParametersKey( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST to close the "Parameters" key. + It closes the handles passed in and does any other work needed. + +Arguments: + + ParametersHandle - The handle used to read other parameters. + +Return Value: + + None. + +--*/ + +{ + + ZwClose (ParametersHandle); + +} /* StCloseParametersKey */ + + +NTSTATUS +StCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called with the "Bind" and "Export" multi-strings. + It counts the number of name entries required in the + CONFIGURATION_DATA structure and then allocates it. + +Arguments: + + ValueName - The name of the value ("Bind" or "Export" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to a pointer to the ConfigurationInfo structure. + When the "Export" callback is made this is filled in + with the allocate structure. + + EntryContext - A pointer to a counter holding the total number + of name entries required. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + ULONG StringCount; + PWCHAR ValuePointer = (PWCHAR)ValueData; + PCONFIG_DATA * ConfigurationInfo = (PCONFIG_DATA *)Context; + PULONG TotalCount = ((PULONG)EntryContext); + ULONG OldTotalCount = *TotalCount; + + ASSERT (ValueType == REG_MULTI_SZ); + + // + // Count the number of strings in the multi-string; first + // check that it is NULL-terminated to make the rest + // easier. + // + + if ((ValueLength < 2) || + (ValuePointer[(ValueLength/2)-1] != (WCHAR)'\0')) { + return STATUS_INVALID_PARAMETER; + } + + StringCount = 0; + while (*ValuePointer != (WCHAR)'\0') { + while (*ValuePointer != (WCHAR)'\0') { + ++ValuePointer; + } + ++StringCount; + ++ValuePointer; + if ((ULONG)((PUCHAR)ValuePointer - (PUCHAR)ValueData) >= ValueLength) { + break; + } + } + + (*TotalCount) += StringCount; + + if (*ValueName == (WCHAR)'E') { + + // + // This is "Export", allocate the config data structure. + // + + *ConfigurationInfo = ExAllocatePool( + NonPagedPool, + sizeof (CONFIG_DATA) + + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + if (*ConfigurationInfo == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( + *ConfigurationInfo, + sizeof(CONFIG_DATA) + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + (*ConfigurationInfo)->DevicesOffset = OldTotalCount; + + } + + return STATUS_SUCCESS; + +} /* StCountEntries */ + + +NTSTATUS +StAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurBindNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertAdapter( + ConfigurationInfo, + *CurBindNum, + (PWSTR)(ValueData)); + + ++(*CurBindNum); + + return STATUS_SUCCESS; + +} /* StAddBind */ + + +NTSTATUS +StAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of exports that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurExportNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertDevice( + ConfigurationInfo, + *CurExportNum, + (PWSTR)(ValueData)); + + ++(*CurExportNum); + + return STATUS_SUCCESS; + +} /* StAddExport */ + + +VOID +StReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST to read its linkage information + from the registry. If there is none present, then ConfigData + is filled with a list of all the adapters that are known + to ST. + +Arguments: + + RegistryPathBuffer - The null-terminated root of the ST registry tree. + + ConfigurationInfo - Returns ST's current configuration. + +Return Value: + + None. + +--*/ + +{ + + UINT ConfigBindings; + UINT NameCount = 0; + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[6]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG BindCount, ExportCount; + UINT i; + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below ST + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call StCountEntries for the "Bind" multi-string + // + + QueryTable[1].QueryRoutine = StCountEntries; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&NameCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call StCountEntries for the "Export" multi-string + // + + QueryTable[2].QueryRoutine = StCountEntries; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[2].Name = Export; + QueryTable[2].EntryContext = (PVOID)&NameCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4) Call StAddBind for each string in "Bind" + // + + QueryTable[3].QueryRoutine = StAddBind; + QueryTable[3].Flags = 0; + QueryTable[3].Name = Bind; + QueryTable[3].EntryContext = (PVOID)&BindCount; + QueryTable[3].DefaultType = REG_NONE; + + // + // 5) Call StAddExport for each string in "Export" + // + + QueryTable[4].QueryRoutine = StAddExport; + QueryTable[4].Flags = 0; + QueryTable[4].Name = Export; + QueryTable[4].EntryContext = (PVOID)&ExportCount; + QueryTable[4].DefaultType = REG_NONE; + + // + // 6) Stop + // + + QueryTable[5].QueryRoutine = NULL; + QueryTable[5].Flags = 0; + QueryTable[5].Name = NULL; + + + BindCount = 0; + ExportCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPathBuffer, + QueryTable, + (PVOID)ConfigurationInfo, + NULL); + + if (Status != STATUS_SUCCESS) { + return; + } + + // + // Make sure that BindCount and ExportCount match, if not + // remove the extras. + // + + if (BindCount < ExportCount) { + + for (i=BindCount; iNumAdapters = ConfigBindings; + +} /* StReadLinkageInformation */ + + +UINT +StReadSizeInformation( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST to read the Size information + from the registry. + +Arguments: + + RegistryHandle - A pointer to the open registry. + +Return Value: + + 0 - no Size specified + 1 - Small + 2 - Medium + 3 - Big / Large + +--*/ + +{ + + UINT SizeToReturn; +// STRING KeywordName; +// PCONFIG_KEYWORD Keyword; + + ULONG InformationBuffer[16]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + ULONG InformationLength; + WCHAR SizeString[] = L"Size"; + UNICODE_STRING SizeValueName; + NTSTATUS Status; + PUCHAR InformationData; + ULONG InformationLong; + + + // + // Read the size parameter out of the registry. + // + + RtlInitUnicodeString (&SizeValueName, SizeString); + + Status = ZwQueryValueKey( + ParametersHandle, + &SizeValueName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + // + // Compare to the expected values. + // + + if (Status == STATUS_SUCCESS) { + + InformationData = ((PUCHAR)Information) + Information->DataOffset; + InformationLong = *((PULONG)InformationData); + + if ((Information->DataLength == sizeof(ULONG)) && + (InformationLong >= 1 && InformationLong <= 3)) { + + SizeToReturn = InformationLong; + + } else { + + if ((Information->DataLength >= 10) && + (RtlEqualMemory (StrLarge, InformationData, 10))) { + + SizeToReturn = 3; + + } else if ((Information->DataLength >= 12) && + (RtlEqualMemory (StrMedium, InformationData, 12))) { + + SizeToReturn = 2; + + } else if ((Information->DataLength >= 10) && + (RtlEqualMemory (StrSmall, InformationData, 10))) { + + SizeToReturn = 1; + + } else { + + SizeToReturn = 0; + + } + + } + + } else { + + SizeToReturn = 0; + + } + + return SizeToReturn; + +} /* StReadSizeInformation */ + + +ULONG +StReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ) + +/*++ + +Routine Description: + + This routine is called by ST to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + ULONG InformationBuffer[16]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + UNICODE_STRING ValueKeyName; + ULONG InformationLength; + ULONG ReturnValue; + NTSTATUS Status; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwQueryValueKey( + ParametersHandle, + &ValueKeyName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + if ((Status == STATUS_SUCCESS) && (Information->DataLength == sizeof(ULONG))) { + + RtlCopyMemory( + (PVOID)&ReturnValue, + ((PUCHAR)Information) + Information->DataOffset, + sizeof(ULONG)); + + if (ReturnValue < 0) { + + ReturnValue = DefaultValue; + + } + + } else { + + ReturnValue = DefaultValue; + + } + + return ReturnValue; + +} /* StReadSingleParameter */ + + +VOID +StWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ) + +/*++ + +Routine Description: + + This routine is called by ST to write a single parameter + from the registry. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to store. + + ValueData - The data to store at the value. + +Return Value: + + None. + +--*/ + +{ + UNICODE_STRING ValueKeyName; + NTSTATUS Status; + ULONG TmpValueData = ValueData; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwSetValueKey( + ParametersHandle, + &ValueKeyName, + 0, + REG_DWORD, + (PVOID)&TmpValueData, + sizeof(ULONG)); + + if (!NT_SUCCESS(Status)) { + StPrint1("ST: Could not write dword key: %lx\n", Status); + } + +} /* StWriteSingleParameter */ + + +VOID +StSaveConfigInRegistry( + IN HANDLE ParametersHandle, + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST to save its configuraition + information in the registry. It saves the information if + the registry structure did not exist before this boot. + +Arguments: + + ParametersHandle - The handle used to read other parameters. + + ConfigurationInfo - Describes ST's current configuration. + +Return Value: + + None. + +--*/ + +{ + + // + // Save the "hidden" parameters, these may not exist in + // the registry. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + // + // Don't write the parameters that are set + // based on Size, since otherwise these will overwrite + // those values since hidden parameters are set up + // after the Size-based configuration is done. + // + + WRITE_HIDDEN_CONFIG (MaxRequests); + WRITE_HIDDEN_CONFIG (MaxConnections); + WRITE_HIDDEN_CONFIG (MaxAddressFiles); + WRITE_HIDDEN_CONFIG (MaxAddresses); + +} /* StSaveConfigInRegistry */ + diff --git a/private/ntos/tdi/st/stcnfg.h b/private/ntos/tdi/st/stcnfg.h new file mode 100644 index 000000000..d09098a6c --- /dev/null +++ b/private/ntos/tdi/st/stcnfg.h @@ -0,0 +1,57 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stcnfg.h + +Abstract: + + Private include file for the NT Sample transport. This + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#ifndef _STCONFIG_ +#define _STCONFIG_ + +// +// configuration structure. +// + +typedef struct { + + ULONG InitRequests; + ULONG InitConnections; + ULONG InitAddressFiles; + ULONG InitAddresses; + ULONG MaxRequests; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + ULONG InitPackets; + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxMemoryUsage; + + // + // Names contains NumAdapters pairs of NDIS adapter names (which + // nbf binds to) and device names (which nbf exports). The nth + // adapter name is in location n and the device name is in + // DevicesOffset+n (DevicesOffset may be different from NumAdapters + // if the registry Bind and Export strings are different sizes). + // + + ULONG NumAdapters; + ULONG DevicesOffset; + NDIS_STRING Names[1]; + +} CONFIG_DATA, *PCONFIG_DATA; + +#endif diff --git a/private/ntos/tdi/st/stconst.h b/private/ntos/tdi/st/stconst.h new file mode 100644 index 000000000..45837dc71 --- /dev/null +++ b/private/ntos/tdi/st/stconst.h @@ -0,0 +1,127 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stconst.h + +Abstract: + + This header file defines manifest constants for the NT Sample transport + provider. It is included by st.h. + +Revision History: + +--*/ + +#ifndef _STCONST_ +#define _STCONST_ + + +// +// some convenient constants used for timing. All values are in clock ticks. +// + +#define MICROSECONDS 10 +#define MILLISECONDS 10000 // MICROSECONDS*1000 +#define SECONDS 10000000 // MILLISECONDS*1000 + + +// +// MAJOR PROTOCOL IDENTIFIERS THAT CHARACTERIZE THIS DRIVER. +// + +#define ST_DEVICE_NAME "\\Device\\St" // name of our driver. +#define ST_DEVICE_NAME_LENGTH 10 +#define MAX_SOURCE_ROUTE_LENGTH 32 // max. bytes of SR. info. +#define MAX_NETWORK_NAME_LENGTH 128 // # bytes in netname in TP_ADDRESS. +#define MAX_USER_PACKET_DATA 1500 // max. user bytes per DFM/DOL. + +#define ST_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// MAJOR CONFIGURATION PARAMETERS THAT WILL BE MOVED TO THE INIT-LARGE_INTEGER +// CONFIGURATION MANAGER. +// + +#define MAX_REQUESTS 30 +#define MAX_UI_FRAMES 25 +#define MAX_SEND_PACKETS 40 +#define MAX_RECEIVE_PACKETS 30 +#define MAX_RECEIVE_BUFFERS 15 +#define MAX_LINKS 10 +#define MAX_CONNECTIONS 10 +#define MAX_ADDRESSFILES 10 +#define MAX_ADDRESSES 10 + +#define MIN_UI_FRAMES 5 // + one per address + one per connection +#define MIN_SEND_PACKETS 20 // + one per link + one per connection +#define MIN_RECEIVE_PACKETS 10 // + one per link + one per address +#define MIN_RECEIVE_BUFFERS 5 // + one per address + +#define SEND_PACKET_RESERVED_LENGTH (sizeof (SEND_PACKET_TAG)) +#define RECEIVE_PACKET_RESERVED_LENGTH (sizeof (RECEIVE_PACKET_TAG)) + + +#define ETHERNET_HEADER_SIZE 14 // BUGBUG: used for current NDIS compliance +#define ETHERNET_PACKET_SIZE 1514 + + +// +// NETBIOS PROTOCOL CONSTANTS. +// + +// +// TDI defined timeouts +// + +#define TDI_TIMEOUT_SEND 60L // sends go 120 seconds +#define TDI_TIMEOUT_RECEIVE 0L // receives +#define TDI_TIMEOUT_CONNECT 60L +#define TDI_TIMEOUT_LISTEN 0L // listens default to never. +#define TDI_TIMEOUT_DISCONNECT 60L // should be 30 +#define TDI_TIMEOUT_NAME_REGISTRATION 60L + + + +// +// GENERAL CAPABILITIES STATEMENTS THAT CANNOT CHANGE. +// + +#define ST_MAX_TSDU_SIZE 65535 // maximum TSDU size supported by NetBIOS. +#define ST_MAX_DATAGRAM_SIZE 512 // maximum Datagram size supported by NetBIOS. +#define ST_MAX_CONNECTION_USER_DATA 0 // no user data supported on connect. +#define ST_SERVICE_FLAGS ( \ + TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY | \ + TDI_SERVICE_BROADCAST_SUPPORTED | \ + TDI_SERVICE_MULTICAST_SUPPORTED | \ + TDI_SERVICE_DELAYED_ACCEPTANCE ) + +#define ST_MIN_LOOKAHEAD_DATA 256 // minimum guaranteed lookahead data. +#define ST_MAX_LOOKAHEAD_DATA 256 // maximum guaranteed lookahead data. + +#define ST_MAX_LOOPBACK_LOOKAHEAD 192 // how much is copied over for loopback + +// +// Number of TDI resources that we report. +// + +#define ST_TDI_RESOURCES 7 + + +// +// More debugging stuff +// + +#define ST_REQUEST_SIGNATURE ((CSHORT)0x5501) +#define ST_CONNECTION_SIGNATURE ((CSHORT)0x5502) +#define ST_ADDRESSFILE_SIGNATURE ((CSHORT)0x5503) +#define ST_ADDRESS_SIGNATURE ((CSHORT)0x5504) +#define ST_DEVICE_CONTEXT_SIGNATURE ((CSHORT)0x5505) +#define ST_PACKET_SIGNATURE ((CSHORT)0x5506) + +#endif // _STCONST_ diff --git a/private/ntos/tdi/st/stdrvr.c b/private/ntos/tdi/st/stdrvr.c new file mode 100644 index 000000000..d04fcd2a1 --- /dev/null +++ b/private/ntos/tdi/st/stdrvr.c @@ -0,0 +1,1627 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stdrvr.c + +Abstract: + + This module contains code which defines the NT Sample + transport provider's device object. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" + + +// +// This is a list of all the device contexts that ST owns, +// used while unloading. +// + +LIST_ENTRY StDeviceList = {0,0}; // initialized for real at runtime. + + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +StUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigData + ); + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +StDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StOpenConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StCloseConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StTdiAccept( + IN PIRP Irp + ); + +NTSTATUS +StTdiConnect( + IN PIRP Irp + ); + +NTSTATUS +StTdiDisconnect( + IN PIRP Irp + ); + +NTSTATUS +StTdiDisassociateAddress ( + IN PIRP Irp + ); + +NTSTATUS +StTdiAssociateAddress( + IN PIRP Irp + ); + +NTSTATUS +StTdiListen( + IN PIRP Irp + ); + +NTSTATUS +StTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +NTSTATUS +StTdiReceive( + IN PIRP Irp + ); + +NTSTATUS +StTdiReceiveDatagram( + IN PIRP Irp + ); + +NTSTATUS +StTdiSend( + IN PIRP Irp + ); + +NTSTATUS +StTdiSendDatagram( + IN PIRP Irp + ); + +NTSTATUS +StTdiSetEventHandler( + IN PIRP Irp + ); + +NTSTATUS +StTdiSetInformation( + IN PIRP Irp + ); + +VOID +StDeallocateResources( + IN PDEVICE_CONTEXT DeviceContext + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the sample + transport driver. It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + ULONG i, j; + STRING nameString; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PTP_PACKET Packet; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + NTSTATUS status; + UINT SuccessfulOpens; + UINT MaxUserData; + + PCONFIG_DATA StConfig = NULL; + + + ASSERT (sizeof (SHORT) == 2); + + // + // This allocates the CONFIG_DATA structure and returns + // it in StConfig. + // + + status = StConfigureTransport(RegistryPath, &StConfig); + + if (!NT_SUCCESS (status)) { + PANIC (" Failed to initialize transport, St initialization failed.\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // make ourselves known to the NDIS wrapper. + // + + RtlInitString( &nameString, ST_DEVICE_NAME ); + + status = StRegisterProtocol (&nameString); + + if (!NT_SUCCESS (status)) { + + StFreeConfigurationInfo(StConfig); + PANIC ("StInitialize: RegisterProtocol failed!\n"); + + StWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = StDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = StDispatch; + + DriverObject->DriverUnload = StUnload; + + // + // Initialize the global list of devices. + // + + InitializeListHead (&StDeviceList); + + SuccessfulOpens = 0; + + for (j=0;jNumAdapters;j++ ) { + + + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + + status = StCreateDeviceContext (DriverObject, &StConfig->Names[StConfig->DevicesOffset+j], &DeviceContext); + + if (!NT_SUCCESS (status)) { + continue; + } + + // + // Initialize our counter that records memory usage. + // + + DeviceContext->MemoryUsage = 0; + DeviceContext->MemoryLimit = StConfig->MaxMemoryUsage; + + // + // Now fire up NDIS so this adapter talks + // + + status = StInitializeNdis (DeviceContext, + StConfig, + j); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + StWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + StConfig->Names[j].Buffer, + 0, + NULL); + + StDereferenceDeviceContext ("Initialize NDIS failed", DeviceContext); + continue; + + } + + + // + // Initialize our provider information structure; since it + // doesn't change, we just keep it around and copy it to + // whoever requests it. + // + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + &MaxUserData); + + DeviceContext->Information.Version = 0x0100; + DeviceContext->Information.MaxSendSize = 0x1fffe; // 128k - 2 + DeviceContext->Information.MaxConnectionUserData = 0; + DeviceContext->Information.MaxDatagramSize = MaxUserData - sizeof(ST_HEADER); + DeviceContext->Information.ServiceFlags = ST_SERVICE_FLAGS; + DeviceContext->Information.MinimumLookaheadData = 128; + DeviceContext->Information.MaximumLookaheadData = + DeviceContext->MaxReceivePacketSize - sizeof(ST_HEADER); + DeviceContext->Information.NumberOfResources = ST_TDI_RESOURCES; + KeQuerySystemTime (&DeviceContext->Information.StartTime); + + + // + // Allocate various structures we will need. + // + + + // + // The TP_PACKET structure has a CHAR[1] field at the end + // which we expand upon to include all the headers needed; + // the size of the MAC header depends on what the adapter + // told us about its max header size. + // + + DeviceContext->PacketHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof (ST_HEADER); + + DeviceContext->PacketLength = + FIELD_OFFSET(TP_PACKET, Header[0]) + + DeviceContext->PacketHeaderLength; + + + // + // The BUFFER_TAG structure has a CHAR[1] field at the end + // which we expand upong to include all the frame data. + // + + DeviceContext->ReceiveBufferLength = + DeviceContext->MaxReceivePacketSize + + FIELD_OFFSET(BUFFER_TAG, Buffer[0]); + + + for (i=0; iInitRequests; i++) { + + StAllocateRequest (DeviceContext, &Request); + + if (Request == NULL) { + PANIC ("StInitialize: insufficient memory to allocate requests.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + DeviceContext->RequestInitAllocated = StConfig->InitRequests; + DeviceContext->RequestMaxAllocated = StConfig->MaxRequests; + + + for (i=0; iInitConnections; i++) { + + StAllocateConnection (DeviceContext, &Connection); + + if (Connection == NULL) { + PANIC ("StInitialize: insufficient memory to allocate connections.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->ConnectionPool, &Connection->LinkList); + } + + DeviceContext->ConnectionInitAllocated = StConfig->InitConnections; + DeviceContext->ConnectionMaxAllocated = StConfig->MaxConnections; + + + for (i=0; iInitAddressFiles; i++) { + + StAllocateAddressFile (DeviceContext, &AddressFile); + + if (AddressFile == NULL) { + PANIC ("StInitialize: insufficient memory to allocate Address Files.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); + } + + DeviceContext->AddressFileInitAllocated = StConfig->InitAddressFiles; + DeviceContext->AddressFileMaxAllocated = StConfig->MaxAddressFiles; + + + for (i=0; iInitAddresses; i++) { + + StAllocateAddress (DeviceContext, &Address); + if (Address == NULL) { + PANIC ("StInitialize: insufficient memory to allocate addresses.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + DeviceContext->AddressInitAllocated = StConfig->InitAddresses; + DeviceContext->AddressMaxAllocated = StConfig->MaxAddresses; + + + for (i=0; iInitPackets; i++) { + + StAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("StInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->PacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); + } + + DeviceContext->PacketInitAllocated = StConfig->InitPackets; + + + for (i=0; iInitReceivePackets; i++) { + + StAllocateReceivePacket (DeviceContext, &NdisPacket); + + if (NdisPacket == NULL) { + PANIC ("StInitialize: insufficient memory to allocate packet MDLs.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)NdisPacket->ProtocolReserved; + PushEntryList (&DeviceContext->ReceivePacketPool, (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage); + + } + + DeviceContext->ReceivePacketInitAllocated = StConfig->InitReceivePackets; + + + for (i=0; iInitReceiveBuffers; i++) { + + StAllocateReceiveBuffer (DeviceContext, &BufferTag); + + if (BufferTag == NULL) { + PANIC ("StInitialize: Unable to allocate receive packet.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->ReceiveBufferPool, &BufferTag->Linkage); + + } + + DeviceContext->ReceiveBufferInitAllocated = StConfig->InitReceiveBuffers; + + + // + // Now link the device into the global list. + // + + InsertTailList (&StDeviceList, &DeviceContext->Linkage); + + DeviceContext->State = DEVICECONTEXT_STATE_OPEN; + + ++SuccessfulOpens; + + continue; + +cleanup: + + StWriteResourceErrorLog (DeviceContext, DeviceContext->MemoryUsage, 501); + + // + // Cleanup whatever device context we were initializing + // when we failed. + // + + StFreeResources (DeviceContext); + StCloseNdis (DeviceContext); + StDereferenceDeviceContext ("Load failed", DeviceContext); + + } + + StFreeConfigurationInfo(StConfig); + + return ((SuccessfulOpens > 0) ? STATUS_SUCCESS : STATUS_DEVICE_DOES_NOT_EXIST); + +} + +VOID +StUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has ST open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER (DriverObject); + + // + // Walk the list of device contexts. + // + + while (!IsListEmpty (&StDeviceList)) { + + p = RemoveHeadList (&StDeviceList); + DeviceContext = CONTAINING_RECORD (p, DEVICE_CONTEXT, Linkage); + + // + // Remove all the storage associated with the device. + // + + StFreeResources (DeviceContext); + + // + // Free the packet pools, etc. and close the + // adapter. + // + + StCloseNdis (DeviceContext); + + // + // And remove the creation reference from the device + // context. + // + + StDereferenceDeviceContext ("Unload", DeviceContext); + + } + + + // + // Finally, remove ourselves as an NDIS protocol. + // + + StDeregisterProtocol(); + + return; + +} + + +VOID +StFreeResources ( + IN PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine is called by ST to clean up the data structures associated + with a given DeviceContext. When this routine exits, the DeviceContext + should be deleted as it no longer has any assocaited resources. + +Arguments: + + DeviceContext - Pointer to the DeviceContext we wish to clean up. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PTP_ADDRESS address; + PTP_CONNECTION connection; + PTP_REQUEST request; + PTP_ADDRESS_FILE addressFile; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; + + + // + // Clean up packet pool. + // + + while ( DeviceContext->PacketPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + StDeallocateSendPacket (DeviceContext, packet); + } + + // + // Clean up address pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressPool) ) { + p = RemoveHeadList (&DeviceContext->AddressPool); + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + StDeallocateAddress (DeviceContext, address); + } + + // + // Clean up address file pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressFilePool) ) { + p = RemoveHeadList (&DeviceContext->AddressFilePool); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + StDeallocateAddressFile (DeviceContext, addressFile); + } + + // + // Clean up connection pool. + // + + while ( !IsListEmpty (&DeviceContext->ConnectionPool) ) { + p = RemoveHeadList (&DeviceContext->ConnectionPool); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + StDeallocateConnection (DeviceContext, connection); + } + + // + // Clean up request pool. + // + + while ( !IsListEmpty( &DeviceContext->RequestPool ) ) { + p = RemoveHeadList( &DeviceContext->RequestPool ); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage ); + + StDeallocateRequest (DeviceContext, request); + } + + // + // Clean up receive packet pool + // + + while ( DeviceContext->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&DeviceContext->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + StDeallocateReceivePacket (DeviceContext, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( DeviceContext->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + StDeallocateReceiveBuffer (DeviceContext, BufferTag); + } + + + return; + +} /* StFreeResources */ + + +NTSTATUS +StDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PDEVICE_CONTEXT DeviceContext; + + // + // Check to see if ST has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + Status = StDeviceControl (DeviceObject, Irp, IrpSp); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + // + // Return the immediate status code to the caller. + // + + return Status; +} /* StDispatch */ + + +NTSTATUS +StDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PFILE_FULL_EA_INFORMATION openType; + USHORT i; + BOOLEAN found; + PTP_ADDRESS_FILE AddressFile; + PTP_CONNECTION Connection; + + // + // Check to see if ST has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + + openType = + (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = StOpenAddress (DeviceObject, Irp, IrpSp); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = StOpenConnection (DeviceObject, Irp, IrpSp); + break; + } + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + IrpSp->FileObject->FsContext = (PVOID)(DeviceContext->ControlChannelIdentifier); + ++DeviceContext->ControlChannelIdentifier; + if (DeviceContext->ControlChannelIdentifier == 0) { + DeviceContext->ControlChannelIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + IrpSp->FileObject->FsContext2 = (PVOID)ST_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + + // + // This creates a reference to AddressFile->Address + // which is removed by StCloseAddress. + // + + Status = StVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = StCloseAddress (DeviceObject, Irp, IrpSp); + } + + break; + + case TDI_CONNECTION_FILE: + + // + // This is a connection + // + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = StVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + + Status = StCloseConnection (DeviceObject, Irp, IrpSp); + StDereferenceConnection ("Temporary Use",Connection); + + } + + break; + + case ST_FILE_TYPE_CONTROL: + + // + // this always succeeds + // + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + Status = StVerifyAddressObject(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + StStopAddressFile (AddressFile, AddressFile->Address); + StDereferenceAddress ("IRP_MJ_CLEANUP", AddressFile->Address); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = StVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + StStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + Status = STATUS_SUCCESS; + StDereferenceConnection ("Temporary Use",Connection); + } + + break; + + case ST_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + + // + // Return the immediate status code to the caller. + // + + return Status; +} /* StDispatchOpenClose */ + + +NTSTATUS +StDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + // StDispatchInternal expects to complete the IRP, + // so we change Status to PENDING so we don't. + // + + (VOID)StDispatchInternal (DeviceObject, Irp); + Status = STATUS_PENDING; + + } + } + + return Status; +} /* StDeviceControl */ + + +NTSTATUS +StDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PIO_STACK_LOCATION IrpSp; + + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->MinorFunction) { + + case TDI_ACCEPT: + Status = StTdiAccept (Irp); + break; + + case TDI_ACTION: + Status = STATUS_SUCCESS; + break; + + case TDI_ASSOCIATE_ADDRESS: + Status = StTdiAssociateAddress (Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + Status = StTdiDisassociateAddress (Irp); + break; + + case TDI_CONNECT: + Status = StTdiConnect (Irp); + break; + + case TDI_DISCONNECT: + Status = StTdiDisconnect (Irp); + break; + + case TDI_LISTEN: + Status = StTdiListen (Irp); + break; + + case TDI_QUERY_INFORMATION: + Status = StTdiQueryInformation (DeviceContext, Irp); + break; + + case TDI_RECEIVE: + Status = StTdiReceive (Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = StTdiReceiveDatagram (Irp); + break; + + case TDI_SEND: + Status = StTdiSend (Irp); + break; + + case TDI_SEND_DATAGRAM: + Status = StTdiSendDatagram (Irp); + break; + + case TDI_SET_EVENT_HANDLER: + + // + // Because this request will enable direct callouts from the + // transport provider at DISPATCH_LEVEL to a client-specified + // routine, this request is only valid in kernel mode, denying + // access to this request in user mode. + // + + Status = StTdiSetEventHandler (Irp); + break; + + case TDI_SET_INFORMATION: + Status = StTdiSetInformation (Irp); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* StDispatchInternal */ + + +VOID +StWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + DeviceContext - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + DeviceContext->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + + StringLoc += DeviceContext->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* StWriteResourceErrorLog */ + + +VOID +StWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceContext - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[3] = L"St"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceContext->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)DeviceContext->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceContext->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* StWriteGeneralErrorLog */ + + +VOID +StWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceContext - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + static WCHAR OidBuffer[9] = L"00000000"; + UINT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + DeviceContext->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* StWriteOidErrorLog */ diff --git a/private/ntos/tdi/st/sthdrs.h b/private/ntos/tdi/st/sthdrs.h new file mode 100644 index 000000000..737832fc1 --- /dev/null +++ b/private/ntos/tdi/st/sthdrs.h @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sthdrs.h + +Abstract: + + This module defines private structure definitions describing the layout + of the NT Sample Transport frames. + +Revision History: + +--*/ + +#ifndef _STHDRS_ +#define _STHDRS_ + +// +// Pack these headers, as they are sent fully packed on the network. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Off(Align_members) +#else +#pragma pack(1) +#endif // def __STDC__ + +#endif // def PACKING + +#define ST_SIGNATURE 0x37 + +#define ST_CMD_CONNECT 'C' +#define ST_CMD_DISCONNECT 'D' +#define ST_CMD_INFORMATION 'I' +#define ST_CMD_DATAGRAM 'G' + +#define ST_FLAGS_LAST 0x0001 // for information frames +#define ST_FLAGS_BROADCAST 0x0002 // for datagrams + +typedef struct _ST_HEADER { + UCHAR Signature; // set to ST_SIGNATURE + UCHAR Command; // command byte + UCHAR Flags; // packet flags + UCHAR Reserved; // unused + UCHAR Destination[16]; // destination Netbios address + UCHAR Source[16]; // source Netbios address +} ST_HEADER, *PST_HEADER; + + +// +// Resume previous structure packing method. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Pop(Align_members) +#else +#pragma pack() +#endif // def __STDC__ + +#endif // def PACKING + +#endif // def _STHDRS_ diff --git a/private/ntos/tdi/st/stmac.c b/private/ntos/tdi/st/stmac.c new file mode 100644 index 000000000..157c96a17 --- /dev/null +++ b/private/ntos/tdi/st/stmac.c @@ -0,0 +1,356 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbfmac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the ST transport. + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + +--*/ + +#include "st.h" + +UCHAR SingleRouteSourceRouting[] = { 0xc2, 0x70 }; +UCHAR GeneralRouteSourceRouting[] = { 0x82, 0x70 }; +ULONG DefaultSourceRoutingLength = 2; + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ); + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,MacInitializeMacInfo) +#pragma alloc_text(INIT,MacSetMulticastAddress) +#endif + + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->DestinationOffset = 0; + MacInfo->SourceOffset = 6; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + case NdisMedium802_5: + MacInfo->DestinationOffset = 2; + MacInfo->SourceOffset = 8; + MacInfo->SourceRouting = TRUE; + MacInfo->AddressLength = 6; + MacInfo->MaxHeaderLength = 32; + MacInfo->MediumType = NdisMedium802_5; + break; + case NdisMediumFddi: + MacInfo->DestinationOffset = 1; + MacInfo->SourceOffset = 7; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->MaxHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + break; + default: + ASSERT(FALSE); + } +} + +VOID +MacConstructHeader ( + IN PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ) + +/*++ + +Routine Description: + + This routine is called to construct the Mac header for the particular + network type we're talking to. + +Arguments: + + MacInfo - Describes the mac we wish to build a header for. + + Buffer - Where to build the header. + + DestinationAddress - the address this packet is to be sent to. + + SourceAddress - Our address. Passing it in as a parameter allows us to play + games with source if we need to. + + PacketLength - The length of this packet. Note that this does not + includes the Mac header. + + SourceRouting - Optional source routing information. + + SourceRoutingLength - The length of SourceRouting. + + HeaderLength - Returns the length of the constructed header. + +Return Value: + + None. + +--*/ +{ + // + // Note network order of bytes. + // + + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + *(ULONG UNALIGNED *)&Buffer[6] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[10] = SourceAddress[4]; + Buffer[11] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[0] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[4] = DestinationAddress[4]; + Buffer[5] = DestinationAddress[5]; + + Buffer[12] = (UCHAR)(PacketLength >> 8); + Buffer[13] = (UCHAR)PacketLength; + + *HeaderLength = 14; + + break; + + case NdisMedium802_5: + + Buffer[0] = TR_HEADER_BYTE_0; + Buffer[1] = TR_HEADER_BYTE_1; + + ASSERT (TR_ADDRESS_LENGTH == 6); + + *(ULONG UNALIGNED *)&Buffer[8] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[12] = SourceAddress[4]; + Buffer[13] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[2] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[6] = DestinationAddress[4]; + Buffer[7] = DestinationAddress[5]; + + *HeaderLength = 14; + if (SourceRouting != NULL) { + RtlCopyMemory (&Buffer[14], SourceRouting, SourceRoutingLength); + Buffer[8] |= 0x80; // add SR bit in source address + *HeaderLength = 14 + SourceRoutingLength; + } + + break; + + case NdisMediumFddi: + + Buffer[0] = FDDI_HEADER_BYTE; + + *(ULONG UNALIGNED *)&Buffer[7] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[11] = SourceAddress[4]; + Buffer[12] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[1] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[5] = DestinationAddress[4]; + Buffer[6] = DestinationAddress[5]; + + *HeaderLength = 13; + + break; + + default: + PANIC ("MacConstructHeader: PANIC! called with unsupported Mac type.\n"); + } +} + + +VOID +MacReturnMaxDataSize( + IN PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all headers + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & 0x70) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + + if (DeviceMaxFrameSize < 548) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 516; + } + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + } +} + + + +VOID +MacSetMulticastAddress ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine sets the multicast address into a buffer provided + by the user. + +Arguments: + + Type the Mac Medium type. + + Buffer the buffer to put the multicast address in. + + +Return Value: + + none. + +--*/ +{ + switch (Type) { + case NdisMedium802_3: + case NdisMediumFddi: + Buffer[0] = 0x03; + Buffer[1] = 0x07; + Buffer[2] = 0x03; + Buffer[3] = 0x07; + Buffer[4] = 0x03; + Buffer[5] = 0x07; + break; + + case NdisMedium802_5: + Buffer[0] = 0xc0; + Buffer[3] = 0x20; + break; + + default: + PANIC ("MacSetMulticastAddress: PANIC! called with unsupported Mac type.\n"); + } +} diff --git a/private/ntos/tdi/st/stmac.h b/private/ntos/tdi/st/stmac.h new file mode 100644 index 000000000..cea2feff0 --- /dev/null +++ b/private/ntos/tdi/st/stmac.h @@ -0,0 +1,635 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stmac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Revision History: + +--*/ + +#ifndef _STMAC_ +#define _STMAC_ + +// +// MAC-specific definitions, some of which get used below +// + +#define MAX_MAC_HEADER_LENGTH 32 +#define MAX_SOURCE_ROUTING 18 +#define MAX_DEFAULT_SR 2 + +#define ETHERNET_ADDRESS_LENGTH 6 +#define ETHERNET_PACKET_LENGTH 1514 // max size of an ethernet packet +#define ETHERNET_HEADER_LENGTH 14 // size of the ethernet MAC header +#define ETHERNET_DATA_LENGTH_OFFSET 12 +#define ETHERNET_DESTINATION_OFFSET 0 +#define ETHERNET_SOURCE_OFFSET 6 + +#define TR_ADDRESS_LENGTH 6 +#define TR_ADDRESS_OFFSET 2 +#define TR_SPECIFIC_OFFSET 0 +#define TR_PACKET_LENGTH 1514 // max size of a TR packet //BUGBUG +#define TR_HEADER_LENGTH 36 // size of the MAC header w/o source routing +#define TR_DATA_LENGTH_OFFSET 0 +#define TR_DESTINATION_OFFSET 2 +#define TR_SOURCE_OFFSET 8 +#define TR_ROUTING_OFFSET 14 // starts at the 14th byte +#define TR_GR_BCAST_LENGTH 2 +#define TR_GR_BROADCAST 0xC270 // what a general route b'cast looks like +#define TR_ROUTING_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit + +#define TR_PREAMBLE_AC 0x10 // how would these be specified? +#define TR_PREAMBLE_FC 0x40 + +#define TR_HEADER_BYTE_0 0x10 +#define TR_HEADER_BYTE_1 0x40 + +#define FDDI_ADDRESS_LENGTH 6 +#define FDDI_HEADER_BYTE 0x57 + + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the stmac routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _ST_NDIS_IDENTIFICATION { + NDIS_MEDIUM MediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + BOOLEAN CopyLookahead; + ULONG DestinationOffset; + ULONG SourceOffset; + ULONG AddressLength; + ULONG MaxHeaderLength; +} ST_NDIS_IDENTIFICATION, *PST_NDIS_IDENTIFICATION; + + + +VOID +MacConstructHeader( + IN PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ); + +VOID +MacReturnMaxDataSize( + IN PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ); + + +extern UCHAR SingleRouteSourceRouting[]; +extern UCHAR GeneralRouteSourceRouting[]; +extern ULONG DefaultSourceRoutingLength; + + +//++ +// +// VOID +// MacReturnDestinationAddress( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * DestinationAddress +// ); +// +// Routine Description: +// +// Returns the a pointer to the destination address in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// DestinationAddress - Returns the start of the destination address. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnDestinationAddress(_MacInfo, _Packet, _DestinationAddress) \ + *(_DestinationAddress) = ((PUCHAR)(_Packet)) + (_MacInfo)->DestinationOffset + + +//++ +// +// VOID +// MacReturnSourceAddress( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PHARDWARE_ADDRESS SourceAddressBuffer, +// OUT PHARDWARE_ADDRESS * SourceAddress, +// ); +// +// Routine Description: +// +// Copies the source address in the packet into SourceAddress. +// NOTE THAT IT MAY COPY THE DATA, UNLIKE ReturnDestinationAddress +// AND ReturnSourceRouting. Optionally, indicates whether the +// destination address is a multicast address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceAddressBuffer - A buffer to hold the source address, +// if needed. +// +// SourceAddress - Returns a pointer to the source address. +// +// Multicast - Optional pointer to a BOOLEAN to receive indication +// of whether the destination was a multicast address. +// +// Return Value: +// +// None. +// +//-- + +// +// NOTE: The default case below handles Ethernet and FDDI. +// + +#define MacReturnSourceAddress(_MacInfo, _Packet, _SourceAddressBuffer, _SourceAddress) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + PUCHAR SrcBuffer = (PUCHAR)(_SourceAddressBuffer); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(PULONG)SrcBuffer = *(ULONG UNALIGNED *)(&TmpPacket[8]) & ~0x80;\ + SrcBuffer[4] = TmpPacket[12]; \ + SrcBuffer[5] = TmpPacket[13]; \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)SrcBuffer; \ + break; \ + default: \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + \ + (_MacInfo)->SourceOffset); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnSourceRouting( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * SourceRouting +// OUT PUINT SourceRoutingLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the source routing info in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceRouting - Returns the start of the source routing information, +// or NULL if none is present. +// +// SourceRoutingLength - Returns the length of the source routing +// information. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSourceRouting(_MacInfo, _Packet, _SourceRouting, _SourceRoutingLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + if ((_MacInfo)->SourceRouting) { \ + if (TmpPacket[8] & 0x80) { \ + *(_SourceRouting) = TmpPacket + 14; \ + *(_SourceRoutingLength) = TmpPacket[14] & 0x1f; \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ +} + +//++ +// +// VOID +// MacReturnPacketLength( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Header, +// IN UINT PacketLength, +// OUT PUINT DataLength +// ); +// +// Routine Description: +// +// Returns the length of data in the packet given the header. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// PacketLength - The length of the data (not including header). +// +// DataLength - Returns the length of the data. Unchanged if the +// packet is not recognized. Should be initialized by caller to 0. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnPacketLength(_MacInfo, _Header, _HeaderLength, _PacketLength, _DataLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + UINT TmpLength; \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + if ((_HeaderLength) >= 14) { \ + TmpLength = (TmpPacket[12] << 8) | TmpPacket[13]; \ + if (TmpLength <= 0x600) { \ + if (TmpLength <= (_PacketLength)) { \ + *(_DataLength) = TmpLength; \ + } \ + } \ + } \ + break; \ + case NdisMedium802_5: \ + if (((_HeaderLength) >= 14) && \ + (!(TmpPacket[8] & 0x80) || \ + ((_HeaderLength) >= \ + (UINT)(14 + (TmpPacket[14] & 0x1f))))) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + case NdisMediumFddi: \ + if ((_HeaderLength) >= 13) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnHeaderLength( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID HeaderLength, +// ); +// +// Routine Description: +// +// Returns the length of the MAC header in a packet (this +// is used for loopback indications to separate header +// and data). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// HeaderLength - Returns the length of the header. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnHeaderLength(_MacInfo, _Header, _HeaderLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + *(_HeaderLength) = 14; \ + break; \ + case NdisMedium802_5: \ + if (TmpPacket[8] & 0x80) { \ + *(_HeaderLength) = (TmpPacket[14] & 0x1f) + 14; \ + } else { \ + *(_HeaderLength) = 14; \ + } \ + break; \ + case NdisMediumFddi: \ + *(_HeaderLength) = 13; \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnSingleRouteSR( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * SingleRouteSR, +// OUT PUINT SingleRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard single route broadcast +// source routing information for the media type. This is used +// for ADD_NAME_QUERY, DATAGRAM, NAME_IN_CONFLICT, NAME_QUERY, +// and STATUS_QUERY frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// SingleRouteSR - Returns a pointer to the data. +// +// SingleRouteSRLength - The length of SingleRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSingleRouteSR(_MacInfo, _SingleRouteSR, _SingleRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_SingleRouteSR) = SingleRouteSourceRouting; \ + *(_SingleRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_SingleRouteSR) = NULL; \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnGeneralRouteSR( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * GeneralRouteSR, +// OUT PUINT GeneralRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard general route broadcast +// source routing information for the media type. This is used +// for NAME_RECOGNIZED frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// GeneralRouteSR - Returns a pointer to the data. +// +// GeneralRouteSRLength - The length of GeneralRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnGeneralRouteSR(_MacInfo, _GeneralRouteSR, _GeneralRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_GeneralRouteSR) = GeneralRouteSourceRouting; \ + *(_GeneralRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_GeneralRouteSR) = NULL; \ + break; \ + } \ +} + +#if 0 + +//++ +// +// VOID +// MacCreateGeneralRouteReplySR( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a general-route source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateGeneralRouteReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + TmpSR[0] = (TmpSR[0] & 0x1f) | 0x80; \ + TmpSR[1] = (TmpSR[1] ^ 0x80) | 0x70; \ + *(_NewSR) = (_ExistingSR); \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} +#endif + + +//++ +// +// VOID +// MacCreateNonBroadcastReplySR( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a non-broadcast source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateNonBroadcastReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + if ((_ExistingSRLength) == 2) { \ + *(_NewSR) = NULL; \ + } else { \ + TmpSR[0] = (TmpSR[0] & 0x1f); \ + TmpSR[1] = (TmpSR[1] ^ 0x80) | 0x70; \ + *(_NewSR) = (_ExistingSR); \ + } \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} + + +//++ +// +// VOID +// MacModifyHeader( +// IN PST_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR Header, +// IN UINT PacketLength +// ); +// +// Routine Description: +// +// Modifies a pre-built packet header to include the +// packet length. Used for connection-oriented traffic +// where the header is pre-built. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The header to modify. +// +// PacketLength - Packet length (not including the header). +// Currently this is the only field that cannot be pre-built. +// +// Return Value: +// +// None. +// +//-- + +#define MacModifyHeader(_MacInfo, _Header, _PacketLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + (_Header)[12] = (UCHAR)((_PacketLength) >> 8); \ + (_Header)[13] = (UCHAR)((_PacketLength) & 0xff); \ + break; \ + } \ +} + + +VOID +MacSetMulticastAddress( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ); + + + +// VOID +// StSetNdisPacketLength ( +// IN NDIS_PACKET Packet, +// IN ULONG Length +// ); +// +// NB: This is not a general purpose macro; it assumes that we are setting the +// length of an NDIS packet with only one NDIS_BUFFER chained. We do +// this to save time during the sending of short control packets. +// + +#define StSetNdisPacketLength(_packet,_length) { \ + PNDIS_BUFFER NdisBuffer; \ + NdisQueryPacket((_packet), NULL, NULL, &NdisBuffer, NULL); \ + NdisAdjustBufferLength(NdisBuffer, (_length)); \ + NdisRecalculatePacketCounts(_packet); \ +} + +#endif // ifdef _STMAC_ + diff --git a/private/ntos/tdi/st/stndis.c b/private/ntos/tdi/st/stndis.c new file mode 100644 index 000000000..43279f815 --- /dev/null +++ b/private/ntos/tdi/st/stndis.c @@ -0,0 +1,986 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stndis.c + +Abstract: + + This module contains code which implements the routines used to interface + ST and NDIS. All callback routines (except for Transfer Data, + Send Complete, and ReceiveIndication) are here, as well as those routines + called to initialize NDIS. + +Environment: + + Kernel mode + +--*/ + +#include "st.h" + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE StNdisProtocolHandle = (NDIS_HANDLE)NULL; + +NDIS_STATUS +StSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,StRegisterProtocol) +#pragma alloc_text(INIT,StSubmitNdisRequest) +#pragma alloc_text(INIT,StInitializeNdis) +#endif + + +NTSTATUS +StRegisterProtocol ( + IN STRING *NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + + // + // Set up the characteristics of this protocol + // + + ProtChars.MajorNdisVersion = 3; + ProtChars.MinorNdisVersion = 0; + + ProtChars.Name.Length = NameString->Length; + ProtChars.Name.Buffer = (PVOID)NameString->Buffer; + + ProtChars.OpenAdapterCompleteHandler = StOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = StCloseAdapterComplete; + ProtChars.ResetCompleteHandler = StResetComplete; + ProtChars.RequestCompleteHandler = StRequestComplete; + + ProtChars.SendCompleteHandler = StSendCompletionHandler; + ProtChars.TransferDataCompleteHandler = StTransferDataComplete; + + ProtChars.ReceiveHandler = StReceiveIndication; + ProtChars.ReceiveCompleteHandler = StReceiveComplete; + ProtChars.StatusHandler = StStatusIndication; + ProtChars.StatusCompleteHandler = StStatusComplete; + + NdisRegisterProtocol ( + &ndisStatus, + &StNdisProtocolHandle, + &ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; +} + + +VOID +StDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (StNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + StNdisProtocolHandle); + StNdisProtocolHandle = (NDIS_HANDLE)NULL; + } +} + + +NDIS_STATUS +StSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + DeviceContext - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + DeviceContext->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + StWriteOidErrorLog( + DeviceContext, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + + return NdisStatus; +} + + +NTSTATUS +StInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA StConfig, + IN UINT ConfigInfoNameIndex + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + ULONG SendPacketReservedLength; + ULONG ReceivePacketReservedLen; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM StSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi }; + UINT SelectedMedium; + NDIS_REQUEST StRequest; + UCHAR StDataBuffer[6]; + NDIS_OID StOid; + ULONG MinimumLookahead = 128 + sizeof(ST_HEADER); + ULONG ProtocolOptions, MacOptions; + PNDIS_STRING AdapterString; + + // + // Initialize this adapter for ST use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + DeviceContext->NdisBindingHandle = NULL; + AdapterString = (PNDIS_STRING)&StConfig->Names[ConfigInfoNameIndex]; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &DeviceContext->NdisBindingHandle, + &SelectedMedium, + StSupportedMedia, + sizeof (StSupportedMedia) / sizeof(NDIS_MEDIUM), + StNdisProtocolHandle, + (NDIS_HANDLE)DeviceContext, + AdapterString, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + StWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 0, + NULL); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + StSupportedMedia[SelectedMedium], + &DeviceContext->MacInfo); + + + // + // Set the multicast/functional addresses first so we avoid windows where we + // receive only part of the addresses. + // + + MacSetMulticastAddress ( + DeviceContext->MacInfo.MediumType, + DeviceContext->MulticastAddress.Address); + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(StDataBuffer, DeviceContext->MulticastAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_802_3_MULTICAST_LIST; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + case NdisMedium802_5: + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + // + // Fill in the data for our functional address. + // + + RtlCopyMemory(StDataBuffer, ((PUCHAR)(DeviceContext->MulticastAddress.Address)) + 2, 4); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = 4; + + break; + + case NdisMediumFddi: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(StDataBuffer, DeviceContext->MulticastAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_FDDI_LONG_MULTICAST_LIST; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + } + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + + StOid = OID_802_3_CURRENT_ADDRESS; + break; + + case NdisMedium802_5: + + StOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + StOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = StOid; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = DeviceContext->LocalAddress.Address; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Set up the reserved Netbios address. + // + + RtlZeroMemory(DeviceContext->ReservedNetBIOSAddress, 10); + RtlCopyMemory(&DeviceContext->ReservedNetBIOSAddress[10], DeviceContext->LocalAddress.Address, 6); + + + // + // Now query the maximum packet sizes. + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxReceivePacketSize); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxSendPacketSize); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now set the minimum lookahead size. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MediumSpeed); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the MAC's optional characteristics. + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Since the sample transport does not try to optimize for the + // cases where transfer data is always synchronous or indications + // are not reentered, we ignore those bits in MacOptions. + // + + DeviceContext->MacInfo.CopyLookahead = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0); + + + // + // Now set our options if needed. We can only support + // partial indications if running over 802.3 where the + // real packet length can be obtained from the header. + // + + if (DeviceContext->MacInfo.MediumType == NdisMedium802_3) { + + ProtocolOptions = NDIS_PROT_OPTION_ESTIMATED_LENGTH; + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_PROTOCOL_OPTIONS; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &ProtocolOptions; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } + + + // + // Calculate the NDIS-related stuff. + // + + SendPacketReservedLength = sizeof (SEND_PACKET_TAG); + ReceivePacketReservedLen = sizeof (RECEIVE_PACKET_TAG); + + // + // The send packet pool is used for UI frames and regular packets. + // + + SendPacketPoolSize = StConfig->SendPacketPoolSize; + + // + // The receive packet pool is used in transfer data. + // + + ReceivePacketPoolSize = StConfig->ReceivePacketPoolSize; + + + NdisAllocatePacketPool ( + &NdisStatus, + &DeviceContext->SendPacketPoolHandle, + SendPacketPoolSize, + SendPacketReservedLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->SendPacketPoolHandle = NULL; + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MemoryUsage += + (SendPacketPoolSize * + (sizeof(NDIS_PACKET) + SendPacketReservedLength)); + + + NdisAllocatePacketPool( + &NdisStatus, + &DeviceContext->ReceivePacketPoolHandle, + ReceivePacketPoolSize, + ReceivePacketReservedLen); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->ReceivePacketPoolHandle = NULL; + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MemoryUsage += + (ReceivePacketPoolSize * + (sizeof(NDIS_PACKET) + ReceivePacketReservedLen)); + + + // + // Allocate the buffer pool; as an estimate, allocate + // one per send or receive packet. + // + + NdisAllocateBufferPool ( + &NdisStatus, + &DeviceContext->NdisBufferPoolHandle, + SendPacketPoolSize + ReceivePacketPoolSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->NdisBufferPoolHandle = NULL; + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + // + // Fill in the OVB for packet filter. + // + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumFddi: + + RtlStoreUlong((PULONG)StDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST)); + break; + + case NdisMedium802_5: + + RtlStoreUlong((PULONG)StDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_FUNCTIONAL)); + break; + + default: + + ASSERT (FALSE); + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* StInitializeNdis */ + + +VOID +StCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in StInitializeNdis. + It is written so that it can be called from within StInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (DeviceContext->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + DeviceContext->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + + if (DeviceContext->SendPacketPoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->SendPacketPoolHandle); + } + + if (DeviceContext->ReceivePacketPoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->ReceivePacketPoolHandle); + } + + if (DeviceContext->NdisBufferPoolHandle != NULL) { + NdisFreeBufferPool (DeviceContext->NdisBufferPoolHandle); + } + +} /* StCloseNdis */ + + +VOID +StOpenAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StCloseAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StResetComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + + return; +} + +VOID +StRequestComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = (PDEVICE_CONTEXT)NdisBindingContext; + + switch (NdisStatus) { + + // + // Handle various status codes here. + // + + default: + break; + + } +} + + +VOID +StStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} diff --git a/private/ntos/tdi/st/stprocs.h b/private/ntos/tdi/st/stprocs.h new file mode 100644 index 000000000..480927f48 --- /dev/null +++ b/private/ntos/tdi/st/stprocs.h @@ -0,0 +1,923 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stprocs.h + +Abstract: + + This header file defines private functions for the NT Sample transport + provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Revision History: + +--*/ + +#ifndef _STPROCS_ +#define _STPROCS_ + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// IF_STDBG( +// IN PSZ Message +// ); +// + +#if DBG +#define IF_STDBG(flags) \ + if (StDebug & (flags)) +#else +#define IF_STDBG(flags) \ + if (0) +#endif + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + DbgPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow DbgPrints that disappear when +// DBG is 0. +// + +#if DBG +#define StPrint0(fmt) DbgPrint(fmt) +#define StPrint1(fmt,v0) DbgPrint(fmt,v0) +#define StPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define StPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define StPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define StPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define StPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define StPrint0(fmt) +#define StPrint1(fmt,v0) +#define StPrint2(fmt,v0,v1) +#define StPrint3(fmt,v0,v1,v2) +#define StPrint4(fmt,v0,v1,v2,v3) +#define StPrint5(fmt,v0,v1,v2,v3,v4) +#define StPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + +// +// The REFCOUNTS message take up a lot of room, so make +// removing them easy. +// + +#if 1 +#define IF_REFDBG IF_STDBG (ST_DEBUG_REFCOUNTS) +#else +#define IF_REFDBG if (0) +#endif + +#define StReferenceConnection(Reason, Connection)\ + StRefConnection (Connection) + +#define StDereferenceConnection(Reason, Connection)\ + StDerefConnection (Connection) + +#define StDereferenceConnectionSpecial(Reason, Connection)\ + StDerefConnectionSpecial (Connection) + +#define StReferenceRequest(Reason, Request)\ + (VOID)InterlockedIncrement( \ + &(Request)->ReferenceCount) + +#define StDereferenceRequest(Reason, Request)\ + StDerefRequest (Request) + +#define StReferenceSendIrp(Reason, IrpSp)\ + (VOID)InterlockedIncrement( \ + &IRP_REFCOUNT(IrpSp)) + +#define StDereferenceSendIrp(Reason, IrpSp)\ + StDerefSendIrp (IrpSp) + +#define StReferenceAddress(Reason, Address)\ + (VOID)InterlockedIncrement( \ + &(Address)->ReferenceCount) + +#define StDereferenceAddress(Reason, Address)\ + StDerefAddress (Address) + +#define StReferenceDeviceContext(Reason, DeviceContext)\ + StRefDeviceContext (DeviceContext) + +#define StDereferenceDeviceContext(Reason, DeviceContext)\ + StDerefDeviceContext (DeviceContext) + +#define StReferencePacket(Packet) \ + (VOID)InterlockedIncrement( \ + &(Packet)->ReferenceCount) + +// +// These macros are used to create and destroy packets, due +// to the allocation or deallocation of structure which +// need them. +// + + +#define StAddSendPacket(DeviceContext) { \ + PTP_PACKET _SendPacket; \ + StAllocateSendPacket ((DeviceContext), &_SendPacket); \ + if (_SendPacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->PacketPool, \ + (PSINGLE_LIST_ENTRY)&_SendPacket->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveSendPacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->PacketAllocated > DeviceContext->PacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->PacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateSendPacket((DeviceContext), \ + (PTP_PACKET)CONTAINING_RECORD(s, TP_PACKET, Linkage)); \ + } \ + } \ +} + + +#define StAddReceivePacket(DeviceContext) { \ + PNDIS_PACKET _ReceivePacket; \ + StAllocateReceivePacket ((DeviceContext), &_ReceivePacket); \ + if (_ReceivePacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + (PSINGLE_LIST_ENTRY)&((PRECEIVE_PACKET_TAG)_ReceivePacket->ProtocolReserved)->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveReceivePacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceivePacketAllocated > DeviceContext->ReceivePacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateReceivePacket((DeviceContext), \ + (PNDIS_PACKET)CONTAINING_RECORD(s, NDIS_PACKET, ProtocolReserved[0])); \ + } \ + } \ +} + + +#define StAddReceiveBuffer(DeviceContext) { \ + PBUFFER_TAG _ReceiveBuffer; \ + StAllocateReceiveBuffer ((DeviceContext), &_ReceiveBuffer); \ + if (_ReceiveBuffer != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &_ReceiveBuffer->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveReceiveBuffer(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceiveBufferAllocated > DeviceContext->ReceiveBufferInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateReceiveBuffer(DeviceContext, \ + (PBUFFER_TAG)CONTAINING_RECORD(s, BUFFER_TAG, Linkage)); \ + } \ + } \ +} + + +// +// These routines are used to maintain counters. +// + +#define INCREMENT_COUNTER(_DeviceContext,_Field) \ + ++(_DeviceContext)->_Field + +#define DECREMENT_COUNTER(_DeviceContext,_Field) \ + --(_DeviceContext)->_Field + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger), (ULONG)(_Ulong)) + + + +// +// Routines in PACKET.C (TP_PACKET object manager). +// + +VOID +StAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ); + +VOID +StAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ); + +VOID +StAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ); + +VOID +StDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ); + +VOID +StDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ); + +VOID +StDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ); + +NTSTATUS +StCreatePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *Packet + ); + +VOID +StDestroyPacket( + IN PTP_PACKET Packet + ); + +VOID +StWaitPacket( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); + +// +// Routines in RCVENG.C (Receive engine). +// + +VOID +AwakenReceive( + IN PTP_CONNECTION Connection + ); + +VOID +ActivateReceive( + IN PTP_CONNECTION Connection + ); + +VOID +CompleteReceive ( + IN PTP_CONNECTION Connection, + IN BOOLEAN EndOfRecord, + KIRQL ConnectionIrql, + KIRQL CancelIrql + ); + +VOID +StCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routines in SENDENG.C (Send engine). +// + +VOID +InitializeSend( + PTP_CONNECTION Connection + ); + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate, + IN KIRQL ConnectionIrql, + IN KIRQL CancelIrql + ); + +VOID +PacketizeConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection + ); + +VOID +CompleteSend( + IN PTP_CONNECTION Connection + ); + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ); + +VOID +StCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +StNdisSend( + IN PTP_PACKET Packet + ); + +VOID +StSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ); + +// +// Routines in DEVCTX.C (TP_DEVCTX object manager). +// + +VOID +StRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +StDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +StCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ); + +VOID +StDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in ADDRESS.C (TP_ADDRESS object manager). +// + +VOID +StRefAddress( + IN PTP_ADDRESS Address + ); + +VOID +StDerefAddress( + IN PTP_ADDRESS Address + ); + +VOID +StAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ); + +VOID +StDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ); + +NTSTATUS +StCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ); + +VOID +StReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +StDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +StStopAddress( + IN PTP_ADDRESS Address + ); + +VOID +StRegisterAddress( + IN PTP_ADDRESS Address + ); + +BOOLEAN +StMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN PUCHAR NetBIOSName + ); + +VOID +StAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ); + +VOID +StDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ); + +NTSTATUS +StCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ); + +PTP_ADDRESS +StLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName + ); + +PTP_CONNECTION +StLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ); + +NTSTATUS +StStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ); + +NTSTATUS +StVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ); + +NTSTATUS +StSendDatagramsOnAddress( + PTP_ADDRESS Address + ); + +// +// +// Routines in CONNOBJ.C (TP_CONNECTION object manager). +// + +VOID +StRefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StDerefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StStopConnection( + IN PTP_CONNECTION TransportConnection, + IN NTSTATUS Status + ); + +VOID +StCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PTP_CONNECTION +StLookupListeningConnection( + IN PTP_ADDRESS Address + ); + +VOID +StAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +VOID +StDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +StCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +PTP_CONNECTION +StLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PTP_CONNECTION +StFindConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR LocalName, + IN PUCHAR RemoteName + ); + +NTSTATUS +StVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ); + +// +// Routines in REQUEST.C (TP_REQUEST object manager). +// + + +VOID +TdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +StRefRequest( + IN PTP_REQUEST Request + ); + +VOID +StDerefRequest( + IN PTP_REQUEST Request + ); + +VOID +StCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +StRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +StDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +StCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +StAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ); + +VOID +StDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ); + +NTSTATUS +StCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ); + +// +// Routines in DLC.C (entrypoints from NDIS interface). +// + +NDIS_STATUS +StReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +NDIS_STATUS +StGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PST_HEADER StHeader, + IN UINT StSize + ); + +VOID +StReceiveComplete ( + IN NDIS_HANDLE BindingContext + ); + +VOID +StTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +// +// Routines in UFRAMES.C, the UI-frame ST frame processor. +// + +NTSTATUS +StIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Header, + IN ULONG Length + ); + +NTSTATUS +StProcessConnectionless( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PST_HEADER StHeader, + IN ULONG StLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ); + +// +// Routines in IFRAMES.C, the I-frame ST frame processor. +// + +NTSTATUS +StProcessIIndicate( + IN PTP_CONNECTION Connection, + IN PST_HEADER StHeader, + IN UINT StIndicatedLength, + IN UINT StTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last + ); + +// +// Routines in RCV.C (data copying routines for receives). +// + +NTSTATUS +StCopyMdlToBuffer( + IN PMDL SourceMdlChain, + IN ULONG SourceOffset, + IN PVOID DestinationBuffer, + IN ULONG DestinationOffset, + IN ULONG DestinationBufferSize, + IN PULONG BytesCopied + ); + +// +// Routines in FRAMESND.C, the UI-frame (non-link) shipper. +// + +NTSTATUS +StSendConnect( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +StSendDisconnect( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +StSendAddressFrame( + IN PTP_ADDRESS Address + ); + +VOID +StSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + + +// +// Routines in stdrvr.c +// + +NTSTATUS +StDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +StDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +StDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routine in stndis.c +// + +VOID +StOpenAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +StCloseAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +StResetComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +StRequestComplete( + IN NDIS_HANDLE NdisBindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS Status + ); + +VOID +StStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +StStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ); + +#if DBG +PUCHAR +StGetNdisStatus ( + IN NDIS_STATUS NdisStatus + ); +#endif + +VOID +StWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +StWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +StWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +VOID +StFreeResources( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// routines in stcnfg.c +// + +NTSTATUS +StConfigureProvider( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +// +// Routines in stndis.c +// + +NTSTATUS +StRegisterProtocol ( + IN STRING *NameString + ); + +VOID +StDeregisterProtocol ( + VOID + ); + + +NTSTATUS +StInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA ConfigInfo, + IN UINT ConfigInfoNameIndex + ); + +VOID +StCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ); + + +#endif // def _STPROCS_ diff --git a/private/ntos/tdi/st/sttypes.h b/private/ntos/tdi/st/sttypes.h new file mode 100644 index 000000000..fdbc2195a --- /dev/null +++ b/private/ntos/tdi/st/sttypes.h @@ -0,0 +1,1103 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sttypes.h + +Abstract: + + This module defines private data structures and types for the NT + Sample transport provider. + +Revision History: + +--*/ + +#ifndef _STTYPES_ +#define _STTYPES_ + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +#define NETBIOS_NAME_LENGTH 16 + +typedef struct _ST_NETBIOS_ADDRESS { + UCHAR NetbiosName[NETBIOS_NAME_LENGTH]; + USHORT NetbiosNameType; +} ST_NETBIOS_ADDRESS, *PST_NETBIOS_ADDRESS; + + +// +// This structure defines things associated with a TP_REQUEST, or outstanding +// TDI request, maintained on a queue somewhere in the transport. All +// requests other than open/close require that a TP_REQUEST block be built. +// + +// +// the types of potential owners of requests +// + +typedef enum _REQUEST_OWNER { + ConnectionType, + AddressType, + DeviceContextType +} REQUEST_OWNER; + +// +// The request itself +// + +typedef struct _TP_REQUEST { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + LIST_ENTRY Linkage; // used by ExInterlocked routines. + KSPIN_LOCK SpinLock; // spinlock for other fields. + // (used in KeAcquireSpinLock calls) + LONG ReferenceCount; // reasons why we can't destroy this req. + + struct _DEVICE_CONTEXT *Provider; // pointer to the device context. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock. + + PIRP IoRequestPacket; // pointer to IRP for this request. + + // + // The following two fields are used to quickly reference the basic + // components of the requests without worming through the IRP's stack. + // + + PVOID Buffer2; // second buffer in the request. + ULONG Buffer2Length; // length of the second buffer. + + // + // The following two fields (Flags and Context) are used to clean up + // queued requests which must be canceled or abnormally completed. + // The Flags field contains bitflags indicating the state of the request, + // and the specific queue type that the request is located on. The + // Context field contains a pointer to the owning structure (TP_CONNECTION + // or TP_ADDRESS) so that the cleanup routines can perform post-cleanup + // operations on the owning structure, such as dereferencing, etc. + // + + ULONG Flags; // disposition of this request. + PVOID Context; // context of this request. + REQUEST_OWNER Owner; // what type of owner this request has. + + KTIMER Timer; // kernel timer for this request. + KDPC Dpc; // DPC object for timeouts. + +} TP_REQUEST, *PTP_REQUEST; + +#define REQUEST_FLAGS_TIMER 0x0001 // a timer is active for this request. +#define REQUEST_FLAGS_TIMED_OUT 0x0002 // a timer expiration occured on this request. +#define REQUEST_FLAGS_ADDRESS 0x0004 // request is attached to a TP_ADDRESS. +#define REQUEST_FLAGS_CONNECTION 0x0008 // request is attached to a TP_CONNECTION. +#define REQUEST_FLAGS_STOPPING 0x0010 // request is being killed. +#define REQUEST_FLAGS_EOR 0x0020 // TdiSend request has END_OF_RECORD mark. +#define REQUEST_FLAGS_DC 0x0080 // request is attached to a TP_DEVICE_CONTEXT +#define REQUEST_FLAGS_SEND_RCV 0x0100 // request is a TdiSend or TdiReceive +#define REQUEST_FLAGS_DELAY 0x0200 // delay IoCompleteRequest until later + +// +// This defines the TP_IRP_PARAMETERS, which is masked onto the +// Parameters section of a send IRP's stack location. +// + +typedef struct _TP_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_SEND Request; + LONG ReferenceCount; + PVOID Connection; +} TP_IRP_PARAMETERS, *PTP_IRP_PARAMETERS; + +#define IRP_SEND_LENGTH(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendLength) + +#define IRP_SEND_FLAGS(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendFlags) + +#define IRP_REFCOUNT(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_CONNECTION(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Connection) + +#define IRP_DEVICE_CONTEXT(_IrpSp) \ + ((PDEVICE_CONTEXT)((_IrpSp)->DeviceObject)) + + +// +// This structure defines the packet object, used to represent a packet +// in some portion of its lifetime. The PACKET.C module contains routines +// to manage this object. +// + +typedef struct _TP_PACKET { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + PNDIS_PACKET NdisPacket; // ptr to owning Ndis Packet + ULONG NdisIFrameLength; // Length of NdisPacket + + LIST_ENTRY Linkage; // used to chain packets together. + BOOLEAN PacketSent; // packet completed by NDIS. + BOOLEAN PacketNoNdisBuffer; // chain on this packet was not allocated. + BOOLEAN CompleteSend; // last packet in send. + PVOID Provider; // The device context of this packet. + + UCHAR Header[1]; // the headers + +} TP_PACKET, *PTP_PACKET; + + + +// +// This structure defines a TP_CONNECTION, or active transport connection, +// maintained on a transport address. +// + +// +// This structure holds our "complex send pointer" indicating +// where we are in the packetization of a send. +// + +typedef struct _TP_SEND_POINTER { + ULONG MessageBytesSent; // up count, bytes sent/this msg. + PIRP CurrentSendIrp; // ptr, current send request in chain. + PMDL CurrentSendMdl; // ptr, current MDL in send chain. + ULONG SendByteOffset; // current byte offset in current MDL. +} TP_SEND_POINTER, *PTP_SEND_POINTER; + +typedef struct _TP_CONNECTION { + + CSHORT Type; + USHORT Size; + + LIST_ENTRY LinkList; // used for link thread or for free + // resource list + KSPIN_LOCK SpinLock; // spinlock for connection protection. + + LONG ReferenceCount; // number of references to this object. + LONG SpecialRefCount; // controls freeing of connection. + + // + // The following lists are used to associate this connection with a + // particular address. + // + + LIST_ENTRY AddressList; // list of connections for given address + LIST_ENTRY AddressFileList; // list for connections bound to a + // given address reference + + // + // The following field is used as linkage in the device context's + // PacketizeQueue + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device context's + // PacketWaitQueue. + // + + LIST_ENTRY PacketWaitLinkage; + + // + // The following field points to the TP_LINK object that describes the + // (active) data link connection for this transport connection. To be + // valid, this field is non-NULL. + // + + struct _TP_ADDRESS_FILE *AddressFile; // pointer to owning Address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock + PFILE_OBJECT FileObject; // easy backlink to file object. + + // + // The following field is specified by the user at connection open time. + // It is the context that the user associates with the connection so that + // indications to and from the client can be associated with a particular + // connection. + // + + CONNECTION_CONTEXT Context; // client-specified value. + + // + // The following two queues are used to associate TdiSend and TdiReceive + // requests with this connection. New arrivals are placed at the end of + // the queues (really a linked list) and requests are processed at the + // front of the queues. The first TdiSend request on the SendQueue is + // the current TdiSend being processed, and the first TdiReceive request + // on the ReceiveQueue is the first TdiReceive being processed, PROVIDED + // the CONNECTION_FLAGS_ACTIVE_RECEIVE flag is set. If this flag is not + // set, then the first TdiReceive request on the ReceiveQueue is not active. + // These queues are managed by the EXECUTIVE interlocked list manipuation + // routines. The actual objects that are on the queues are request control + // blocks (TP_REQUESTs). + // + + LIST_ENTRY SendQueue; // FIFO of outstanding TdiSends. + LIST_ENTRY ReceiveQueue; // FIFO of outstanding TdiReceives. + + // + // The following fields are used to maintain state for the current receive. + // + + ULONG MessageBytesReceived; // up count, bytes recd/this msg. + ULONG MessageBytesAcked; // bytes acked (NR or RO) this msg. + + // + // These fields are only valid if the CONNECTION_FLAGS_ACTIVE_RECEIVE + // flag is set. + // + + PIRP SpecialReceiveIrp; // a "no-request" receive IRP exists. + PTP_REQUEST CurrentReceiveRequest; // ptr, current receive request. + PMDL CurrentReceiveMdl; // ptr, current MDL in receive chain. + ULONG ReceiveByteOffset; // current byte offset in current MDL. + ULONG ReceiveLength; // current receive length, in bytes (total) + + // + // The following fields are used to maintain state for the active send. + // They only have meaning if the connection's SendState is not IDLE. + // Because the TDI client may submit multiple TdiSend requests to comprise + // a full message, we have to keep a complex pointer to the first byte of + // unACKed data (hence the first three fields). We also have a complex + // pointer to the first byte of unsent data (hence the last three fields). + // + + ULONG SendState; // send state machine variable. + + PIRP FirstSendIrp; // ptr, 1st TdiSend's IRP. + PMDL FirstSendMdl; // ptr, 1st unacked MDL in chain/this msg. + ULONG FirstSendByteOffset; // pre-acked bytes in that MDL. + + TP_SEND_POINTER sp; // current send loc, defined above. + ULONG CurrentSendLength; // how long is this send (total) + + // + // This field will be TRUE if we are in the middle of + // processing a receive indication on this connection and + // we are not yet in a state where another indication + // can be handled. + // + + UINT IndicationInProgress; + + // + // The following list head is used as a pointer to a TdiListen/TdiConnect + // request which is in progress. Although manipulated + // with queue instructions, there will only be one request in the queue. + // This is done for consistency with respect to TpCreateRequest, which + // does a great job of creating a request and associating it atomically + // with a supervisory object. + // + + LIST_ENTRY InProgressRequest; // TdiListen/TdiConnect + + // + // If the connection is being disconnected as a result of + // a TdiDisconnect call (RemoteDisconnect is FALSE) then this + // will hold the IRP passed to TdiDisconnect. It is needed + // when the TdiDisconnect request is completed. + // + + PIRP DisconnectIrp; + + // + // If the connection is being closed, this will hold + // the IRP passed to TdiCloseConnection. It is needed + // when the request is completed. + // + + PIRP CloseIrp; + + // + // The following fields are used for connection housekeeping. + // + + ULONG Flags; // attributes of the connection. + ULONG Flags2; // more attributes of the connection. + NTSTATUS Status; // status code for connection rundown. + ST_NETBIOS_ADDRESS CalledAddress; // TdiConnect request's T.A. + USHORT MaximumDataSize; // maximum I-frame data size. + + CHAR RemoteName[16]; + +} TP_CONNECTION, *PTP_CONNECTION; + + +#define CONNECTION_FLAGS_REMOTE_BUSY 0x00000002 // remote netbios reported NO RECEIVE. +#define CONNECTION_FLAGS_VERSION2 0x00000004 // remote netbios is version 2.0. +#define CONNECTION_FLAGS_RECEIVE_WAKEUP 0x00000008 // send a RECEIVE_OUTSTANDING when a receive arrives. +#define CONNECTION_FLAGS_ACTIVE_RECEIVE 0x00000010 // a receive is active. +#define CONNECTION_FLAGS_LISTENER 0x00000020 // we were the passive listener. +#define CONNECTION_FLAGS_CONNECTOR 0x00000040 // we were the active connector. +#define CONNECTION_FLAGS_WAIT_LISTEN 0x00001000 // waiting for listen. +#define CONNECTION_FLAGS_DESTROY 0x00002000 // destroy this connection. +#define CONNECTION_FLAGS_ABORT 0x00004000 // abort this connection. +#define CONNECTION_FLAGS_ORDREL 0x00008000 // we're in orderly release. +#define CONNECTION_FLAGS_STOPPING 0x00020000 // connection is running down. +#define CONNECTION_FLAGS_READY 0x00040000 // sends/rcvs/discons valid. +#define CONNECTION_FLAGS_SUSPENDED 0x00100000 // we're on the PacketWaitQueue. +#define CONNECTION_FLAGS_PACKETIZE 0x00200000 // we're on the PacketizeQueue. +#define CONNECTION_FLAGS_NO_INDICATE 0x40000000 // don't take packets at indication time +#define CONNECTION_FLAGS_FAILING_TO_EOR 0x80000000 // wait for an EOF in an incoming request before sending + +#define CONNECTION_FLAGS2_CLOSING 0x00000002 // connection is closing +#define CONNECTION_FLAGS2_ASSOCIATED 0x00000004 // associated with address +#define CONNECTION_FLAGS2_DISCONNECT 0x00000008 // disconnect done on connection +#define CONNECTION_FLAGS2_ACCEPTED 0x00000010 // accept done on connection +#define CONNECTION_FLAGS2_INDICATING 0x00000020 // connection was manipulated while + // indication was in progress +#define CONNECTION_FLAGS2_WAIT_ACCEPT 0x00000040 // the connection is waiting for + // and accept to send the + // session confirm +#define CONNECTION_FLAGS2_REQ_COMPLETED 0x00000080 // Listen/Connect request completed. +#define CONNECTION_FLAGS2_DISASSOCIATED 0x00000100 // associate CRef has been removed +#define CONNECTION_FLAGS2_DISCONNECTED 0x00000200 // disconnect has been indicated +#define CONNECTION_FLAGS2_REMOTE_VALID 0x00000800 // Connection->RemoteName is valid +#define CONNECTION_FLAGS2_RCV_CANCELLED 0x00002000 // current receive was cancelled +#define CONNECTION_FLAGS2_PRE_ACCEPT 0x00008000 // no TdiAccept after listen completes +#define CONNECTION_FLAGS2_RC_PENDING 0x00010000 // a receive is pending completion +#define CONNECTION_FLAGS2_PEND_INDICATE 0x00020000 // new data received during RC_PENDING + + +#define CONNECTION_SENDSTATE_IDLE 0 // no sends being processed. +#define CONNECTION_SENDSTATE_PACKETIZE 1 // send being packetized. +#define CONNECTION_SENDSTATE_W_PACKET 2 // waiting for free packet. +#define CONNECTION_SENDSTATE_W_LINK 3 // waiting for good link conditions. +#define CONNECTION_SENDSTATE_W_EOR 4 // waiting for TdiSend(EOR). +#define CONNECTION_SENDSTATE_W_ACK 5 // waiting for DATA_ACK. + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to a TP_ADDRESS +// structure, which describes the address that it is bound to. Thus, a +// connection will point to this structure, which describes the address the +// connection was associated with. When the address file closes, all connections +// opened on this address file get closed, too. Note that this may leave an +// address hanging around, with other references. +// + +typedef struct _TP_ADDRESS_FILE { + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + LONG ReferenceCount; // number of references to this object. + + // + // This structure is edited after taking the Address spinlock for the + // owning address. This ensures that the address and this structure + // will never get out of syncronization with each other. + // + + // + // The following field points to a list of TP_CONNECTION structures, + // one per connection open on this address. This list of connections + // is used to help the cleanup process if a process closes an address + // before disassociating all connections on it. By design, connections + // will stay around until they are explicitly + // closed; we use this database to ensure that we clean up properly. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + // + // The following fields are kept for housekeeping purposes. + // + + PIRP Irp; // the irp used for open or close + struct _TP_ADDRESS *Address; // address to which we are bound. + PFILE_OBJECT FileObject; // easy backlink to file object. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the Irp used to close this address file, + // for pended completion. + // + + PIRP CloseIrp; + + // + // is this address file currently indicating a connection request? if yes, we + // need to mark connections that are manipulated during this time. + // + + BOOLEAN ConnectIndicationInProgress; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredConnectionHandler; + BOOLEAN RegisteredDisconnectHandler; + BOOLEAN RegisteredReceiveHandler; + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredExpeditedDataHandler; + BOOLEAN RegisteredErrorHandler; + + // + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + // + // + + PTDI_IND_CONNECT ConnectionHandler; + PVOID ConnectionHandlerContext; + + // + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_DISCONNECT DisconnectHandler; + PVOID DisconnectHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. If the NULL handler + // is specified in a TdiSetEventHandler, then this points to an internal + // routine which does not accept the incoming data. + // + + PTDI_IND_RECEIVE ReceiveHandler; + PVOID ReceiveHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + + // + // An expedited data handler. This handler is used if expedited data is + // expected; it never is in ST, thus this handler should always point to + // the default handler. + // + + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + PVOID ExpeditedDataHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + PVOID ErrorHandlerOwner; + + +} TP_ADDRESS_FILE, *PTP_ADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a TP_ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +typedef struct _TP_ADDRESS { + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + LONG ReferenceCount; // number of references to this object. + + // + // The following spin lock is acquired to edit this TP_ADDRESS structure + // or to scan down or edit the list of address files. + // + + KSPIN_LOCK SpinLock; // lock to manipulate this structure. + + // + // The following fields comprise the actual address itself. + // + + PIRP Irp; // pointer to address creation IRP. + PST_NETBIOS_ADDRESS NetworkName; // this address + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY SendDatagramQueue; // FIFO of outstanding TdiSendDatagrams. + + // + // The following field points to a list of TP_CONNECTION structures, + // one per active, connecting, or disconnecting connections on this + // address. By definition, if a connection is on this list, then + // it is visible to the client in terms of receiving events and being + // able to post requests by naming the ConnectionId. If the connection + // is not on this list, then it is not valid, and it is guaranteed that + // no indications to the client will be made with reference to it, and + // no requests specifying its ConnectionId will be accepted by the transport. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + PTP_PACKET Packet; // header for datagram sends. + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // This structure is used to hold ACLs on the address. + // WARNING: It is allocated from paged pool and can + // only be accessed at IRQL 0. + // + + PSECURITY_DESCRIPTOR SecurityDescriptor; + + // + // Used for delaying StDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + +} TP_ADDRESS, *PTP_ADDRESS; + +#define ADDRESS_FLAGS_GROUP 0x00000001 // set if group, otherwise unique. +#define ADDRESS_FLAGS_CONFLICT 0x00000002 // address in conflict detected. +#define ADDRESS_FLAGS_REGISTERING 0x00000004 // registration in progress. +#define ADDRESS_FLAGS_DEREGISTERING 0x00000008 // deregistration in progress. +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000010 // duplicate name was found on net. +#define ADDRESS_FLAGS_NEEDS_REG 0x00000020 // address must be registered. +#define ADDRESS_FLAGS_STOPPING 0x00000040 // TpStopAddress is in progress. +#define ADDRESS_FLAGS_BAD_ADDRESS 0x00000080 // name in conflict on associated address. +#define ADDRESS_FLAGS_SEND_IN_PROGRESS 0x00000100 // send datagram process active. +#define ADDRESS_FLAGS_CLOSED 0x00000200 // address has been closed; + // existing activity can + // complete, nothing new can start + + + +// +// This structure defines the DEVICE_OBJECT and its extension allocated at +// the time the transport provider creates its device object. +// + +typedef struct _DEVICE_CONTEXT { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + + LIST_ENTRY Linkage; // links them on StDeviceList; + + KSPIN_LOCK Interlock; // GLOBAL spinlock for reference count. + // (used in ExInterlockedXxx calls) + LONG ReferenceCount; // activity count/this provider. + + + // + // The queue of (currently receive only) IRPs waiting to complete. + // + + LIST_ENTRY IrpCompletionQueue; + + // + // Following are protected by Global Device Context SpinLock + // + + KSPIN_LOCK SpinLock; // lock to manipulate this object. + // (used in KeAcquireSpinLock calls) + + // + // the device context state, among open, closing + // + + UCHAR State; + + + // + // The following queue holds free TP_ADDRESS objects available for allocation. + // + + LIST_ENTRY AddressPool; + + // + // These counters keep track of resources uses by TP_ADDRESS objects. + // + + ULONG AddressAllocated; + ULONG AddressInitAllocated; + ULONG AddressMaxAllocated; + ULONG AddressInUse; + ULONG AddressMaxInUse; + ULONG AddressExhausted; + ULONG AddressTotal; + ULONG AddressSamples; + + + // + // The following queue holds free TP_ADDRESS_FILE objects available for allocation. + // + + LIST_ENTRY AddressFilePool; + + // + // These counters keep track of resources uses by TP_ADDRESS_FILE objects. + // + + ULONG AddressFileAllocated; + ULONG AddressFileInitAllocated; + ULONG AddressFileMaxAllocated; + ULONG AddressFileInUse; + ULONG AddressFileMaxInUse; + ULONG AddressFileExhausted; + ULONG AddressFileTotal; + ULONG AddressFileSamples; + + + // + // The following queue holds free TP_CONNECTION objects available for allocation. + // + + LIST_ENTRY ConnectionPool; + + // + // These counters keep track of resources uses by TP_CONNECTION objects. + // + + ULONG ConnectionAllocated; + ULONG ConnectionInitAllocated; + ULONG ConnectionMaxAllocated; + ULONG ConnectionInUse; + ULONG ConnectionMaxInUse; + ULONG ConnectionExhausted; + ULONG ConnectionTotal; + ULONG ConnectionSamples; + + + // + // The following is a free list of TP_REQUEST blocks which have been + // previously allocated and are available for use. + // + + LIST_ENTRY RequestPool; // free request block pool. + + // + // These counters keep track of resources uses by TP_REQUEST objects. + // + + ULONG RequestAllocated; + ULONG RequestInitAllocated; + ULONG RequestMaxAllocated; + ULONG RequestInUse; + ULONG RequestMaxInUse; + ULONG RequestExhausted; + ULONG RequestTotal; + ULONG RequestSamples; + + + // + // The following queue holds I-frame Send packets managed by PACKET.C. + // + + SINGLE_LIST_ENTRY PacketPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG PacketLength; + ULONG PacketHeaderLength; + ULONG PacketAllocated; + ULONG PacketInitAllocated; + ULONG PacketExhausted; + + + // + // The following queue contains Receive packets + // + + SINGLE_LIST_ENTRY ReceivePacketPool; + + // + // These counters keep track of resources uses by NDIS_PACKET objects. + // + + ULONG ReceivePacketAllocated; + ULONG ReceivePacketInitAllocated; + ULONG ReceivePacketExhausted; + + + // + // This queue contains pre-allocated receive buffers + // + + SINGLE_LIST_ENTRY ReceiveBufferPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG ReceiveBufferLength; + ULONG ReceiveBufferAllocated; + ULONG ReceiveBufferInitAllocated; + ULONG ReceiveBufferExhausted; + + + // + // This holds the total memory allocated for the above structures. + // + + ULONG MemoryUsage; + ULONG MemoryLimit; + + + // + // The following field is a head of a list of TP_ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. + + // + // The following queue holds connections which are waiting on available + // packets. As each new packet becomes available, a connection is removed + // from this queue and placed on the PacketizeQueue. + // + + LIST_ENTRY PacketWaitQueue; // queue of packet-starved connections. + LIST_ENTRY PacketizeQueue; // queue of ready-to-packetize connections. + + // + // This queue contains receives that are in progress + // + + LIST_ENTRY ReceiveInProgress; + + // + // NDIS fields + // + + // + // following is used to keep adapter information. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The following fields are used for talking to NDIS. They keep information + // for the NDIS wrapper to use when determining what pool to use for + // allocating storage. + // + + NDIS_HANDLE SendPacketPoolHandle; + NDIS_HANDLE ReceivePacketPoolHandle; + NDIS_HANDLE NdisBufferPoolHandle; + PVOID BufferPoolPointer; + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; + ULONG DeviceNameLength; + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + ST_NDIS_IDENTIFICATION MacInfo; // MAC type and other info + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalAddress; // our local hardware address. + HARDWARE_ADDRESS MulticastAddress; // used as dest in all send + + // + // The reserved Netbios address; consists of 10 zeroes + // followed by LocalAddress; + // + + UCHAR ReservedNetBIOSAddress[NETBIOS_NAME_LENGTH]; + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + + + // + // Counters for most of the statistics that ST maintains; + // some of these are kept elsewhere. + // + // *** NOTE: THE ELEMENTS THAT FOLLOW MATCH THE *** + // *** TDI_PROVIDER_STATISTICS STRUCTURE EXACTLY, *** + // *** ALLOWING THEM TO BE COPIED EASILY. DO NOT *** + // *** CHANGE THEM UNLESS THAT STRUCTURE CHANGES. *** + // + + // + // Basic connections counters. + // + + ULONG OpenConnections; + ULONG ConnectionsAfterNoRetry; + ULONG ConnectionsAfterRetry; + + // + // Counters of previous connections, by disconnect reason. + // + + ULONG LocalDisconnects; + ULONG RemoteDisconnects; + ULONG LinkFailures; + ULONG AdapterFailures; + ULONG SessionTimeouts; + ULONG CancelledConnections; + + // + // Keep track of why connect attempts failed. + // + + ULONG RemoteResourceFailures; + ULONG LocalResourceFailures; + ULONG NotFoundFailures; + ULONG NoListenFailures; // where WE sent "no listen" response + + // + // Counters for datagrams. + // + + ULONG DatagramsSent; + LARGE_INTEGER DatagramBytesSent; + ULONG DatagramsReceived; + LARGE_INTEGER DatagramBytesReceived; + + // + // Counters for NDIS packets. + // + + ULONG PacketsSent; + ULONG PacketsReceived; + + // + // Counters for data packets. + // + + ULONG IFramesSent; + LARGE_INTEGER IFrameBytesSent; + ULONG IFramesReceived; + LARGE_INTEGER IFrameBytesReceived; + ULONG IFramesResent; + LARGE_INTEGER IFrameBytesResent; + ULONG IFramesRejected; + LARGE_INTEGER IFrameBytesRejected; + + + // + // LLC stats. + // + + ULONG T1Expirations; + ULONG T2Expirations; + ULONG MaximumSendWindow; + ULONG AverageSendWindow; + + // + // Netbios stats. + // + + ULONG PiggybackAckQueued; + ULONG PiggybackAckTimeouts; + + // + // Keeps track of "wasted" packet space. + // + + LARGE_INTEGER WastedPacketSpace; + ULONG WastedSpacePackets; + + // + // *** END OF SECTION THAT MATCHES TDI_PROVIDER_STATISTICS *** + // + + // + // Counters for "active" time. + // + + LARGE_INTEGER StStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// device context state definitions +// + +#define DEVICECONTEXT_STATE_CLOSED 0x00 +#define DEVICECONTEXT_STATE_OPEN 0x01 +#define DEVICECONTEXT_STATE_STOPPING 0x02 + + + +// +// Types used to hold information in the send and receive NDIS packets. +// These are storied in the ProtocolReserved section of the packet. +// + +typedef struct _SEND_PACKET_TAG { + USHORT Type; // identifier for packet type + PTP_PACKET Packet; // backpointer to owning TP_PACKET + PVOID Owner; // backpointer to owning structure +} SEND_PACKET_TAG, *PSEND_PACKET_TAG; + +// +// Packet types used in send completion +// + +#define TYPE_I_FRAME 1 // information +#define TYPE_G_FRAME 2 // datagram +#define TYPE_C_FRAME 3 // connect +#define TYPE_D_FRAME 4 // disconnect + + +// +// receive packet used to hold information about this receive +// + +typedef struct _RECEIVE_PACKET_TAG { + LIST_ENTRY Linkage; // used for threading on receive queue + NDIS_STATUS NdisStatus; // completion status for send + PTP_CONNECTION Connection; // connection this receive is occuring on + UCHAR PacketType; // the type of packet we're processing + BOOLEAN AllocatedNdisBuffer; // did we allocate our own NDIS_BUFFERs + BOOLEAN EndOfMessage; // does this receive complete the message + BOOLEAN CompleteReceive; // complete the receive after TransferData? + BOOLEAN TransferDataPended; // TRUE if TransferData returned PENDING +} RECEIVE_PACKET_TAG, *PRECEIVE_PACKET_TAG; + +#define TYPE_AT_INDICATE 1 +#define TYPE_AT_COMPLETE 2 + +// +// receive buffer descriptor (built in memory at the beginning of the buffer) +// + +typedef struct _BUFFER_TAG { + SINGLE_LIST_ENTRY Linkage; // so we always know where it is + PTP_ADDRESS Address; // the address this datagram is for. + PNDIS_BUFFER NdisBuffer; // describes the rest of the buffer + ULONG Length; // the length of the buffer + UCHAR Buffer[1]; // the actual storage (accessed through the NDIS_BUFFER) +} BUFFER_TAG, *PBUFFER_TAG; + +#endif // def _TYPES_ + diff --git a/private/ntos/tdi/st/uframes.c b/private/ntos/tdi/st/uframes.c new file mode 100644 index 000000000..c05f6eaa6 --- /dev/null +++ b/private/ntos/tdi/st/uframes.c @@ -0,0 +1,980 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + uframes.c + +Abstract: + + This module contains a routine called StProcessConnectionless, + that gets control from routines in IND.C when a connectionless + frame is received. + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "st.h" + + + +NTSTATUS +StIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Header, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine processes an incoming DATAGRAM or DATAGRAM_BROADCAST frame. + BROADCAST and normal datagrams have the same receive logic, except + for broadcast datagrams Address will be the broadcast address. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + StHeader - Pointer to a buffer that contains the receive datagram. + The first byte of information is the ST header. + + Length - The length of the MDL pointed to by StHeader. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PLIST_ENTRY p, q; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PTP_REQUEST Request; + ULONG IndicateBytesCopied, MdlBytesCopied; + KIRQL oldirql; + TA_NETBIOS_ADDRESS SourceName; + TA_NETBIOS_ADDRESS DestinationName; + PTDI_CONNECTION_INFORMATION remoteInformation; + ULONG returnLength; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PST_HEADER StHeader; + + // + // If this datagram wasn't big enough for a transport header, then don't + // let the caller look at any data. + // + + if (Length < sizeof(ST_HEADER)) { + return STATUS_ABANDONED; + } + + // + // Update our statistics. + // + + ++DeviceContext->DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &DeviceContext->DatagramBytesReceived, + Length - sizeof(ST_HEADER)); + + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + StHeader = (PST_HEADER)Header; + + TdiBuildNetbiosAddress (StHeader->Source, FALSE, &SourceName); + TdiBuildNetbiosAddress (StHeader->Destination, FALSE, &DestinationName); + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + // + // Find the first open address file in the list. + // + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + StReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. + // + // NOTE: We should check if this receive dataframs is for + // a specific address. + // + + q = RemoveHeadList (&addressFile->ReceiveDatagramQueue); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if (q != &addressFile->ReceiveDatagramQueue) { + + Request = CONTAINING_RECORD (q, TP_REQUEST, Linkage); + + // + // Copy the actual user data. + // + + MdlBytesCopied = 0; + + status = TdiCopyBufferToMdl ( + StHeader, + sizeof(ST_HEADER), // offset + Length - sizeof(ST_HEADER), // length + Request->IoRequestPacket->MdlAddress, + 0, + &MdlBytesCopied); + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))-> + ReturnDatagramInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + sizeof (TA_NETBIOS_ADDRESS)); + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + remoteInformation->RemoteAddressLength); + + returnLength = remoteInformation->RemoteAddressLength; + remoteInformation->RemoteAddressLength = returnLength; + + } + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } + + StCompleteRequest (Request, STATUS_SUCCESS, MdlBytesCopied); + + } else { + + // + // no receive datagram requests; is there a kernel client? + // + + if (addressFile->RegisteredReceiveDatagramHandler) { + + IndicateBytesCopied = 0; + + // + // Note that we can always set the COPY_LOOKAHEAD + // flag because we are indicating from our own + // buffer, not directly from a lookahead indication. + // + + status = (*addressFile->ReceiveDatagramHandler)( + addressFile->ReceiveDatagramHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + Length - sizeof(ST_HEADER), // indicated + Length - sizeof(ST_HEADER), // available + &IndicateBytesCopied, + Header + sizeof(ST_HEADER), + &irp); + + if (status == STATUS_SUCCESS) { + + // + // The client accepted the datagram and so we're done. + // + + } else if (status == STATUS_DATA_NOT_ACCEPTED) { + + // + // The client did not accept the datagram and we need to satisfy + // a TdiReceiveDatagram, if possible. + // + + status = STATUS_MORE_PROCESSING_REQUIRED; + + } else if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client returned an IRP that we should queue up to the + // address to satisfy the request. + // + + irp->IoStatus.Status = STATUS_PENDING; // init status information. + irp->IoStatus.Information = 0; + irpSp = IoGetCurrentIrpStackLocation (irp); // get current stack loctn. + if ((irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) || + (irpSp->MinorFunction != TDI_RECEIVE_DATAGRAM)) { + irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + return status; + } + + // + // Now copy the actual user data. + // + + MdlBytesCopied = 0; + + status = TdiCopyBufferToMdl ( + StHeader, + sizeof(ST_HEADER) + IndicateBytesCopied, + Length - sizeof(ST_HEADER) - IndicateBytesCopied, + irp->MdlAddress, + 0, + &MdlBytesCopied); + + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = status; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + } + } + } + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + StReferenceAddressFile(addressFile); + break; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now dereference the previous address file with + // the lock released. + // + + StDereferenceAddressFile (prevaddressFile); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + } // end of while loop + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return status; // to dispatcher. +} /* StIndicateDatagram */ + + +NTSTATUS +StProcessConnect( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PST_HEADER Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming connect frame. It scans for + posted listens, otherwise it indicates to connect handlers + on this address if they are registered. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the ST header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1, cancelirql; + NTSTATUS status; + PTP_CONNECTION Connection; + BOOLEAN ConnectIndicationBlocked = FALSE; + PLIST_ENTRY p; + BOOLEAN UsedListeningConnection = FALSE; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + + PTP_REQUEST request; + PIO_STACK_LOCATION irpSp; + ULONG returnLength; + PTDI_CONNECTION_INFORMATION remoteInformation; + TA_NETBIOS_ADDRESS TempAddress; + PIRP acceptIrp; + + CONNECTION_CONTEXT connectionContext; + + // + // If we are just registering or deregistering this address, then don't + // allow state changes. Just throw the packet away, and let the frame + // distributor try the next address. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING)) { + return STATUS_SUCCESS; + } + + // + // This is an incoming connection request. If we have a listening + // connection on this address, then continue with the connection setup. + // If there is no outstanding listen, then indicate any kernel mode + // clients that want to know about this frame. If a listen was posted, + // then a connection has already been set up for it. + // + + // + // First, check if we already have an active connection with + // this remote on this address. If so, we ignore this + // (NOTE: This is not the correct behaviour for a real + // transport). + // + + // + // If successful this adds a reference. + // + + if (Connection = StLookupRemoteName(Address, Header->Source)) { + + StDereferenceConnection ("Lookup done", Connection); + return STATUS_ABANDONED; + + } + + // If successful, this adds a reference which is removed before + // this function returns. + + Connection = StLookupListeningConnection (Address); + if (Connection == NULL) { + + // + // not having a listening connection is not reason to bail out here. + // we need to indicate to the user that a connect attempt occurred, + // and see if there is a desire to use this connection. We + // indicate in order to all address files that are + // using this address. + // + // If we already have an indication pending on this address, + // we ignore this frame (the NAME_QUERY may have come from + // a different address, but we can't know that). Also, if + // there is already an active connection on this remote + // name, then we ignore the frame. + // + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + StReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if ((addressFile->RegisteredConnectionHandler == TRUE) && + (!addressFile->ConnectIndicationInProgress)) { + + + TdiBuildNetbiosAddress ( + Header->Source, + FALSE, + &TempAddress); + + addressFile->ConnectIndicationInProgress = TRUE; + + // + // we have a connection handler, now indicate that a connection + // attempt occurred. + // + + status = (addressFile->ConnectionHandler)( + addressFile->ConnectionHandlerContext, + sizeof (TDI_ADDRESS_NETBIOS), + &TempAddress, + 0, + NULL, + 0, + NULL, + &connectionContext, + &acceptIrp); + + if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // the user has connected a currently open connection, but + // we have to figure out which one it is. + // + + // + // If successful this adds a reference of type LISTENING + // (the same what StLookupListeningConnection adds). + // + + Connection = StLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + StPrint0("MORE_PROCESSING_REQUIRED, connection not found\n"); + addressFile->ConnectIndicationInProgress = FALSE; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + + } else { + + if (Connection->AddressFile->Address != Address) { + addressFile->ConnectIndicationInProgress = FALSE; + + StPrint0("MORE_PROCESSING_REQUIRED, address wrong\n"); + StStopConnection (Connection, STATUS_INVALID_ADDRESS); + StDereferenceConnection("Bad Address", Connection); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + // + // OK, we have a valid connection. If the response to + // this connection was disconnect, we need to reject + // the connection request and return. If it was accept + // or not specified (to be done later), we simply + // fall through and continue processing on the U Frame. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + + Connection->Flags2 &= ~CONNECTION_FLAGS2_DISCONNECT; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + StPrint0("MORE_PROCESSING_REQUIRED, disconnect\n"); + addressFile->ConnectIndicationInProgress = FALSE; + StDereferenceConnection("Disconnecting", Connection); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + } + + // + // This connection is ready. + // + + Connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + Connection->Status = STATUS_PENDING; + Connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + Connection->Flags |= CONNECTION_FLAGS_READY; + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Indication completed", Connection); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + // + // Make a note that we have to set + // addressFile->ConnectIndicationInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + ConnectIndicationBlocked = TRUE; + StDereferenceAddressFile (addressFile); + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + break; // exit the while + + } else if (status == STATUS_INSUFFICIENT_RESOURCES) { + + // + // we know the address, but can't create a connection to + // use on it. This gets passed to the network as a response + // saying I'm here, but can't help. + // + + addressFile->ConnectIndicationInProgress = FALSE; + + StDereferenceAddressFile (addressFile); + return STATUS_ABANDONED; + + } else { + + addressFile->ConnectIndicationInProgress = FALSE; + goto whileend; // try next address file + + } // end status ifs + + } else { + + goto whileend; // try next address file + + } // end no indication handler + +whileend: + // + // Jumping here is like a continue, except that the + // addressFile pointer is advanced correctly. + // + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + StReferenceAddressFile(addressFile); + break; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now dereference the previous address file with + // the lock released. + // + + StDereferenceAddressFile (prevaddressFile); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + } // end of loop through the address files. + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if (Connection == NULL) { + + // + // We used to return MORE_PROCESSING_REQUIRED, but + // since we matched with this address, no other + // address is going to match, so abandon it. + // + + return STATUS_ABANDONED; + + } + + } else { // end connection == null + + UsedListeningConnection = TRUE; + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + // + // If this listen indicated that we should wait for a + // TdiAccept, then do that, otherwise the connection is + // ready. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) == 0) { + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_ACCEPT; + + } else { + + Connection->Flags |= CONNECTION_FLAGS_READY; + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Listen completed", Connection); + + } + + // + // We have a completed connection with a queued listen. Complete + // the listen and let the user do an accept at some time down the + // road. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock (cancelirql); + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + // + // Don't clear this until now, so that the connection is all + // set up before we allow more indications. + // + + Connection->IndicationInProgress = FALSE; + + StCompleteRequest (request, status, 0); + + } + + + // + // Before we continue, store the remote guy's transport address + // into the TdiListen's TRANSPORT_CONNECTION buffer. This allows + // the client to determine who called him. + // + + Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + NdisMoveFromMappedMemory( + Connection->CalledAddress.NetbiosName, + Header->Source, + 16); + + NdisMoveFromMappedMemory( + Connection->RemoteName, + Header->Source, + 16); + + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + + StDereferenceConnection("ProcessNameQuery done", Connection); + + return STATUS_ABANDONED; + +} /* StProcessConnect */ + + +NTSTATUS +StProcessConnectionless( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PST_HEADER StHeader, + IN ULONG StLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ) + +/*++ + +Routine Description: + + This routine receives control from the data link provider as an + indication that a connectionless frame has been received on the data link. + Here we dispatch to the correct handler. + +Arguments: + + DeviceContext - Pointer to our device context. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + StHeader - Points to the ST header of the incoming packet. + + StLength - Actual length in bytes of the packet, starting at the + StHeader. + + SourceRouting - Source routing information in the MAC header. + + SourceRoutingLength - The length of SourceRouting. + + DatagramAddress - If this function returns STATUS_MORE_PROCESSING_ + REQUIRED, this will be the address the datagram should be + indicated to. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS Address; + KIRQL oldirql; + NTSTATUS status; + PLIST_ENTRY Flink; + BOOLEAN MatchedAddress; + PUCHAR MatchName; + + // + // Verify that this frame is long enough to examine. + // + + if (StLength < sizeof(ST_HEADER)) { + return STATUS_ABANDONED; // frame too small. + } + + // + // We have a valid connectionless protocol frame that's not a + // datagram, so deliver it to every address which matches the + // destination name in the frame. + // + + MatchedAddress = FALSE; + + // + // Search for the address; for broadcast datagrams we + // search for the special "broadcast" address. + // + + if ((StHeader->Command == ST_CMD_DATAGRAM) && + (StHeader->Flags & ST_FLAGS_BROADCAST)) { + + MatchName = NULL; + + } else { + + MatchName = StHeader->Destination; + + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (Flink = DeviceContext->AddressDatabase.Flink; + Flink != &DeviceContext->AddressDatabase; + Flink = Flink->Flink) { + + Address = CONTAINING_RECORD ( + Flink, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + if (StMatchNetbiosAddress (Address, MatchName)) { + + StReferenceAddress ("UI Frame", Address); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (MatchedAddress) { + + // + // Deliver the frame to the current address. + // + + switch (StHeader->Command) { + + case ST_CMD_CONNECT: + + status = StProcessConnect ( + DeviceContext, + Address, + StHeader, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case ST_CMD_DATAGRAM: + + // + // Reference the datagram so it sticks around until the + // ReceiveComplete, when it is processed. + // + + StReferenceAddress ("Datagram indicated", Address); + *DatagramAddress = Address; + status = STATUS_MORE_PROCESSING_REQUIRED; + break; + + default: + + ASSERT(FALSE); + + } /* switch on frame command code */ + + StDereferenceAddress ("Done", Address); // done with previous address. + + } else { + + status = STATUS_ABANDONED; + + } + + return status; + +} /* StProcessConnectionless */ + diff --git a/private/ntos/tdi/tcpip/dirs b/private/ntos/tdi/tcpip/dirs new file mode 100644 index 000000000..1ada6a1e5 --- /dev/null +++ b/private/ntos/tdi/tcpip/dirs @@ -0,0 +1,28 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + ip \ + tcp + +OPTIONAL_DIRS= + diff --git a/private/ntos/tdi/tcpip/h/oscfg.h b/private/ntos/tdi/tcpip/h/oscfg.h new file mode 100644 index 000000000..5544d5b40 --- /dev/null +++ b/private/ntos/tdi/tcpip/h/oscfg.h @@ -0,0 +1,72 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#ifndef OSCFG_INCLUDED +#define OSCFG_INCLUDED + + +#define net_short(x) ((((x)&0xff) << 8) | (((x)&0xff00) >> 8)) + +//#define net_long(x) (((net_short((x)&0xffff)) << 16) | net_short((((x)&0xffff0000L)>>16))) +#define net_long(x) (((((ulong)(x))&0xffL)<<24) | \ + ((((ulong)(x))&0xff00L)<<8) | \ + ((((ulong)(x))&0xff0000L)>>8) | \ + ((((ulong)(x))&0xff000000L)>>24)) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + + +#ifdef VXD +///////////////////////////////////////////////////////////////////////////// +// +// VXD definitions +// +//////////////////////////////////////////////////////////////////////////// + +#include + +#pragma code_seg("_LTEXT", "LCODE") +#pragma data_seg("_LDATA", "LCODE") + +//* pragma bodies for bracketing of initialization code. + +#define BEGIN_INIT code_seg("_ITEXT", "ICODE") +#define BEGIN_INIT_DATA data_seg("_IDATA", "ICODE") +#define END_INIT code_seg() +#define END_INIT_DATA data_seg() + +#else // VXD +#ifdef NT + +////////////////////////////////////////////////////////////////////////////// +// +// NT definitions +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#define BEGIN_INIT +#define END_INIT + +#else // NT + +///////////////////////////////////////////////////////////////////////////// +// +// Definitions for additional environments go here +// +///////////////////////////////////////////////////////////////////////////// + +#error Environment specific definitions missing + +#endif // NT + +#endif // VXD + + +#endif // OSCFG_INCLUDED diff --git a/private/ntos/tdi/tcpip/h/packoff.h b/private/ntos/tdi/tcpip/h/packoff.h new file mode 100644 index 000000000..c47cd038f --- /dev/null +++ b/private/ntos/tdi/tcpip/h/packoff.h @@ -0,0 +1,37 @@ +/*++ + +Copyright (c) 1990,91 Microsoft Corporation + +Module Name: + + packoff.h + +Abstract: + + This file turns packing of structures off. (That is, it enables + automatic alignment of structure fields.) An include file is needed + because various compilers do this in different ways. + + packoff.h is the complement to packon.h. An inclusion of packoff.h + MUST ALWAYS be preceded by an inclusion of packon.h, in one-to-one + correspondence. + +Author: + + Chuck Lenzmeier (chuckl) 4-Mar-1990 + +Revision History: + + 15-Apr-1991 JohnRo + Created lint-able variant. +--*/ + +#if ! (defined(lint) || defined(_lint)) + +#ifndef VXD +#if i386 +#pragma warning(disable:4103) +#endif +#endif +#pragma pack() // x86, MS compiler; MIPS, MIPS compiler +#endif // ! (defined(lint) || defined(_lint)) diff --git a/private/ntos/tdi/tcpip/h/packon.h b/private/ntos/tdi/tcpip/h/packon.h new file mode 100644 index 000000000..f2c73cb85 --- /dev/null +++ b/private/ntos/tdi/tcpip/h/packon.h @@ -0,0 +1,35 @@ +/*++ + +Copyright (c) 1990,91 Microsoft Corporation + +Module Name: + + packon.h + +Abstract: + + This file turns packing of structures on. (That is, it disables + automatic alignment of structure fields.) An include file is needed + because various compilers do this in different ways. + + The file packoff.h is the complement to this file. + +Author: + + Chuck Lenzmeier (chuckl) 4-Mar-1990 + +Revision History: + + 15-Apr-1991 JohnRo + Created lint-able variant. +--*/ + +#if ! (defined(lint) || defined(_lint)) + +#ifndef VXD +#if i386 +#pragma warning(disable:4103) +#endif +#endif +#pragma pack(1) // x86, MS compiler; MIPS, MIPS compiler +#endif // ! (defined(lint) || defined(_lint)) diff --git a/private/ntos/tdi/tcpip/h/queue.h b/private/ntos/tdi/tcpip/h/queue.h new file mode 100644 index 000000000..f1e23d682 --- /dev/null +++ b/private/ntos/tdi/tcpip/h/queue.h @@ -0,0 +1,87 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** QUEUE.H - TCP/UDP queuing definitons. +// +// This file contains the definitions for the queue functions used +// by the TCP/UDP code. +// + +//* Definition of a queue linkage field. +struct Queue { + struct Queue *q_next; + struct Queue *q_prev; +}; /* Queue */ + +typedef struct Queue Queue; + +//* Initialize queue macro. + +#define INITQ(q) { (q)->q_next = (q);\ + (q)->q_prev = (q); } + +//* Macro to check for queue empty. +#define EMPTYQ(q) ((q)->q_next == (q)) + +//* Place an element onto the end of the queue. + +#define ENQUEUE(q, e) { (q)->q_prev->q_next = (e);\ + (e)->q_prev = (q)->q_prev;\ + (q)->q_prev = (e);\ + (e)->q_next = (q); } + +//* Remove an element from the head of the queue. This macro assumes the queue +// is not empty. The element is returned as type t, queued through linkage +// l. + +#define DEQUEUE(q, ptr, t, l) {\ + Queue *__tmp__;\ + \ + __tmp__ = (q)->q_next;\ + (q)->q_next = __tmp__->q_next;\ + __tmp__->q_next->q_prev = (q);\ + (ptr) = STRUCT_OF(t, __tmp__, l);\ + } + +//* Peek at an element at the head of the queue. We return a pointer to it +// without removing anything. + +#define PEEKQ(q, ptr, t, l) {\ + Queue *__tmp__;\ + \ + __tmp__ = (q)->q_next;\ + (ptr) = STRUCT_OF(t, __tmp__, l);\ + } + +//* Macro to push an element onto the head of a queue. + +#define PUSHQ(q, e) { (e)->q_next = (q)->q_next;\ + (q)->q_next->q_prev = (e);\ + (e)->q_prev = (q);\ + (q)->q_next = e; } + +//* Macro to remove an element from the middle of a queue. +#define REMOVEQ(q) { (q)->q_next->q_prev = (q)->q_prev;\ + (q)->q_prev->q_next = (q)->q_next; } + +//** The following macros define methods for working with queue without +// dequeueing, mostly dealing with Queue structures directly. + +//* Macro to define the end of a Q, used in walking a queue sequentially. +#define QEND(q) (q) + +//* Macro to get the first on a queue. +#define QHEAD(q) (q)->q_next + +//* Macro to get a structure, given a queue. + +#define QSTRUCT(t, q, l) STRUCT_OF(t, (q), l) + +//* Macro to get the next thing on q queue. + +#define QNEXT(q) (q)->q_next + + diff --git a/private/ntos/tdi/tcpip/h/tdint.h b/private/ntos/tdi/tcpip/h/tdint.h new file mode 100644 index 000000000..852f29c2a --- /dev/null +++ b/private/ntos/tdi/tcpip/h/tdint.h @@ -0,0 +1,41 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) 1991-1993 Microsoft Corporation + +Module Name: + + tdint.h + +Abstract: + + This file defines TDI types specific to the NT environment. + +Author: + + Mike Massa (mikemas) August 13, 1993 + +Revision History: + +--*/ + +#ifndef _TDINT_ +#define _TDINT_ + +#include + +typedef PTDI_IND_CONNECT PConnectEvent; +typedef PTDI_IND_DISCONNECT PDisconnectEvent; +typedef PTDI_IND_ERROR PErrorEvent; +typedef PTDI_IND_RECEIVE PRcvEvent; +typedef PTDI_IND_RECEIVE_DATAGRAM PRcvDGEvent; +typedef PTDI_IND_RECEIVE_EXPEDITED PRcvExpEvent; + +typedef IRP EventRcvBuffer; +typedef IRP ConnectEventInfo; + +// +// BUGBUG: What about SEND_POSSIBLE???? +// + +#endif // ifndef _TDINT_ + diff --git a/private/ntos/tdi/tcpip/ip/arp.c b/private/ntos/tdi/tcpip/ip/arp.c new file mode 100644 index 000000000..3c8253566 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arp.c @@ -0,0 +1,4839 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** arp.c - ARP VxD routines. +// +// This file containes all of the ARP related routines, including +// table lookup, registration, etc. +// +// ARP is architected to support multiple protocols, but for now +// it in only implemented to take one protocol (IP). This is done +// for simplicity and ease of implementation. In the future we may +// split ARP out into a seperate driver. + +#include "oscfg.h" +#ifdef VXD +#include +#endif +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "ipdef.h" +#include "llipif.h" +#include "arp.h" +#include "arpdef.h" +#include "tdiinfo.h" +#include "ipinfo.h" +#include "llinfo.h" +#include "tdistat.h" +#include "iproute.h" +#include "iprtdef.h" +#include "arpinfo.h" +#include "ipinit.h" + +#ifndef CHICAGO +#ifndef _PNP_POWER +#define NDIS_MAJOR_VERSION 0x03 +#define NDIS_MINOR_VERSION 0 +#else +#define NDIS_MAJOR_VERSION 0x04 +#define NDIS_MINOR_VERSION 0 +#endif +#endif + +#ifndef NDIS_API +#define NDIS_API +#endif + + +static ulong ARPLookahead = LOOKAHEAD_SIZE; + +static uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06"; +static uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70"; +static uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00"; +static uchar ARCBcst[] = "\x00\x00\xd5"; + +static uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00"; +static uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00"; +static uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06"; + +#ifdef NT +static WCHAR ARPName[] = TCP_NAME; +#else // NT +static uchar ARPName[] = TCP_NAME; +#endif // NT + +NDIS_HANDLE ARPHandle; // Our NDIS protocol handle. + +uint ArpCacheLife; +uint sArpAlwaysSourceRoute; // True if we always send ARP requests + // with source route info on token ring. +uint sIPAlwaysSourceRoute; +extern uchar TrRii; + +extern PDRIVER_OBJECT IPDriverObject; + +extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint); +extern void IPTDComplete(void *, PNDIS_PACKET, NDIS_STATUS, uint); +extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS); +extern void IPStatus(void *, NDIS_STATUS, void *, uint); +extern void IPRcvComplete(void); +extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset); + +extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS); +extern void IPULUnloadNotify(void); + +#ifdef _PNP_POWER +extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNP, + void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo); +extern void IPDelInterface(void *Context); + +extern void NotifyOfUnload(void); + + +extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle); +extern int IsLLInterfaceValueNull (NDIS_HANDLE Handle) ; +extern void CloseIFConfig(NDIS_HANDLE Handle); + +#endif + + +// Tables for bitswapping. + +uchar SwapTableLo[] = { + 0, // 0 + 0x08, // 1 + 0x04, // 2 + 0x0c, // 3 + 0x02, // 4 + 0x0a, // 5, + 0x06, // 6, + 0x0e, // 7, + 0x01, // 8, + 0x09, // 9, + 0x05, // 10, + 0x0d, // 11, + 0x03, // 12, + 0x0b, // 13, + 0x07, // 14, + 0x0f // 15 +}; + +uchar SwapTableHi[] = { + 0, // 0 + 0x80, // 1 + 0x40, // 2 + 0xc0, // 3 + 0x20, // 4 + 0xa0, // 5, + 0x60, // 6, + 0xe0, // 7, + 0x10, // 8, + 0x90, // 9, + 0x50, // 10, + 0xd0, // 11, + 0x30, // 12, + 0xb0, // 13, + 0x70, // 14, + 0xf0 // 15 +}; + +// Table of source route maximum I-field lengths for token ring. +ushort IFieldSize[] = { + 516, + 1500, + 2052, + 4472, + 8191 +}; + +#define LF_BIT_SHIFT 4 +#define MAX_LF_BITS 4 + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Disposable init code. +// +void FreeARPInterface(ARPInterface *Interface); +void ARPOpen(void *Context); + +#pragma alloc_text(INIT, ARPInit) +#ifndef _PNP_POWER +#pragma alloc_text(INIT, FreeARPInterface) +#pragma alloc_text(INIT, ARPOpen) +#pragma alloc_text(INIT, ARPRegister) +#else +#pragma alloc_text(PAGE, ARPOpen) +#pragma alloc_text(PAGE, ARPRegister) + +#endif + +// +// Paged code +// +void NotifyConflictProc(CTEEvent *Event, void *Context); + +#pragma alloc_text(PAGE, NotifyConflictProc) + +#endif // ALLOC_PRAGMA +#endif // NT + +#ifdef VXD +extern void EnableInts(void); +#endif + +//* DoNDISRequest - Submit a request to an NDIS driver. +// +// This is a utility routine to submit a general request to an NDIS +// driver. The caller specifes the request code (OID), a buffer and +// a length. This routine allocates a request structure, +// fills it in, and submits the request. +// +// Entry: +// Adapter - A pointer to the ARPInterface adapter structure. +// Request - Type of request to be done (Set or Query) +// OID - Value to be set/queried. +// Info - A pointer to the buffer to be passed. +// Length - Length of data in the buffer. +// Needed - On return, filled in with bytes needed in buffer. +// +// Exit: +// +NDIS_STATUS +DoNDISRequest(ARPInterface *Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID, + void *Info, uint Length, uint *Needed) +{ + NDIS_REQUEST Request; // Request structure we'll use. + NDIS_STATUS Status; + + // Now fill it in. + Request.RequestType = RT; + if (RT == NdisRequestSetInformation) { + Request.DATA.SET_INFORMATION.Oid = OID; + Request.DATA.SET_INFORMATION.InformationBuffer = Info; + Request.DATA.SET_INFORMATION.InformationBufferLength = Length; + } else { + Request.DATA.QUERY_INFORMATION.Oid = OID; + Request.DATA.QUERY_INFORMATION.InformationBuffer = Info; + Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; + } + + // Initialize the block structure. + CTEInitBlockStruc(&Adapter->ai_block); +#ifdef VXD + EnableInts(); +#endif + + // Submit the request. + NdisRequest(&Status, Adapter->ai_handle, &Request); + + // Wait for it to finish + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&Adapter->ai_block); + + if (Needed != NULL) + *Needed = Request.DATA.QUERY_INFORMATION.BytesNeeded; + + return Status; +} +//* FreeARPBuffer - Free a header and buffer descriptor pair. +// +// Called when we're done with a buffer. We'll free the buffer and the +// buffer descriptor pack to the interface. +// +// Entry: Interface - Interface buffer/bd came frome. +// Buffer - NDIS_BUFFER to be freed. +// +// Returns: Nothing. +// +void +FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer) +{ + CTELockHandle lhandle; + uchar **Header; // header buffer to be freed. + uint Size; + + Size = NdisBufferLength(Buffer); + + if (Size <= Interface->ai_sbsize) { +#ifdef VXD + // A small buffer, put him on the list. + NDIS_BUFFER_LINKAGE(Buffer) = Interface->ai_sblist; + Interface->ai_sblist = Buffer; +#else + ExInterlockedPushEntrySList( + &Interface->ai_sblist, + STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next), + &Interface->ai_lock + ); + +#endif + + return; + } else { + // A big buffer. Get the buffer pointer, link it on, and free the + // NDIS buffer. + Header = (uchar **)NdisBufferVirtualAddress(Buffer); + + CTEGetLock(&Interface->ai_lock, &lhandle); + *Header = Interface->ai_bblist; + Interface->ai_bblist = (uchar *)Header; + CTEFreeLock(&Interface->ai_lock, lhandle); + + NdisFreeBuffer(Buffer); + } +} + +//* GrowARPHeaders - Grow the ARP header buffer list. +// +// Called when we need to grow the ARP header buffer list. Called with the +// interface lock held. +// +// Input: Interface - Interface on which to grow. +// +// Returns: Pointer to newly allocated buffer, or NULL. +// +PNDIS_BUFFER +GrowARPHeaders(ARPInterface *Interface) +{ + ARPBufferTracker *NewTracker; + PNDIS_BUFFER Buffer, ReturnBuffer; + uchar *Header; + uint i; + NDIS_STATUS Status; + CTELockHandle Handle; + + CTEGetLock(&Interface->ai_lock, &Handle); + + // Make sure we're allowed to allocate. + if (Interface->ai_curhdrs >= Interface->ai_maxhdrs) + goto failure; + + NewTracker = CTEAllocMem(sizeof(ARPBufferTracker)); + if (NewTracker == NULL) + goto failure; // We're out of memory. + + NdisAllocateBufferPool(&Status, &NewTracker->abt_handle, + ARP_HDRBUF_GROW_SIZE); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewTracker); + goto failure; + } + + Header = CTEAllocMem((uint)Interface->ai_sbsize * ARP_HDRBUF_GROW_SIZE); + if (Header == NULL) { + NdisFreeBufferPool(NewTracker->abt_handle); + CTEFreeMem(NewTracker); + goto failure; + } + + // Got the resources we need, allocate the buffers. + NewTracker->abt_buffer = Header; + NewTracker->abt_next = Interface->ai_buflist; + Interface->ai_buflist = NewTracker; + ReturnBuffer = NULL; + Interface->ai_curhdrs += ARP_HDRBUF_GROW_SIZE; + CTEFreeLock(&Interface->ai_lock, Handle); + + for (i = 0; i < ARP_HDRBUF_GROW_SIZE; i++) { + NdisAllocateBuffer(&Status, &Buffer, NewTracker->abt_handle, + Header + (i * Interface->ai_sbsize), Interface->ai_sbsize); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + if (i != 0) { + FreeARPBuffer(Interface, Buffer); + } else + ReturnBuffer = Buffer; + } + + // Update for what we didn't allocate, if any. + CTEInterlockedAddUlong(&Interface->ai_curhdrs, i - ARP_HDRBUF_GROW_SIZE, + &Interface->ai_lock); + + return ReturnBuffer; + +failure: + CTEFreeLock(&Interface->ai_lock, Handle); + return NULL; +} + +//* GetARPBuffer - Get a buffer and descriptor +// +// Returns a pointer to an NDIS_BUFFER and a pointer to a buffer +// of the specified size. +// +// Entry: Interface - Pointer to ARPInterface structure to allocate buffer from. +// BufPtr - Pointer to where to return buf address. +// Size - Size in bytes of buffer needed. +// +// Returns: Pointer to NDIS_BUFFER if successfull, NULL if not +// +PNDIS_BUFFER +GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar size) +{ + CTELockHandle lhandle; // Lock handle + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; // NDIS buffer allocated. + + if (size <= Interface->ai_sbsize) { +#ifdef VXD + Buffer = Interface->ai_sblist; + if (Buffer != NULL) { + Interface->ai_sblist = NDIS_BUFFER_LINKAGE(Buffer); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + return Buffer; +#else + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &Interface->ai_sblist, + &Interface->ai_lock + ); + if (BufferLink != NULL) { + Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + return Buffer; +#endif + + } else { + Buffer = GrowARPHeaders(Interface); + if (Buffer != NULL) { + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + } + return Buffer; + } + } else { + // Need a 'big' buffer. + CTEGetLock(&Interface->ai_lock, &lhandle); + if ((*BufPtr = Interface->ai_bblist) != (uchar *)NULL) { + Interface->ai_bblist = *(uchar **)*BufPtr; + CTEFreeLock(&Interface->ai_lock, lhandle); // Got a buffer. + NdisAllocateBuffer(&Status, &Buffer, Interface->ai_bpool, *BufPtr, + size); + if (Status == NDIS_STATUS_SUCCESS) + return Buffer; + else { // Couldn't get NDIS buffer, free our buffer. + CTEGetLock(&Interface->ai_lock, &lhandle); + *(uchar **)&**BufPtr = Interface->ai_bblist; + Interface->ai_bblist = *BufPtr; + CTEFreeLock(&Interface->ai_lock, lhandle); + return (PNDIS_BUFFER)NULL; + } + } + + // Couldn't get a header buffer, free lock and return NULL. + CTEFreeLock(&Interface->ai_lock, lhandle); + return (PNDIS_BUFFER)NULL; + } +} + + +//* BitSwap - Bit swap two strings. +// +// A routine to bitswap two strings. +// +// Input: Dest - Destination of swap. +// Src - Src string to be swapped. +// Length - Length in bytes to swap. +// +// Returns: Nothing. +// +void +BitSwap(uchar *Dest, uchar *Src, uint Length) +{ + uint i; + uchar Temp, TempSrc; + + for (i = 0; i < Length; i++, Dest++, Src++) { + TempSrc = *Src; + Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f]; + *Dest = Temp; + } + +} + + +//* SendARPPacket - Build a header, and send a packet. +// +// A utility routine to build and ARP header and send a packet. We assume +// the media specific header has been built. +// +// Entry: Interface - Interface for NDIS drive. +// Packet - Pointer to packet to be sent +// Header - Pointer to header to fill in. +// Opcode - Opcode for packet. +// Address - Source HW address. +// SrcAddr - Address to use as our source h/w address. +// Destination - Destination IP address. +// Src - Source IP address. +// HWType - Hardware type. +// CheckIF - TRUE iff we are to check the I/F status before +// sending. +// +// Returns: NDIS_STATUS of send. +// +NDIS_STATUS +SendARPPacket(ARPInterface *Interface, PNDIS_PACKET Packet, ARPHeader *Header, ushort Opcode, + uchar *Address, uchar *SrcAddr, IPAddr Destination, IPAddr Src, + ushort HWType, uint CheckIF) +{ + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + uint PacketDone; + uchar *AddrPtr; + + Header->ah_hw = HWType; + Header->ah_pro = net_short(ARP_ETYPE_IP); + Header->ah_hlen = Interface->ai_addrlen; + Header->ah_plen = sizeof(IPAddr); + Header->ah_opcode = Opcode; + AddrPtr = Header->ah_shaddr; + + if (SrcAddr == NULL) + SrcAddr = Interface->ai_addr; + + CTEMemCopy(AddrPtr, SrcAddr, Interface->ai_addrlen); + + AddrPtr += Interface->ai_addrlen; + *(IPAddr UNALIGNED *)AddrPtr = Src; + AddrPtr += sizeof(IPAddr); + + if (Address != (uchar *)NULL) + CTEMemCopy(AddrPtr, Address, Interface->ai_addrlen); + else + CTEMemSet(AddrPtr, 0, Interface->ai_addrlen); + + AddrPtr += Interface->ai_addrlen; + *(IPAddr UNALIGNED *)AddrPtr = Destination; + + PacketDone = FALSE; + + if (!CheckIF || Interface->ai_state == INTERFACE_UP) { + + Interface->ai_qlen++; + NdisSend(&Status, Interface->ai_handle, Packet); + + if (Status != NDIS_STATUS_PENDING) { + PacketDone = TRUE; + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + if (Status == NDIS_STATUS_SUCCESS) + Interface->ai_outoctets += Packet->Private.TotalLength; + else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + } + } else { + PacketDone = TRUE; + Status = NDIS_STATUS_ADAPTER_NOT_READY; + } + + if (PacketDone) { + NdisUnchainBufferAtFront(Packet, &Buffer); + FreeARPBuffer(Interface, Buffer); + NdisFreePacket(Packet); + } + return Status; +} + +//* SendARPRequest - Send an ARP packet +// +// Called when we need to ARP an IP address, or respond to a request. We'll send out +// the packet, and the receiving routines will process the response. +// +// Entry: Interface - Interface to send the request on. +// Destination - The IP address to be ARPed. +// Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL +// SrcAddr - NULL if we're sending from ourselves, the value +// to use otherwise. +// CheckIF - Flag passed through to SendARPPacket(). +// +// Returns: Status of attempt to send ARP request. +// +NDIS_STATUS +SendARPRequest(ARPInterface *Interface, IPAddr Destination, uchar Type, + uchar *SrcAddr, uint CheckIF) +{ + uchar *MHeader; // Pointer to media header. + PNDIS_BUFFER Buffer; // NDIS buffer descriptor. + uchar MHeaderSize; // Size of media header. + uchar *MAddr; // Pointer to media address structure. + uint SAddrOffset; // Offset into media address of source address. + uchar SRFlag = 0; // Source routing flag. + uchar SNAPLength = 0; + uchar *SNAPAddr; // Address of SNAP header. + PNDIS_PACKET Packet; // Packet for sending. + NDIS_STATUS Status; + ushort HWType; + IPAddr Src; + CTELockHandle Handle; + ARPIPAddr *Addr; + + // First, get a source address we can use. + CTEGetLock(&Interface->ai_lock, &Handle); + Addr = &Interface->ai_ipaddr; + Src = NULL_IP_ADDR; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + // + // This is a valid address. See if it is the same as the + // target address - i.e. arp'ing for ourselves. If it is, + // we want to use that as our source address. + // + if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) { + Src = Addr->aia_addr; + break; + } + + // See if the target is on this subnet. + if (IP_ADDR_EQUAL( + Addr->aia_addr & Addr->aia_mask, + Destination & Addr->aia_mask + )) + { + // + // See if we've already found a suitable candidate on the + // same subnet. If we haven't, we'll use this one. + // + if (!IP_ADDR_EQUAL( + Addr->aia_addr & Addr->aia_mask, + Src & Addr->aia_mask + )) + { + Src = Addr->aia_addr; + } + } + else { + // He's not on our subnet. If we haven't already found a valid + // address save this one in case we don't find a match for the + // subnet. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + Src = Addr->aia_addr; + } + } + } + + Addr = Addr->aia_next; + + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + + // If we didn't find a source address, give up. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) + return NDIS_STATUS_SUCCESS; + + NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); + if (Status != NDIS_STATUS_SUCCESS) { + Interface->ai_outdiscards++; + return Status; + } + + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; + (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; + + // Figure out what type of media this is, and do the appropriate thing. + switch (Interface->ai_media) { + case NdisMedium802_3: + MHeaderSize = ARP_MAX_MEDIA_ENET; + MAddr = ENetBcst; + if (Interface->ai_snapsize == 0) { + SNAPAddr = (uchar *)NULL; + HWType = net_short(ARP_HW_ENET); + } else { + SNAPLength = sizeof(SNAPHeader); + SNAPAddr = ARPSNAP; + HWType = net_short(ARP_HW_802); + } + + SAddrOffset = offsetof(struct ENetHeader, eh_saddr); + break; + case NdisMedium802_5: + // Token ring. We have logic for dealing with the second transmit + // of an arp request. + MAddr = TRBcst; + SAddrOffset = offsetof(struct TRHeader, tr_saddr); + SNAPLength = sizeof(SNAPHeader); + SNAPAddr = ARPSNAP; + MHeaderSize = sizeof(TRHeader); + HWType = net_short(ARP_HW_802); + if (Type == ARP_RESOLVING_GLOBAL) { + MHeaderSize += sizeof(RC); + SRFlag = TR_RII; + } + break; + case NdisMediumFddi: + MHeaderSize = sizeof(FDDIHeader); + MAddr = FDDIBcst; + SNAPAddr = ARPSNAP; + SNAPLength = sizeof(SNAPHeader); + SAddrOffset = offsetof(struct FDDIHeader, fh_saddr); + HWType = net_short(ARP_HW_ENET); + break; + case NdisMediumArcnet878_2: + MHeaderSize = ARP_MAX_MEDIA_ARC; + MAddr = ARCBcst; + SNAPAddr = (uchar *)NULL; + SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr); + HWType = net_short(ARP_HW_ARCNET); + break; + default: + DEBUGCHK; + Interface->ai_outerrors++; + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + + + if ((Buffer = GetARPBuffer(Interface, &MHeader, + (uchar)(sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER)NULL) { + NdisFreePacket(Packet); + Interface->ai_outdiscards++; + return NDIS_STATUS_RESOURCES; + } + + if (Interface->ai_media == NdisMediumArcnet878_2) + NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT; + + // Copy broadcast address into packet. + CTEMemCopy(MHeader, MAddr, MHeaderSize); + // Fill in source address. + if (SrcAddr == NULL) { + SrcAddr = Interface->ai_addr; + } + + if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) { + ENetHeader *Hdr = (ENetHeader *)MHeader; + + // Using SNAP on ethernet. Adjust the etype to a length. + Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); + } + + CTEMemCopy(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen); + if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) { + // Turn on source routing. + MHeader[SAddrOffset] |= SRFlag; + MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii; + } + // Copy in SNAP header, if any. + CTEMemCopy(&MHeader[MHeaderSize], SNAPAddr, SNAPLength); + + // Media header is filled in. Now do ARP packet itself. + NdisChainBufferAtFront(Packet, Buffer); + return SendARPPacket(Interface, Packet,(ARPHeader *)&MHeader[MHeaderSize + SNAPLength], + net_short(ARP_REQUEST), (uchar *)NULL, SrcAddr, Destination, Src, + HWType, CheckIF); +} + +//* SendARPReply - Reply to an ARP request. +// +// Called by our receive packet handler when we need to reply. We build a packet +// and buffer and call SendARPPacket to send it. +// +// Entry: Interface - Pointer to interface to reply on. +// Destination - IPAddress to reply to. +// Src - Source address to reply from. +// HWAddress - Hardware address to reply to. +// SourceRoute - Source Routing information, if any. +// SourceRouteSize - Size in bytes of soure routing. +// UseSNAP - Whether or not to use SNAP for this reply. +// +// Returns: Nothing. +// +void +SendARPReply(ARPInterface *Interface, IPAddr Destination, IPAddr Src, uchar *HWAddress, + RC UNALIGNED *SourceRoute, uint SourceRouteSize, uint UseSNAP) +{ + PNDIS_PACKET Packet; // Buffer and packet to be used. + PNDIS_BUFFER Buffer; + uchar *Header; // Pointer to media header. + NDIS_STATUS Status; + uchar Size = 0; // Size of media header buffer. + ushort HWType; + ENetHeader *EH; + FDDIHeader *FH; + ARCNetHeader *AH; + TRHeader *TRH; + + // Allocate a packet for this. + NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); + if (Status != NDIS_STATUS_SUCCESS) { + Interface->ai_outdiscards++; + return; + } + + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; + (Interface->ai_outpcount[AI_UCAST_INDEX])++; + + Size = Interface->ai_hdrsize; + + if (UseSNAP) + Size += Interface->ai_snapsize; + + if (Interface->ai_media == NdisMedium802_5) + Size += SourceRouteSize; + + if ((Buffer = GetARPBuffer(Interface, &Header, (uchar)(Size + sizeof(ARPHeader)))) == + (PNDIS_BUFFER)NULL) { + Interface->ai_outdiscards++; + NdisFreePacket(Packet); + return; + } + + // Decide how to build the header based on the media type. + switch (Interface->ai_media) { + case NdisMedium802_3: + EH = (ENetHeader *)Header; + CTEMemCopy(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + if (!UseSNAP) { + EH->eh_type = net_short(ARP_ETYPE_ARP); + HWType = net_short(ARP_HW_ENET); + } else { + // Using SNAP on ethernet. + EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_802); + CTEMemCopy(Header + sizeof(ENetHeader), ARPSNAP, + sizeof(SNAPHeader)); + } + break; + case NdisMedium802_5: + TRH = (TRHeader *)Header; + TRH->tr_ac = ARP_AC; + TRH->tr_fc = ARP_FC; + CTEMemCopy(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + if (SourceRouteSize) {// If we have source route info, deal with + // it. + CTEMemCopy(Header + sizeof(TRHeader), SourceRoute, + SourceRouteSize); + // Convert to directed response. + ((RC *)&Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK; + + ((RC *)&Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR; + TRH->tr_saddr[0] |= TR_RII; + } + CTEMemCopy(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP, + sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_802); + break; + case NdisMediumFddi: + FH = (FDDIHeader *)Header; + FH->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + CTEMemCopy(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_ENET); + break; + case NdisMediumArcnet878_2: + AH = (ARCNetHeader *)Header; + AH->ah_saddr = Interface->ai_addr[0]; + AH->ah_daddr = *HWAddress; + AH->ah_prot = ARP_ARCPROT_ARP; + NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT; + HWType = net_short(ARP_HW_ARCNET); + break; + default: + DEBUGCHK; + Interface->ai_outerrors++; + FreeARPBuffer(Interface, Buffer); + NdisFreePacket(Packet); + return; + } + + NdisChainBufferAtFront(Packet, Buffer); + SendARPPacket(Interface, Packet,(ARPHeader *)(Header + Size), net_short(ARP_RESPONSE), + HWAddress, NULL, Destination, Src, HWType, TRUE); +} + + +//* ARPRemoveRCE - Remove an RCE from the ATE list. +// +// This funtion removes a specified RCE from a given ATE. It assumes the ate_lock +// is held by the caller. +// +// Entry: ATE - ATE from which RCE is to be removed. +// RCE - RCE to be removed. +// +// Returns: Nothing +// +void +ARPRemoveRCE(ARPTableEntry *ATE, RouteCacheEntry *RCE) +{ + ARPContext *CurrentAC; // Current ARP Context being checked. +#ifdef DEBUG + uint Found = FALSE; +#endif + + CurrentAC = (ARPContext *)(((char *)&ATE->ate_rce) - + offsetof(struct ARPContext, ac_next)); + + while (CurrentAC->ac_next != (RouteCacheEntry *)NULL) + if (CurrentAC->ac_next == RCE) { + ARPContext *DummyAC = (ARPContext *)RCE->rce_context; + CurrentAC->ac_next = DummyAC->ac_next; + DummyAC->ac_ate = (ARPTableEntry *)NULL; +#ifdef DEBUG + Found = TRUE; +#endif + break; + } + else + CurrentAC = (ARPContext *)CurrentAC->ac_next->rce_context; + + CTEAssert(Found); +} +//* ARPLookup - Look up an entry in the ARP table. +// +// Called to look up an entry in an interface's ARP table. If we find it, we'll +// lock the entry and return a pointer to it, otherwise we return NULL. We +// assume that the caller has the ARP table locked when we are called. +// +// The ARP table entry is structured as a hash table of pointers to +// ARPTableEntrys.After hashing on the IP address, a linear search is done to +// lookup the entry. +// +// If we find the entry, we lock it for the caller. If we don't find +// the entry, we leave the ARP table locked so that the caller may atomically +// insert a new entry without worrying about a duplicate being inserted between +// the time the table was checked and the time the caller went to insert the +// entry. +// +// Entry: Interface - The interface to be searched upon. +// Address - The IP address we're looking up. +// Handle - Pointer to lock handle to be used to lock entry. +// +// Returns: Pointer to ARPTableEntry if found, or NULL if not. +// +ARPTableEntry * +ARPLookup(ARPInterface *Interface, IPAddr Address, CTELockHandle *Handle) +{ + int i = ARP_HASH(Address); // Index into hash table. + ARPTableEntry *Current; // Current ARP Table entry being + // examined. + + Current = (*Interface->ai_ARPTbl)[i]; + + while (Current != (ARPTableEntry *)NULL) { + CTEGetLock(&Current->ate_lock, Handle); + if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match. + return Current; + } + CTEFreeLock(&Current->ate_lock, *Handle); + Current = Current->ate_next; + } + // If we got here, we didn't find the entry. Leave the table locked and + // return the handle. + return (ARPTableEntry *)NULL; +} + +//* IsBCastOnIF- See it an address is a broadcast address on an interface. +// +// Called to see if a particular address is a broadcast address on an +// interface. We'll check the global, net, and subnet broadcasts. We assume +// the caller holds the lock on the interface. +// +// Entry: Interface - Interface to check. +// Addr - Address to check. +// +// Returns: TRUE if it it a broadcast, FALSE otherwise. +// +uint +IsBCastOnIF(ARPInterface *Interface, IPAddr Addr) +{ + IPAddr BCast; + IPMask Mask; + ARPIPAddr *ARPAddr; + IPAddr LocalAddr; + + // First get the interface broadcast address. + BCast = Interface->ai_bcast; + + // First check for global broadcast. + if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr)) + return TRUE; + + // Now walk the local addresses, and check for net/subnet bcast on each + // one. + ARPAddr = &Interface->ai_ipaddr; + do { + // See if this one is valid. + LocalAddr = ARPAddr->aia_addr; + if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { + // He's valid. + Mask = ARPAddr->aia_mask; + + // First check for subnet bcast. + if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) + return TRUE; + + // Now check all nets broadcast. + Mask = IPNetMask(LocalAddr); + if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) + return TRUE; + } + + ARPAddr = ARPAddr->aia_next; + + } while (ARPAddr != NULL); + + // If we're here, it's not a broadcast. + return FALSE; + +} + + +//* ARPSendBCast - See if this is a bcast or mcast frame, and send it. +// +// Called when we have a packet to send and we want to see if it's a broadcast +// or multicast frame on this interface. We'll search the local addresses and +// see if we can determine if it is. If it is, we'll send it here. Otherwise +// we return FALSE, and the caller will try to resolve the address. +// +// Entry: Interface - A pointer to an AI structure. +// Dest - Destination of datagram. +// Packet - Packet to be sent. +// Status - Place to return status of send attempt. +// +// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise. +// +uint +ARPSendBCast(ARPInterface *Interface, IPAddr Dest, PNDIS_PACKET Packet, + PNDIS_STATUS Status) +{ + uint IsBCast; + CTELockHandle Handle; + PNDIS_BUFFER ARPBuffer; // ARP Header buffer. + uchar *BufAddr; // Address of NDIS buffer + NDIS_STATUS MyStatus; + ENetHeader *Hdr; + FDDIHeader *FHdr; + TRHeader *TRHdr; + SNAPHeader UNALIGNED *SNAPPtr; + RC UNALIGNED *RCPtr; + ARCNetHeader *AHdr; + uint DataLength; + + // Get the lock, and see if it's a broadcast. + CTEGetLock(&Interface->ai_lock, &Handle); + IsBCast = IsBCastOnIF(Interface, Dest); + CTEFreeLock(&Interface->ai_lock, Handle); + + if (IsBCast) { + if (Interface->ai_state == INTERFACE_UP) { + uchar Size; + + Size = Interface->ai_hdrsize + Interface->ai_snapsize; + + if (Interface->ai_media == NdisMedium802_5) + Size += sizeof(RC); + + ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size); + if (ARPBuffer != NULL) { + uint UNALIGNED *Temp; + + // Got the buffer we need. + switch (Interface->ai_media) { + + case NdisMedium802_3: + + Hdr = (ENetHeader *)BufAddr; + if (!CLASSD_ADDR(Dest)) + CTEMemCopy(Hdr, ENetBcst, ARP_802_ADDR_LENGTH); + else { + CTEMemCopy(Hdr, ENetMcst, ARP_802_ADDR_LENGTH); + Temp = (uint UNALIGNED *)&Hdr->eh_daddr[2]; + *Temp |= (Dest & ARP_MCAST_MASK); + } + + CTEMemCopy(Hdr->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + if (Interface->ai_snapsize == 0) { + // No snap on this interface, so just use ETypr. + Hdr->eh_type = net_short(ARP_ETYPE_IP); + } else { + ushort ShortDataLength; + + // We're using SNAP. Find the size of the packet. + NdisQueryPacket(Packet, NULL, NULL, NULL, + &DataLength); + ShortDataLength = (ushort)(DataLength + + sizeof(SNAPHeader)); + Hdr->eh_type = net_short(ShortDataLength); + SNAPPtr = (SNAPHeader UNALIGNED *) + (BufAddr + sizeof(ENetHeader)); + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + } + + break; + + case NdisMedium802_5: + + // This is token ring. We'll have to screw around with + // source routing. + + // BUGBUG Need to support 'real' TR functional address + // for multicast - see RFC 1469. + + TRHdr = (TRHeader *)BufAddr; + + CTEMemCopy(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr)); + CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + if (sIPAlwaysSourceRoute) + { + TRHdr->tr_saddr[0] |= TR_RII; + + RCPtr = (RC UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader)); + RCPtr->rc_blen = TrRii | RC_LEN; + RCPtr->rc_dlf = RC_BCST_LEN; + SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)RCPtr + sizeof(RC)); + } + else + { + + // + // Adjust the size of the buffer to account for the + // fact that we don't have the RC field. + // + NdisAdjustBufferLength(ARPBuffer,(Size - sizeof(RC))); + SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader)); + } + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + + break; + case NdisMediumFddi: + FHdr = (FDDIHeader *)BufAddr; + + if (!CLASSD_ADDR(Dest)) + CTEMemCopy(FHdr, FDDIBcst, + offsetof(FDDIHeader, fh_saddr)); + else { + CTEMemCopy(FHdr, FDDIMcst, + offsetof(FDDIHeader, fh_saddr)); + Temp = (uint UNALIGNED *)&FHdr->fh_daddr[2]; + *Temp |= (Dest & ARP_MCAST_MASK); + } + + CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + SNAPPtr = (SNAPHeader UNALIGNED *)(BufAddr + sizeof(FDDIHeader)); + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + + break; + case NdisMediumArcnet878_2: + AHdr = (ARCNetHeader *)BufAddr; + AHdr->ah_saddr = Interface->ai_addr[0]; + AHdr->ah_daddr = 0; + AHdr->ah_prot = ARP_ARCPROT_IP; + break; + default: + DEBUGCHK; + *Status = NDIS_STATUS_UNSUPPORTED_MEDIA; + FreeARPBuffer(Interface, ARPBuffer); + return FALSE; + + } + + (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; + Interface->ai_qlen++; + NdisChainBufferAtFront(Packet, ARPBuffer); + NdisSend(&MyStatus, Interface->ai_handle, Packet); + + *Status = MyStatus; + + if (MyStatus != NDIS_STATUS_PENDING) { // Send finished + // immediately. + if (MyStatus == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (MyStatus == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + NdisUnchainBufferAtFront(Packet, &ARPBuffer); + FreeARPBuffer(Interface, ARPBuffer); + } + } else + *Status = NDIS_STATUS_RESOURCES; + } else + *Status = NDIS_STATUS_ADAPTER_NOT_READY; + + return TRUE; + + } else + return FALSE; +} + +//* ARPSendData - Send a frame to a specific destination address. +// +// Called when we need to send a frame to a particular address, after the +// ATE has been looked up. We take in an ATE and a packet, validate the state of the +// ATE, and either send or ARP for the address if it's not done resolving. We assume +// the lock on the ATE is held where we're called, and we'll free it before returning. +// +// Entry: Interface - A pointer to the AI structure. +// Packet - A pointer to the BufDesc chain to be sent. +// entry - A pointer to the ATE for the send. +// lhandle - Pointer to a lock handle for the ATE. +// +// Returns: Status of the transmit - success, an error, or pending. +// +NDIS_STATUS +ARPSendData(ARPInterface *Interface, PNDIS_PACKET Packet, ARPTableEntry *entry, + CTELockHandle lhandle) +{ + PNDIS_BUFFER ARPBuffer; // ARP Header buffer. + uchar *BufAddr; // Address of NDIS buffer + NDIS_STATUS Status; // Status of send. + + if (Interface->ai_state == INTERFACE_UP) { + + if (entry->ate_state == ARP_GOOD) { // Entry is valid + + entry->ate_useticks = ArpCacheLife; + if ((ARPBuffer = GetARPBuffer(Interface, &BufAddr, + entry->ate_addrlength)) != (PNDIS_BUFFER)NULL) { + + // Everything's in good shape, copy header and send packet. + + (Interface->ai_outpcount[AI_UCAST_INDEX])++; + Interface->ai_qlen++; + CTEMemCopy(BufAddr, entry->ate_addr, entry->ate_addrlength); + + // If we're on Ethernet, see if we're using SNAP here. + if (Interface->ai_media == NdisMedium802_3 && + entry->ate_addrlength != sizeof(ENetHeader)) { + ENetHeader *Header; + uint DataSize; + ushort ShortDataSize; + + // We're apparently using SNAP on Ethernet. Query the + // packet for the size, and set the length properly. + NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize); + ShortDataSize = (ushort)(DataSize + sizeof(SNAPHeader)); + Header = (ENetHeader *)BufAddr; + Header->eh_type = net_short(ShortDataSize); + } + + CTEFreeLock(&entry->ate_lock, lhandle); + NdisChainBufferAtFront(Packet, ARPBuffer); + NdisSend(&Status, Interface->ai_handle, Packet); + if (Status != NDIS_STATUS_PENDING) { // Send finished + // immediately. + if (Status == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + NdisUnchainBufferAtFront(Packet, &ARPBuffer); + FreeARPBuffer(Interface, ARPBuffer); + } + return Status; + } else { // No buffer, free lock and return. + CTEFreeLock(&entry->ate_lock, lhandle); + Interface->ai_outdiscards++; + return NDIS_STATUS_RESOURCES; + } + } + // The IP addresses match, but the state of the ARP entry indicates + // it's not valid. If the address is marked as resolving, we'll replace + // the current cached packet with this one. If it's been more than + // ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send + // another one now. + if (entry->ate_state <= ARP_RESOLVING) { + PNDIS_PACKET OldPacket = entry->ate_packet; + ulong Now = CTESystemUpTime(); + entry->ate_packet = Packet; + if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) { + IPAddr Dest = entry->ate_dest; + + entry->ate_valid = Now; + entry->ate_state = ARP_RESOLVING_GLOBAL; // We're done this + // at least once. + CTEFreeLock(&entry->ate_lock, lhandle); + SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, + NULL, TRUE); // Send a request. + } else + CTEFreeLock(&entry->ate_lock, lhandle); + + if (OldPacket) + IPSendComplete(Interface->ai_context, OldPacket, + NDIS_STATUS_SUCCESS); + + return NDIS_STATUS_PENDING; + } else { + DEBUGCHK; + CTEFreeLock(&entry->ate_lock, lhandle); + Interface->ai_outerrors++; + return NDIS_STATUS_INVALID_PACKET; + } + } else { + // Adapter is down. Just return the error. + CTEFreeLock(&entry->ate_lock, lhandle); + return NDIS_STATUS_ADAPTER_NOT_READY; + } +} + +//* CreateARPTableEntry - Create a new entry in the ARP table. +// +// A function to put an entry into the ARP table. We allocate memory if we +// need to. +// +// The first thing to do is get the lock on the ARP table, and see if the +// entry already exists. If it does, we're done. Otherwise we need to allocate +// memory and create a new entry. +// +// Entry: Interface - Interface for ARP table. +// Destination - Destination address to be mapped. +// Handle - Pointer to lock handle for entry. +// +// Returns: Pointer to newly created entry. +// +ARPTableEntry * +CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination, + CTELockHandle *Handle) +{ + ARPTableEntry *NewEntry, *Entry; + CTELockHandle TableHandle; + int i = ARP_HASH(Destination); + int Size; + + // First look for it, and if we don't find it return try to create one. + CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); + if ((Entry = ARPLookup(Interface, Destination, Handle)) != + (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, *Handle); + *Handle = TableHandle; + return Entry; + } + + // Allocate memory for the entry. If we can't, fail the request. + Size = sizeof(ARPTableEntry) - 1 + + (Interface->ai_media == NdisMedium802_5 ? + ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize + + Interface->ai_snapsize)); + + if ((NewEntry = CTEAllocMem(Size)) == (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); + return (ARPTableEntry *)NULL; + } + + CTEMemSet(NewEntry, 0, Size); + NewEntry->ate_dest = Destination; + if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute) + NewEntry->ate_state = ARP_RESOLVING_GLOBAL; + else + NewEntry->ate_state = ARP_RESOLVING_LOCAL; + + NewEntry->ate_rce = NULL; + + NewEntry->ate_valid = CTESystemUpTime(); + NewEntry->ate_useticks = ArpCacheLife; + CTEInitLock(&NewEntry->ate_lock); + + // Entry does not exist. Insert the new entry into the table at the appropriate spot. + // ARPLookup returns with the table lock held if it fails. + NewEntry->ate_next = (*Interface->ai_ARPTbl)[i]; + (*Interface->ai_ARPTbl)[i] = NewEntry; + Interface->ai_count++; + CTEGetLock(&NewEntry->ate_lock, Handle); + CTEFreeLock(&Interface->ai_ARPTblLock, *Handle); + *Handle = TableHandle; + return NewEntry; +} + + +//* ARPTransmit - Send a frame. +// +// The main ARP transmit routine, called by the upper layer. This routine +// takes as input a buf desc chain, RCE, and size. We validate the cached +// information in the RCE. If it is valid, we use it to send the frame. Otherwise +// we do a table lookup. If we find it in the table, we'll update the RCE and continue. +// Otherwise we'll queue the packet and start an ARP resolution. +// +// Entry: Context - A pointer to the AI structure. +// Packet - A pointer to the BufDesc chain to be sent. +// Destination - IP address of destination we're trying to reach, +// RCE - A pointer to an RCE which may have cached information. +// +// Returns: Status of the transmit - success, an error, or pending. +// +NDIS_STATUS +ARPTransmit(void *Context, PNDIS_PACKET Packet, IPAddr Destination, + RouteCacheEntry *RCE) +{ + ARPInterface *ai = (ARPInterface *)Context; // Set up as AI pointer. + ARPContext *ac; // ARP context pointer. + ARPTableEntry *entry; // Pointer to ARP tbl. entry + CTELockHandle lhandle; // Lock handle + CTELockHandle tlhandle; // Lock handle for ARP table. + NDIS_STATUS Status; + + CTEGetLock(&ai->ai_ARPTblLock, &tlhandle); + if (RCE != (RouteCacheEntry *)NULL) { // Have a valid RCE. + ac = (ARPContext *)RCE->rce_context; // Get pointer to context + entry = ac->ac_ate; + if (entry != (ARPTableEntry *)NULL) { // Have a valid ATE. + CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure + if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) { + CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle); + return ARPSendData(ai, Packet, entry, tlhandle); // Send the data + } + + // We have an RCE that identifies the wrong ATE. We'll free it from + // this list and try and find an ATE that is valid. + ARPRemoveRCE(entry, RCE); + CTEFreeLock(&entry->ate_lock, lhandle); + // Fall through to 'no valid entry' code. + } + } + + // Here we have no valid ATE, either because the RCE is NULL or the ATE + // specified by the RCE was invalid. We'll try and find one in the table. If + // we find one, we'll fill in this RCE and send the packet. Otherwise we'll + // try to create one. At this point we hold the lock on the ARP table. + + if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *)NULL) { + // Found a matching entry. ARPLookup returns with the ATE lock held. + if (RCE != (RouteCacheEntry *)NULL) { + ac->ac_next = entry->ate_rce; // Fill in context for next time. + entry->ate_rce = RCE; + ac->ac_ate = entry; + } + CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle); + return ARPSendData(ai, Packet, entry, tlhandle); + } + + // No valid entry in the ARP table. First we'll see if we're sending to a + // broadcast address or multicast address. If not, we'll try to create + // an entry in the table and get an ARP resolution going. ARPLookup returns + // with the table lock held when it fails, we'll free it here. + CTEFreeLock(&ai->ai_ARPTblLock, tlhandle); + + if (ARPSendBCast(ai, Destination, Packet, &Status)) + return Status; + + entry = CreateARPTableEntry(ai, Destination, &lhandle); + if (entry != NULL) { + if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry. + + // Someone else could have raced in and created the entry between + // the time we free the lock and the time we called + // CreateARPTableEntry(). We check this by looking at the packet + // on the entry. If there is no old packet we'll ARP. If there is, + // we'll call ARPSendData to figure out what to do. + + if (entry->ate_packet == NULL) { + entry->ate_packet = Packet; + CTEFreeLock(&entry->ate_lock, lhandle); + SendARPRequest(ai, Destination, entry->ate_state, NULL, TRUE); + // We don't know the state of the entry - we've freed the lock + // and yielded, and it could conceivably have timed out by now, + // or SendARPRequest could have failed, etc. We could take the + // lock, check the status from SendARPRequest, see if it's + // still the same packet, and then make a decision on the + // return value, but it's easiest just to return pending. If + // SendARPRequest failed, the entry will time out anyway. + return NDIS_STATUS_PENDING; + } else + return ARPSendData(ai, Packet, entry, lhandle); + + } else { + if (entry->ate_state == ARP_GOOD) // Yow! A valid entry. + return ARPSendData(ai, Packet, entry, lhandle); + else { // An invalid entry! + CTEFreeLock(&entry->ate_lock, lhandle); + return NDIS_STATUS_RESOURCES; + } + } + } else // Couldn't create an entry. + return NDIS_STATUS_RESOURCES; + +} + +//* RemoveARPTableEntry - Delete an entry from the ARP table. +// +// This is a simple utility function to delete an entry from the ATP table. We +// assume locks are held on both the table and the entry. +// +// Entry: Previous - The entry immediately before the one to be deleted. +// Entry - The entry to be deleted. +// +// Returns: Nothing. +// +void +RemoveARPTableEntry(ARPTableEntry *Previous, ARPTableEntry *Entry) +{ + RouteCacheEntry *RCE; // Pointer to route cache entry + ARPContext *AC; + + RCE = Entry->ate_rce; + // Loop through and invalidate all RCEs on this ATE. + while (RCE != (RouteCacheEntry *)NULL) { + AC = (ARPContext *)RCE->rce_context; + AC->ac_ate = (ARPTableEntry *)NULL; + RCE = AC->ac_next; + } + + // Splice this guy out of the list. + Previous->ate_next = Entry->ate_next; +} + +//* ARPXferData - Transfer data on behalf on an upper later protocol. +// +// This routine is called by the upper layer when it needs to transfer data +// from an NDIS driver. We just map his call down. +// +// Entry: Context - Context value we gave to IP (really a pointer to an AI). +// MACContext - Context value MAC gave us on a receive. +// MyOffset - Packet offset we gave to the protocol earlier. +// ByteOffset - Byte offset into packet protocol wants transferred. +// BytesWanted - Number of bytes to transfer. +// Packet - Pointer to packet to be used for transferring. +// Transferred - Pointer to where to return bytes transferred. +// +// Returns: NDIS_STATUS of command. +// +NDIS_STATUS +ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset, + uint BytesWanted, PNDIS_PACKET Packet, uint *Transferred) +{ + ARPInterface *Interface = (ARPInterface *)Context; + NDIS_STATUS Status; + + NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset+MyOffset, + BytesWanted, Packet, Transferred); + + return Status; +} + + +//* ARPClose - Close an adapter. +// +// Called by IP when it wants to close an adapter, presumably due to an error condition. +// We'll close the adapter, but we won't free any memory. +// +// Entry: Context - Context value we gave him earlier. +// +// Returns: Nothing. +// +void +ARPClose(void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; + NDIS_STATUS Status; + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + Interface->ai_operstate = IF_STATUS_DOWN; + Interface->ai_state = INTERFACE_DOWN; + CTEInitBlockStruc(&Interface->ai_block); + + CTEGetLock(&Interface->ai_lock, &LockHandle); + if (Interface->ai_handle != (NDIS_HANDLE)NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + NdisCloseAdapter(&Status, Handle); + + if (Status == NDIS_STATUS_PENDING) + Status = CTEBlock(&Interface->ai_block); + + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + } +} + +//* ARPInvalidate - Notification that an RCE is invalid. +// +// Called by IP when an RCE is closed or otherwise invalidated. We look up the ATE for +// the specified RCE, and then remove the RCE from the ATE list. +// +// Entry: Context - Context value we gave him earlier. +// RCE - RCE to be invalidated +// +// Returns: Nothing. +// +void +ARPInvalidate(void *Context, RouteCacheEntry *RCE) +{ + ARPInterface *Interface = (ARPInterface *)Context; + ARPTableEntry *ATE; + CTELockHandle Handle, ATEHandle; + ARPContext *AC = (ARPContext *)RCE->rce_context; + + CTEGetLock(&Interface->ai_ARPTblLock, &Handle); + if ((ATE = AC->ac_ate) == (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE. + return; + } + + CTEGetLock(&ATE->ate_lock, &ATEHandle); + ARPRemoveRCE(ATE, RCE); + CTEMemSet(RCE->rce_context, 0, RCE_CONTEXT_SIZE); + CTEFreeLock(&Interface->ai_ARPTblLock, ATEHandle); + CTEFreeLock(&ATE->ate_lock, Handle); + +} + +//* ARPSetMCastList - Set the multicast address list for the adapter. +// +// Called to try and set the multicast reception list for the adapter. +// We allocate a buffer big enough to hold the new address list, and format +// the address list into the buffer. Then we submit the NDIS request to set +// the list. If we can't set the list because the multicast address list is +// full we'll put the card into all multicast mode. +// +// Input: Interface - Interface on which to set list. +// +// Returns: NDIS_STATUS of attempt. +// +NDIS_STATUS +ARPSetMCastList(ARPInterface *Interface) +{ + CTELockHandle Handle; + uchar *MCastBuffer, *CurrentPtr; + uint MCastSize; + NDIS_STATUS Status; + uint i; + ARPMCastAddr *AddrPtr; + IPAddr UNALIGNED *Temp; + + CTEGetLock(&Interface->ai_lock, &Handle); + MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH; + if (MCastSize != 0) + MCastBuffer = CTEAllocMem(MCastSize); + else + MCastBuffer = NULL; + + if (MCastBuffer != NULL || MCastSize == 0) { + // Got the buffer. Loop through, building the list. + AddrPtr = Interface->ai_mcast; + + CurrentPtr = MCastBuffer; + + for (i = 0; i < Interface->ai_mcastcnt; i++) { + CTEAssert(AddrPtr != NULL); + + if (Interface->ai_media == NdisMedium802_3) { + + CTEMemCopy(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH); + Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2); + *Temp |= AddrPtr->ama_addr; + } else + if (Interface->ai_media == NdisMediumFddi) { + CTEMemCopy(CurrentPtr, ((FDDIHeader *)FDDIMcst)->fh_daddr, + ARP_802_ADDR_LENGTH); + Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2); + *Temp |= AddrPtr->ama_addr; + } else + DEBUGCHK; + + CurrentPtr += ARP_802_ADDR_LENGTH; + AddrPtr = AddrPtr->ama_next; + } + + CTEFreeLock(&Interface->ai_lock, Handle); + + // We're built the list. Now give it to the driver to handle. + if (Interface->ai_media == NdisMedium802_3) { + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL); + } else + if (Interface->ai_media == NdisMediumFddi) { + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL); + } else + DEBUGCHK; + + if (MCastBuffer != NULL) { + CTEFreeMem(MCastBuffer); + } + + if (Status == NDIS_STATUS_MULTICAST_FULL) { + // Multicast list is full. Try to set the filter to all multicasts. + Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST; + + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, + sizeof(uint), NULL); + } + + } else { + CTEFreeLock(&Interface->ai_lock, Handle); + Status = NDIS_STATUS_RESOURCES; + } + + return Status; + +} + +//* ARPFindMCast - Find a multicast address structure on our list. +// +// Called as a utility to find a multicast address structure. If we find +// it, we return a pointer to it and it's predecessor. Otherwise we return +// NULL. We assume the caller holds the lock on the interface already. +// +// Input: Interface - Interface to search. +// Addr - Addr to find. +// Prev - Where to return previous pointer. +// +// Returns: Pointer if we find one, NULL otherwise. +// +ARPMCastAddr * +ARPFindMCast(ARPInterface *Interface, IPAddr Addr, ARPMCastAddr **Prev) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + + PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next); + AddrPtr = PrevPtr->ama_next; + while (AddrPtr != NULL) { + if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr)) + break; + else { + PrevPtr = AddrPtr; + AddrPtr = PrevPtr->ama_next; + } + } + + *Prev = PrevPtr; + return AddrPtr; +} + +//* ARPDelMCast - Delete a multicast address. +// +// Called when we want to delete a multicast address. We look for a matching +// (masked) address. If we find one, we'll dec. the reference count and if +// it goes to 0 we'll pull him from the list and reset the multicast list. +// +// Input: Interface - Interface on which to act. +// Addr - Address to be deleted. +// +// Returns: TRUE if it worked, FALSE otherwise. +// +uint +ARPDelMCast(ARPInterface *Interface, IPAddr Addr) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + CTELockHandle Handle; + uint Status = TRUE; + + // When we support TR (RFC 1469) fully we'll need to change this. + if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media == + NdisMediumFddi) { + // This is an interface that supports mcast addresses. + Addr &= ARP_MCAST_MASK; + + CTEGetLock(&Interface->ai_lock, &Handle); + AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found one. Dec. his refcnt, and if it's 0 delete him. + (AddrPtr->ama_refcnt)--; + if (AddrPtr->ama_refcnt == 0) { + // He's done. + PrevPtr->ama_next = AddrPtr->ama_next; + (Interface->ai_mcastcnt)--; + CTEFreeLock(&Interface->ai_lock, Handle); + CTEFreeMem(AddrPtr); + ARPSetMCastList(Interface); + CTEGetLock(&Interface->ai_lock, &Handle); + } + } else + Status = FALSE; + + CTEFreeLock(&Interface->ai_lock, Handle); + } + + return Status; +} +//* ARPAddMCast - Add a multicast address. +// +// Called when we want to start receiving a multicast address. We'll mask +// the address and look it up in our address list. If we find it, we'll just +// bump the reference count. Otherwise we'll try to create one and put him +// on the list. In that case we'll need to set the multicast address list for +// the adapter. +// +// Input: Interface - Interface to set on. +// Addr - Address to set. +// +// Returns: TRUE if we succeed, FALSE if we fail. +// +uint +ARPAddMCast(ARPInterface *Interface, IPAddr Addr) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + CTELockHandle Handle; + uint Status = TRUE; + + + if (Interface->ai_state != INTERFACE_UP) + return FALSE; + + // BUGBUG Currently we don't do anything with token ring, since we send + // all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to + // fix this. + if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media == + NdisMediumFddi) { + // This is an interface that supports mcast addresses. + Addr &= ARP_MCAST_MASK; + + CTEGetLock(&Interface->ai_lock, &Handle); + AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found one, just bump refcnt. + (AddrPtr->ama_refcnt)++; + } else { + // Didn't find one. Allocate space for one, link him in, and + // try to set the list. + AddrPtr = CTEAllocMem(sizeof(ARPMCastAddr)); + if (AddrPtr != NULL) { + // Got one. Link him in. + AddrPtr->ama_addr = Addr; + AddrPtr->ama_refcnt = 1; + AddrPtr->ama_next = Interface->ai_mcast; + Interface->ai_mcast = AddrPtr; + (Interface->ai_mcastcnt)++; + CTEFreeLock(&Interface->ai_lock, Handle); + + // Now try to set the list. + if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) { + // Couldn't set the list. Call the delete routine to delete + // the address we just tried to set. + Status = ARPDelMCast(Interface, Addr); + if (!Status) + DEBUGCHK; + Status = FALSE; + } + CTEGetLock(&Interface->ai_lock, &Handle); + } else + Status = FALSE; // Couldn't get memory. + } + + // We've done out best. Free the lock and return. + CTEFreeLock(&Interface->ai_lock, Handle); + } + + return Status; +} + +//* ARPAddAddr - Add an address to the ARP table. +// +// This routine is called by IP to add an address as a local address, or +// or specify the broadcast address for this interface. +// +// Entry: Context - Context we gave IP earlier (really an ARPInterface pointer) +// Type - Type of address (local, p-arp, multicast, or +// broadcast). +// Address - Broadcast IP address to be added. +// Mask - Mask for address. +// +// Returns: 0 if we failed, non-zero otherwise +// +uint +ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle; + + if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) { + // Not a local address, must be broadcast or multicast. + + if (Type == LLIP_ADDR_BCAST) { + Interface->ai_bcast = Address; + return TRUE; + } else + if (Type == LLIP_ADDR_MCAST) { + return ARPAddMCast(Interface, Address); + } else + return FALSE; + } else { // This is a local address. + CTEGetLock(&Interface->ai_lock, &Handle); + if (Type != LLIP_ADDR_PARP) { + uint RetStatus = FALSE; + uint ArpForSelf = FALSE; + + if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) { + Interface->ai_ipaddr.aia_addr = Address; + Interface->ai_ipaddr.aia_mask = Mask; + Interface->ai_ipaddr.aia_age = ARPADDR_NEW_LOCAL; + if (Interface->ai_state == INTERFACE_UP) { + Interface->ai_ipaddr.aia_context = Context2; + ArpForSelf = TRUE; + } else { + Interface->ai_ipaddr.aia_context = NULL; + } + RetStatus = TRUE; + } else { + ARPIPAddr *NewAddr; + + NewAddr = CTEAllocMem(sizeof(ARPIPAddr)); + if (NewAddr != (ARPIPAddr *)NULL) { + NewAddr->aia_addr = Address; + NewAddr->aia_mask = Mask; + NewAddr->aia_age = ARPADDR_NEW_LOCAL; + NewAddr->aia_next = Interface->ai_ipaddr.aia_next; + if (Interface->ai_state == INTERFACE_UP) { + NewAddr->aia_context = Context2; + ArpForSelf = TRUE; + } else { + NewAddr->aia_context = NULL; + } + + Interface->ai_ipaddr.aia_next = NewAddr; + RetStatus = TRUE; + } + } + + CTEFreeLock(&Interface->ai_lock, Handle); + // ARP for the address we've added, to see it it already exists. + if (RetStatus == TRUE && ArpForSelf == TRUE) { + SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + return IP_PENDING; + } + + return RetStatus; + } else if (Type == LLIP_ADDR_PARP) { + ARPPArpAddr *NewPArp; + + // He's adding a proxy arp address. + NewPArp = CTEAllocMem(sizeof(ARPPArpAddr)); + if (NewPArp != NULL) { + NewPArp->apa_addr = Address; + NewPArp->apa_mask = Mask; + NewPArp->apa_next = Interface->ai_parpaddr; + Interface->ai_parpaddr = NewPArp; + Interface->ai_parpcount++; + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + CTEFreeLock(&Interface->ai_lock, Handle); + return FALSE; + } + } + +} + +//* ARPDeleteAddr - Delete a local or proxy address. +// +// Called to delete a local or proxy address. +// +// Entry: Context - An ARPInterface pointer. +// Type - Type of address (local or p-arp). +// Address - IP address to be deleted. +// Mask - Mask for address. Used only for deleting proxy-ARP +// entries. +// +// Returns: 0 if we failed, non-zero otherwise +// +uint +ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle; + ARPIPAddr *DelAddr, *PrevAddr; + ARPPArpAddr *DelPAddr, *PrevPAddr; + + if (Type == LLIP_ADDR_LOCAL) { + CTEGetLock(&Interface->ai_lock, &Handle); + + if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) { + Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR; + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } else { + PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next); + DelAddr = PrevAddr->aia_next; + while (DelAddr != NULL) + if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address)) + break; + else { + PrevAddr = DelAddr; + DelAddr = DelAddr->aia_next; + } + + if (DelAddr != NULL) { + PrevAddr->aia_next = DelAddr->aia_next; + CTEFreeMem(DelAddr); + } + CTEFreeLock(&Interface->ai_lock, Handle); + return (DelAddr != NULL); + } + } else if (Type == LLIP_ADDR_PARP) { + CTEGetLock(&Interface->ai_lock, &Handle); + PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next); + DelPAddr = PrevPAddr->apa_next; + while (DelPAddr != NULL) + if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) && + DelPAddr->apa_mask == Mask) + break; + else { + PrevPAddr = DelPAddr; + DelPAddr = DelPAddr->apa_next; + } + + if (DelPAddr != NULL) { + PrevPAddr->apa_next = DelPAddr->apa_next; + Interface->ai_parpcount--; + CTEFreeMem(DelPAddr); + } + CTEFreeLock(&Interface->ai_lock, Handle); + return (DelPAddr != NULL); + } else + if (Type == LLIP_ADDR_MCAST) + return ARPDelMCast(Interface, Address); + else + return FALSE; +} + +//* ARPTimeout - ARP timeout routine. +// +// This is the timeout routine that is called periodically. We scan the ARP table, looking +// for invalid entries that can be removed. +// +// Entry: Timer - Pointer to the timer that just fired. +// Context - Pointer to the interface to be timed out. +// +// Returns: Nothing. +// +void +ARPTimeout(CTEEvent *Timer, void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; // Our interface. + ARPTable *Table; + ARPTableEntry *Current, *Previous; + int i; // Index variable. + ulong Now = CTESystemUpTime(), ValidTime; + CTELockHandle tblhandle, entryhandle; + uchar Deleted; + PNDIS_PACKET PList = (PNDIS_PACKET)NULL; + ARPIPAddr *Addr; + + // Walk down the list of addresses, decrementing the age. + CTEGetLock(&Interface->ai_lock, &tblhandle); + + Addr = &Interface->ai_ipaddr; + + do { + if (Addr->aia_age != ARPADDR_OLD_LOCAL) { + (Addr->aia_age)--; + if (Addr->aia_age == ARPADDR_OLD_LOCAL) { + if (Addr->aia_context != NULL) { + SetAddrControl *SAC; + SetAddrRtn Rtn; + + SAC = (SetAddrControl *)Addr->aia_context; + Rtn = (SetAddrRtn)SAC->sac_rtn; + CTEFreeLock(&Interface->ai_lock, tblhandle); + (*Rtn)(SAC, IP_SUCCESS); + CTEGetLock(&Interface->ai_lock, &tblhandle); + Addr->aia_context = NULL; + } + } else { + CTEFreeLock(&Interface->ai_lock, tblhandle); + SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Interface->ai_lock, &tblhandle); + } + } + + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, tblhandle); + + // Loop through the ARP table for this interface, and delete stale entries. + CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); + Table = Interface->ai_ARPTbl; + for (i = 0; i < ARP_TABLE_SIZE;i++) { + Previous = (ARPTableEntry *)((uchar *)&((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next)); + Current = (*Table)[i]; + while (Current != (ARPTableEntry *)NULL) { + CTEGetLock(&Current->ate_lock, &entryhandle); + Deleted = 0; + + if (Current->ate_state == ARP_GOOD) { + // + // The ARP entry is valid for ARP_VALID_TIMEOUT by default. + // If a cache life greater than ARP_VALID_TIMEOUT has been + // configured, we'll make the entry valid for that time. + // + ValidTime = ArpCacheLife * ARP_TIMER_TIME; + + if (ValidTime < ARP_MIN_VALID_TIMEOUT) { + ValidTime = ARP_MIN_VALID_TIMEOUT; + } + } + else { + ValidTime = ARP_RESOLVE_TIMEOUT; + } + + if (Current->ate_valid != ALWAYS_VALID && + ( ((Now - Current->ate_valid) > ValidTime) || + (Current->ate_state == ARP_GOOD && + !(--(Current->ate_useticks))))) { + + if (Current->ate_state != ARP_RESOLVING_LOCAL) { + // Really need to delete this guy. + PNDIS_PACKET Packet = Current->ate_packet; + + if (Packet != (PNDIS_PACKET)NULL) { + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link + = PList; + PList = Packet; + } + RemoveARPTableEntry(Previous, Current); + Interface->ai_count--; + Deleted = 1; + } else { + IPAddr Dest = Current->ate_dest; + // This entry is only resoving locally, presumably this is + // token ring. We'll need to transmit a 'global' resolution + // now. + CTEAssert(Interface->ai_media == NdisMedium802_5); + + Now = CTESystemUpTime(); + Current->ate_valid = Now; + Current->ate_state = ARP_RESOLVING_GLOBAL; + CTEFreeLock(&Current->ate_lock, entryhandle); + CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); + // Send a global request. + SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); + + // Since we've freed the locks, we need to start over from + // the start of this chain. + Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]), + ate_next); + Current = (*Table)[i]; + continue; + + } + } + + // If we deleted the entry, leave the previous pointer alone, advance the + // current pointer, and free the memory. Otherwise move both pointers forward. + // We can free the entry lock now because the next pointers are protected by + // the table lock, and we've removed it from the list so nobody else should + // find it anyway. + CTEFreeLock(&Current->ate_lock, entryhandle); + if (Deleted) { + ARPTableEntry *Temp = Current; + Current = Current->ate_next; + CTEFreeMem(Temp); + } else { + Previous = Current; + Current = Current->ate_next; + } + } + } + + CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); + + while (PList != (PNDIS_PACKET)NULL) { + PNDIS_PACKET Packet = PList; + + PList = ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link; + IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); + } + + CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface); +} + +//* IsLocalAddr - Return info. about local status of address. +// +// Called when we need info. about whether or not a particular address is +// local. We return info about whether or not it is, and if it is how old +// it is. +// +// Entry: Interface - Pointer to interface structure to be searched. +// Address - Address in question. +// +// Returns: ARPADDR_*, for how old it is. +// +// +uint +IsLocalAddr(ARPInterface *Interface, IPAddr Address) +{ + CTELockHandle Handle; + ARPIPAddr *CurrentAddr; + uint Age; + + CTEGetLock(&Interface->ai_lock, &Handle); + + CurrentAddr = &Interface->ai_ipaddr; + Age = ARPADDR_NOT_LOCAL; + + do { + if (CurrentAddr->aia_addr == Address) { + Age = CurrentAddr->aia_age; + break; + } + CurrentAddr = CurrentAddr->aia_next; + } while (CurrentAddr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + return Age; +} + +//* ARPLocalAddr - Determine whether or not a given address if local. +// +// This routine is called when we receive an incoming packet and need to determine whether +// or not it's local. We look up the provided address on the specified interface. +// +// Entry: Interface - Pointer to interface structure to be searched. +// Address - Address in question. +// +// Returns: TRUE if it is a local address, FALSE if it's not. +// +uchar +ARPLocalAddr(ARPInterface *Interface, IPAddr Address) +{ + CTELockHandle Handle; + ARPPArpAddr *CurrentPArp; + IPMask Mask, NetMask; + IPAddr MatchAddress; + + // First, see if he's a local (not-proxy) address. + if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL) + return TRUE; + + CTEGetLock(&Interface->ai_lock, &Handle); + + // Didn't find him in out local address list. See if he exists on our + // proxy ARP list. + for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL; + CurrentPArp = CurrentPArp->apa_next) { + // See if this guy matches. + Mask = CurrentPArp->apa_mask; + MatchAddress = Address & Mask; + if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) { + // He matches. We need to make a few more checks to make sure + // we don't reply to a broadcast address. + if (Mask == HOST_MASK) { + // We're matching the whole address, so it's OK. + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + // See if the non-mask part it all-zeros. Since the mask presumably + // covers a subnet, this trick will prevent us from replying to + // a zero host part. + if (IP_ADDR_EQUAL(MatchAddress, Address)) + continue; + + // See if the host part is all ones. + if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask))) + continue; + + // If the mask we were given is not the net mask for this address, + // we'll need to repeat the above checks. + NetMask = IPNetMask(Address); + if (NetMask != Mask) { + + MatchAddress = Address & NetMask; + if (IP_ADDR_EQUAL(MatchAddress, Address)) + continue; + + if (IP_ADDR_EQUAL(Address, MatchAddress | + (IP_LOCAL_BCST & ~NetMask))) + continue; + } + + // If we get to this point we've passed all the tests, so it's + // local. + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + } + + CTEFreeLock(&Interface->ai_lock, Handle); + return FALSE; + +} + + +#ifdef VXD + + +#ifndef CHICAGO +extern void DisplayPopup(uchar *Msg); +uchar CMsg1[] = "The system has detected a conflict for IP address "; +uchar CMsg2[] = " with the system having hardware address "; +uchar CMsg3[] = ". The local interface has been disabled"; + +uchar CMsg[sizeof(CMsg1) - 1 + sizeof(CMsg2) - 1 + sizeof(CMsg3) - 1 + + ((sizeof(IPAddr) * 4) - 1) + ((ARP_802_ADDR_LENGTH * 3) - 1) + + 1 + 1]; +#else +extern void NotifyConflictProc(CTEEvent *Event, void *Context); +extern void DisplayConflictPopup(uchar *IPAddr, uchar *HWAddr, uint Shutoff); +#endif // CHICAGO + +#endif // NT + +//* NotifyConflictProc - Notify the user of an address conflict. +// +// Called when we need to notify the user of an address conflict. The +// exact mechanism is system dependent, but generally involves a popup. +// +// Input: Event - Event that fired. +// Context - Pointer to ARPNotifyStructure. +// +// Returns: Nothing. +// +void +#ifndef CHICAGO +NotifyConflictProc(CTEEvent *Event, void *Context) +#else +DisplayConflictProc(void *Context) +#endif +{ +#ifdef VXD + uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; + uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + uint i; + uint IPAddrCharCount; + ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context; + +#ifndef CHICAGO + uint TotalSize; + + CTEMemCopy(CMsg, CMsg1, sizeof(CMsg1) - 1); + TotalSize = sizeof(CMsg1) - 1; +#endif + + // Convert the IP address into a string. + IPAddrCharCount = 0; + + for (i = 0; i < sizeof(IPAddr); i++) { + uint CurrentByte; + + CurrentByte = NotifyStruct->ans_addr & 0xff; + if (CurrentByte > 99) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0'; + CurrentByte %= 100; + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } else if (CurrentByte > 9) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } + + IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0'; + if (i != (sizeof(IPAddr) - 1)) + IPAddrBuffer[IPAddrCharCount++] = '.'; + + NotifyStruct->ans_addr >>= 8; + } + +#ifndef CHICAGO + CTEMemCopy(&CMsg[TotalSize], IPAddrBuffer, IPAddrCharCount); + TotalSize += IPAddrCharCount; + + CTEMemCopy(&CMsg[TotalSize], CMsg2, sizeof(CMsg2) - 1); + TotalSize += sizeof(CMsg2) - 1; +#else + IPAddrBuffer[IPAddrCharCount] = '\0'; +#endif + + for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { + uchar CurrentHalf; + + CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; + HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; + HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + if (i != (NotifyStruct->ans_hwaddrlen - 1)) + HWAddrBuffer[(i*3)+2] = ':'; + } + +#ifndef CHICAGO + CTEMemCopy(&CMsg[TotalSize], HWAddrBuffer, + (NotifyStruct->ans_hwaddrlen * 3) - 1); + TotalSize += (NotifyStruct->ans_hwaddrlen * 3) - 1; + + if (NotifyStruct->ans_shutoff) { + CTEMemCopy(&CMsg[TotalSize], CMsg3, sizeof(CMsg3) - 1); + TotalSize += sizeof(CMsg3) - 1; + } + + CMsg[TotalSize] = '.'; + CMsg[TotalSize+1] = '\0'; + + DisplayPopup(CMsg); +#else + HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0'; + DisplayConflictPopup(IPAddrBuffer, HWAddrBuffer, NotifyStruct->ans_shutoff); + CTEFreeMem(NotifyStruct); +#endif + +#else // VXD + + ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context; + PWCHAR stringList[2]; + uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; + uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)]; + WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + uint i; + uint IPAddrCharCount; + UNICODE_STRING unicodeString; + ANSI_STRING ansiString; + + + PAGED_CODE(); + + // + // Convert the IP address into a string. + // + IPAddrCharCount = 0; + + for (i = 0; i < sizeof(IPAddr); i++) { + uint CurrentByte; + + CurrentByte = NotifyStruct->ans_addr & 0xff; + if (CurrentByte > 99) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0'; + CurrentByte %= 100; + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } else if (CurrentByte > 9) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } + + IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0'; + if (i != (sizeof(IPAddr) - 1)) + IPAddrBuffer[IPAddrCharCount++] = '.'; + + NotifyStruct->ans_addr >>= 8; + } + + // + // Convert the hardware address into a string. + // + for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { + uchar CurrentHalf; + + CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; + HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; + HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + if (i != (NotifyStruct->ans_hwaddrlen - 1)) + HWAddrBuffer[(i*3)+2] = ':'; + } + + // + // Unicode the strings. + // + *unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL; + + unicodeString.Buffer = unicodeIPAddrBuffer; + unicodeString.Length = 0; + unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1); + ansiString.Buffer = IPAddrBuffer; + ansiString.Length = IPAddrCharCount; + ansiString.MaximumLength = IPAddrCharCount; + + RtlAnsiStringToUnicodeString( + &unicodeString, + &ansiString, + FALSE + ); + + stringList[0] = unicodeIPAddrBuffer; + + unicodeString.Buffer = unicodeHWAddrBuffer; + unicodeString.Length = 0; + unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3); + ansiString.Buffer = HWAddrBuffer; + ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1; + ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3; + + RtlAnsiStringToUnicodeString( + &unicodeString, + &ansiString, + FALSE + ); + + stringList[1] = unicodeHWAddrBuffer; + + // + // Kick off a popup and log an event. + // + if (NotifyStruct->ans_shutoff) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADDRESS_CONFLICT1, + 0, + 2, + stringList, + 0, + NULL + ); + + IoRaiseInformationalHardError( + STATUS_IP_ADDRESS_CONFLICT1, + NULL, + NULL + ); + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADDRESS_CONFLICT2, + 0, + 2, + stringList, + 0, + NULL + ); + + IoRaiseInformationalHardError( + STATUS_IP_ADDRESS_CONFLICT2, + NULL, + NULL + ); + } + + CTEFreeMem(NotifyStruct); + +#endif // VXD + + return; +} + + +//* HandleARPPacket - Process an incoming ARP packet. +// +// This is the main routine to process an incoming ARP packet. We look at all ARP frames, +// and update our cache entry for the source address if one exists. Else, if we are the +// target we create an entry if one doesn't exist. Finally, we'll handle the opcode, +// responding if this is a request or sending pending packets if this is a response. +// +// Entry: Interface - Pointer to interface structure for this adapter. +// Header - Pointer to header buffer. +// HeaderSize - Size of header buffer. +// ARPHdr - ARP packet header. +// ARPHdrSize - Size of ARP header. +// ProtOffset - Offset into original data field of arp header. +// Will be non-zero if we're using SNAP. +// +// Returns: An NDIS_STATUS value to be returned to the NDIS driver. +// +NDIS_STATUS +HandleARPPacket(ARPInterface *Interface, void *Header, uint HeaderSize, + ARPHeader UNALIGNED *ARPHdr, uint ARPHdrSize, uint ProtOffset) +{ + ARPTableEntry *Entry; // Entry in ARP table + CTELockHandle LHandle, TableHandle; + RC UNALIGNED *SourceRoute = (RC UNALIGNED *)NULL; // Pointer to Source Route info, if any. + uint SourceRouteSize = 0; + ulong Now = CTESystemUpTime(); + uchar LocalAddr; + uint LocalAddrAge; + uchar *SHAddr, *DHAddr; + IPAddr UNALIGNED *SPAddr, *DPAddr; + ENetHeader *ENetHdr; + TRHeader *TRHdr; + FDDIHeader *FHdr; + ARCNetHeader *AHdr; + ushort MaxMTU; + uint UseSNAP; + SetAddrControl *SAC; + SetAddrRtn Rtn = NULL; + + // We examine all ARP frames. If we find the source address in the ARP table, we'll + // update the hardware address and set the state to valid. If we're the + // target and he's not in the table, we'll add him. Otherwise if we're the + // target and this is a response we'll send any pending packets to him. + if (Interface->ai_media != NdisMediumArcnet878_2) { + if (ARPHdrSize < sizeof(ARPHeader)) + return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. + + if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) && + ARPHdr->ah_hw != net_short(ARP_HW_802)) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type + + if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. + + if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0) + UseSNAP = FALSE; + else + UseSNAP = (ProtOffset != 0); + + // Figure out SR size on TR. + if (Interface->ai_media == NdisMedium802_5) { + // Check for source route information. SR is present if the header + // size is greater than the standard TR header size. If the SR is + // only an RC field, we ignore it because it came from the same + // ring which is the same as no SR. + + if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) { + SourceRouteSize = HeaderSize - sizeof(TRHeader); + SourceRoute = (RC UNALIGNED *)((uchar *)Header + + sizeof(TRHeader)); + } + } + + SHAddr = ARPHdr->ah_shaddr; + SPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_spaddr; + DHAddr = ARPHdr->ah_dhaddr; + DPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_dpaddr; + + } else { + if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT)) + return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. + + if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET)) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type + + if (ARPHdr->ah_hlen != 1) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. + + UseSNAP = FALSE; + SHAddr = ARPHdr->ah_shaddr; + SPAddr = (IPAddr UNALIGNED *)(SHAddr + 1); + DHAddr = (uchar *)SPAddr + sizeof(IPAddr); + DPAddr = (IPAddr UNALIGNED *)(DHAddr + 1); + } + + if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP)) + return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type. + + if (ARPHdr->ah_plen != sizeof(IPAddr)) + return NDIS_STATUS_NOT_RECOGNIZED; + + if (IP_ADDR_EQUAL(*SPAddr, NULL_IP_ADDR)) + return NDIS_STATUS_NOT_RECOGNIZED; + + // First, let's see if we have an address conflict. + LocalAddrAge = IsLocalAddr(Interface, *SPAddr); + if (LocalAddrAge != ARPADDR_NOT_LOCAL) { + // The source IP address is one of ours. See if the source h/w address + // is ours also. + if (ARPHdr->ah_hlen != Interface->ai_addrlen || + CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) { + + uint Shutoff; + ARPNotifyStruct *NotifyStruct; + + // This isn't from us; we must have an address conflict somewhere. + // We always log an error about this. If what triggered this is a + // response and the address in conflict is young, we'll turn off + // the interface. + if (LocalAddrAge != ARPADDR_OLD_LOCAL && + ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) { + // Send an arp request with the owner's address to reset the + // caches. + + CTEGetLock(&Interface->ai_lock, &LHandle); + Interface->ai_state = INTERFACE_DOWN; + Interface->ai_adminstate = IF_STATUS_DOWN; + if (Interface->ai_ipaddr.aia_context != NULL) { + SAC = (SetAddrControl *)Interface->ai_ipaddr.aia_context; + Rtn = (SetAddrRtn)SAC->sac_rtn; + Interface->ai_ipaddr.aia_context = NULL; + } + CTEFreeLock(&Interface->ai_lock, LHandle); + + SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL, + SHAddr, FALSE); // Send a request. + + Shutoff = TRUE; + + if (Rtn != NULL) { + // + // this is a dhcp adapter. report the conflict to + // CompleteIPSetNTEAddrRequest so IOCTL_IP_SET_ADDRESS will + // be completed + // + + (*Rtn)(SAC, IP_GENERAL_FAILURE); + + // + // don't display a warning dialog in this case - DHCP will + // alert the user + // + + goto no_dialog; + } + } else { + if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) && + (IsLocalAddr(Interface, *DPAddr) != ARPADDR_NOT_LOCAL)) { + // Send a response. + SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr, + SourceRoute, SourceRouteSize, UseSNAP); + } + Shutoff = FALSE; + } + + // Now allocate a structure, and schedule an event to notify + // the user. + NotifyStruct = CTEAllocMem(offsetof(ARPNotifyStruct, ans_hwaddr) + + ARPHdr->ah_hlen); + if (NotifyStruct != NULL) { + NotifyStruct->ans_addr = *SPAddr; + NotifyStruct->ans_shutoff = Shutoff; + NotifyStruct->ans_hwaddrlen = (uint)ARPHdr->ah_hlen; + CTEMemCopy(NotifyStruct->ans_hwaddr, SHAddr, + ARPHdr->ah_hlen); + CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc); + CTEScheduleEvent(&NotifyStruct->ans_event, NotifyStruct); + } + + + no_dialog: + ; + + } + return NDIS_STATUS_NOT_RECOGNIZED; + } + + CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); + MaxMTU = Interface->ai_mtu; + + LocalAddr = ARPLocalAddr(Interface, *DPAddr); + Entry = ARPLookup(Interface, *SPAddr, &LHandle); + if (Entry == (ARPTableEntry *)NULL) { + + // Didn't find him, create one if it's for us. The call to ARPLookup + // returned with the ARPTblLock held, so we need to free it. + + CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); + if (LocalAddr) + Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle); + else + return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us. + } else { + CTEFreeLock(&Interface->ai_ARPTblLock, LHandle); + LHandle = TableHandle; + } + + // At this point, entry should be valid, and we hold the lock on the entry + // in LHandle. + + if (Entry != (ARPTableEntry *)NULL) { + PNDIS_PACKET Packet; // Packet to be sent. + + // If the entry is already static, we'll want to leave it as static. + if (Entry->ate_valid == ALWAYS_VALID) + Now = ALWAYS_VALID; + + // OK, we have an entry to use, and hold the lock on it. Fill in the + // required fields. + switch (Interface->ai_media) { + + case NdisMedium802_3: + + // This is an Ethernet. + ENetHdr = (ENetHeader *)Entry->ate_addr; + + CTEMemCopy(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(ENetHdr->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + ENetHdr->eh_type = net_short(ARP_ETYPE_IP); + + // If we're using SNAP on this entry, copy in the SNAP header. + if (UseSNAP) { + CTEMemCopy(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP, + sizeof(SNAPHeader)); + Entry->ate_addrlength = (uchar)(sizeof(ENetHeader) + + sizeof(SNAPHeader)); + *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } else + Entry->ate_addrlength = sizeof(ENetHeader); + + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + + case NdisMedium802_5: + + // This is TR. + // For token ring we have to deal with source routing. There's + // a special case to handle multiple responses for an all-routes + // request - if the entry is currently good and we knew it was + // valid recently, we won't update the entry. + + + if (Entry->ate_state != ARP_GOOD || + (Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) { + + TRHdr = (TRHeader *)Entry->ate_addr; + + // We need to update a TR entry. + TRHdr->tr_ac = ARP_AC; + TRHdr->tr_fc = ARP_FC; + CTEMemCopy(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + if (SourceRoute != (RC UNALIGNED *)NULL) { + uchar MaxIFieldBits; + + // We have source routing information. + CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)], + SourceRoute, SourceRouteSize); + MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >> + LF_BIT_SHIFT; + MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS); + MaxMTU = IFieldSize[MaxIFieldBits]; + + // The new MTU we've computed is the max I-field size, + // which doesn't include source routing info but + // does include SNAP info. Subtract off the SNAP size. + MaxMTU -= sizeof(SNAPHeader); + + TRHdr->tr_saddr[0] |= TR_RII; + (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^= + RC_DIR; + // Make sure it's non-broadcast. + (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_blen &= + RC_LENMASK; + + } + CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)+SourceRouteSize], + ARPSNAP, sizeof(SNAPHeader)); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; + Entry->ate_addrlength = (uchar)(sizeof(TRHeader) + + SourceRouteSize + sizeof(SNAPHeader)); + *(ushort *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } + break; + case NdisMediumFddi: + FHdr = (FDDIHeader *)Entry->ate_addr; + + FHdr->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + CTEMemCopy(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP, + sizeof(SNAPHeader)); + Entry->ate_addrlength = (uchar)(sizeof(FDDIHeader) + + sizeof(SNAPHeader)); + *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + case NdisMediumArcnet878_2: + AHdr = (ARCNetHeader *)Entry->ate_addr; + AHdr->ah_saddr = Interface->ai_addr[0]; + AHdr->ah_daddr = *SHAddr; + AHdr->ah_prot = ARP_ARCPROT_IP; + Entry->ate_addrlength = sizeof(ARCNetHeader); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + default: + DEBUGCHK; + break; + } + + // At this point we've updated the entry, and we still hold the lock + // on it. If we have a packet that was pending to be sent, send it now. + // Otherwise just free the lock. + + Packet = Entry->ate_packet; + + if (Packet != NULL) { + // We have a packet to send. + CTEAssert(Entry->ate_state == ARP_GOOD); + + Entry->ate_packet = NULL; + + if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING) + IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); + } else + CTEFreeLock(&Entry->ate_lock, LHandle); + } + + // See if the MTU is less than our local one. This should only happen + // in the case of token ring source routing. + if (MaxMTU < Interface->ai_mtu) { + LLIPAddrMTUChange LAM; + + LAM.lam_mtu = MaxMTU; + LAM.lam_addr = *SPAddr; + + // It is less. Notify IP. + CTEAssert(Interface->ai_media == NdisMedium802_5); + IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE, + &LAM, sizeof(LLIPAddrMTUChange)); + + } + + // At this point we've updated the entry (if we had one), and we've free + // all locks. If it's for a local address and it's a request, reply to + // it. + if (LocalAddr) { // It's for us. + if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) { + // It's a request, and we need to respond. + SendARPReply(Interface, *SPAddr, *DPAddr, + SHAddr, SourceRoute, SourceRouteSize, UseSNAP); + } + } + + + return NDIS_STATUS_SUCCESS; +} + + +//* InitAdapter - Initialize an adapter. +// +// Called when an adapter is open to finish initialization. We set +// up our lookahead size and packet filter, and we're ready to go. +// +// Entry: +// adapter - Pointer to an adapter structure for the adapter to be +// initialized. +// +// Exit: Nothing +// +void +InitAdapter(ARPInterface *Adapter) +{ + NDIS_STATUS Status; + CTELockHandle Handle; + ARPIPAddr *Addr, *OldAddr; + + if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead), NULL)) + != NDIS_STATUS_SUCCESS) { + Adapter->ai_state = INTERFACE_DOWN; + return; + } + + if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint), + NULL)) == NDIS_STATUS_SUCCESS) { + Adapter->ai_adminstate = IF_STATUS_UP; + Adapter->ai_operstate = IF_STATUS_UP; + Adapter->ai_state = INTERFACE_UP; + // Now walk through any addresses we have, and ARP for them. + CTEGetLock(&Adapter->ai_lock, &Handle); + OldAddr = NULL; + Addr = &Adapter->ai_ipaddr; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + IPAddr Address = Addr->aia_addr; + + Addr->aia_age = ARPADDR_NEW_LOCAL; + CTEFreeLock(&Adapter->ai_lock, Handle); + OldAddr = Addr; + SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Adapter->ai_lock, &Handle); + } + Addr = &Adapter->ai_ipaddr; + while (Addr != OldAddr && Addr != NULL) + Addr = Addr->aia_next; + if (Addr != NULL) + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Adapter->ai_lock, Handle); + + } else + Adapter->ai_state = INTERFACE_DOWN; + +} + +//** ARPOAComplete - ARP Open adapter complete handler. +// +// This routine is called by the NDIS driver when an open adapter +// call completes. Presumably somebody is blocked waiting for this, so +// we'll wake him up now. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// ErrorStatus - Final error status. +// +// Exit: Nothing. +// +void NDIS_API +ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus) +{ + ARPInterface *ai = (ARPInterface *)Handle; // For compiler. + + CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status. + +} + +//** ARPCAComplete - ARP close adapter complete handler. +// +// This routine is called by the NDIS driver when a close adapter +// call completes. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) +{ + ARPInterface *ai = (ARPInterface *)Handle; // For compiler. + + CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status. + +} + +//** ARPSendComplete - ARP send complete handler. +// +// This routine is called by the NDIS driver when a send completes. +// This is a pretty time critical operation, we need to get through here +// quickly. We'll strip our buffer off and put it back, and call the upper +// later send complete handler. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Packet - A pointer to the packet that was sent. +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status) +{ + ARPInterface *Interface = (ARPInterface *)Handle; + PacketContext *PC = (PacketContext *)Packet->ProtocolReserved; + PNDIS_BUFFER Buffer; + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + + if (Status == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + // Get first buffer on packet. + NdisUnchainBufferAtFront(Packet, &Buffer); +#ifdef DEBUG + if (Buffer == (PNDIS_BUFFER)NULL) // No buffer! + DEBUGCHK; +#endif + + FreeARPBuffer(Interface, Buffer); // Free it up. + if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one. + IPSendComplete(Interface->ai_context, Packet, Status); + return; + } + + // This packet belongs to us, so free it. + NdisFreePacket(Packet); + +} + +//** ARPTDComplete - ARP transfer data complete handler. +// +// This routine is called by the NDIS driver when a transfer data +// call completes. Since we never transfer data ourselves, this must be +// from the upper layer. We'll just call his routine and let him deal +// with it. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Packet - A pointer to the packet used for the TD. +// Status - Final status of command. +// BytesCopied - Count of bytes copied. +// +// Exit: Nothing. +// +void NDIS_API +ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status, + uint BytesCopied) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + IPTDComplete(ai->ai_context, Packet, Status, BytesCopied); + +} + +//** ARPResetComplete - ARP reset complete handler. +// +// This routine is called by the NDIS driver when a reset completes. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) +{ +} + +//** ARPRequestComplete - ARP request complete handler. +// +// This routine is called by the NDIS driver when a general request +// completes. ARP blocks on all requests, so we'll just wake up +// whoever's blocked on this request. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Request - A pointer to the request that completed. +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST Request, + NDIS_STATUS Status) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + CTESignal(&ai->ai_block, (uint)Status); +} + +//** ARPRcv - ARP receive data handler. +// +// This routine is called when data arrives from the NDIS driver. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Context - NDIS context to be used for TD. +// Header - Pointer to header +// HeaderSize - Size of header +// Data - Pointer to buffer of received data +// Size - Byte count of data in buffer. +// TotalSize - Byte count of total packet size. +// +// Exit: Status indicating whether or not we took the packet. +// +NDIS_STATUS NDIS_API +ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize, + void *Data, uint Size, uint TotalSize) +{ + ARPInterface *Interface = Handle; // Interface for this driver. + ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *)Header; + SNAPHeader UNALIGNED *SNAPHdr; + ushort type; // Protocol type + uint ProtOffset; // Offset in Data to non-media info. + uint NUCast; // TRUE if the frame is not + // a unicast frame. + + if (Interface->ai_state == INTERFACE_UP && + HeaderSize >= (uint)Interface->ai_hdrsize) { + + Interface->ai_inoctets += TotalSize; + + if (Interface->ai_media != NdisMediumArcnet878_2) { + if (Interface->ai_media == NdisMedium802_3 && + (type = net_short(EHdr->eh_type)) >= MIN_ETYPE) + ProtOffset = 0; + else { + SNAPHdr = (SNAPHeader UNALIGNED *)Data; + + if (Size >= sizeof(SNAPHeader) && + SNAPHdr->sh_dsap == SNAP_SAP && + SNAPHdr->sh_ssap == SNAP_SAP && + SNAPHdr->sh_ctl == SNAP_UI) { + type = net_short(SNAPHdr->sh_etype); + ProtOffset = sizeof(SNAPHeader); + } else { + // BUGBUG handle XID/TEST here. + Interface->ai_uknprotos++; + return NDIS_STATUS_NOT_RECOGNIZED; + } + } + } else { + ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *)Header; + + ProtOffset = 0; + if (AH->ah_prot == ARP_ARCPROT_IP) + type = ARP_ETYPE_IP; + else + if (AH->ah_prot == ARP_ARCPROT_ARP) + type = ARP_ETYPE_ARP; + else + type = 0; + } + + NUCast = ((*((uchar UNALIGNED *)EHdr + Interface->ai_bcastoff) & + Interface->ai_bcastmask) == Interface->ai_bcastval) ? + AI_NONUCAST_INDEX : AI_UCAST_INDEX; + + if (type == ARP_ETYPE_IP) { + + (Interface->ai_inpcount[NUCast])++; + IPRcv(Interface->ai_context, (uchar *)Data+ProtOffset, + Size-ProtOffset, TotalSize-ProtOffset, Context, ProtOffset, + NUCast); + return NDIS_STATUS_SUCCESS; + } + else { + if (type == ARP_ETYPE_ARP) { + (Interface->ai_inpcount[NUCast])++; + return HandleARPPacket(Interface, Header, HeaderSize, + (ARPHeader *)((uchar *)Data+ProtOffset), Size-ProtOffset, + ProtOffset); + } else { + Interface->ai_uknprotos++; + return NDIS_STATUS_NOT_RECOGNIZED; + } + } + } else { + // Interface is marked as down. + return NDIS_STATUS_NOT_RECOGNIZED; + } +} + +//** ARPRcvComplete - ARP receive complete handler. +// +// This routine is called by the NDIS driver after some number of +// receives. In some sense, it indicates 'idle time'. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// +// Exit: Nothing. +// +void NDIS_API +ARPRcvComplete(NDIS_HANDLE Handle) +{ + IPRcvComplete(); + +} + + +//** ARPStatus - ARP status handler. +// +// Called by the NDIS driver when some sort of status change occurs. +// We take action depending on the type of status. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// GStatus - General type of status that caused the call. +// Status - Pointer to a buffer of status specific information. +// StatusSize - Size of the status buffer. +// +// Exit: Nothing. +// +void NDIS_API +ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint + StatusSize) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + IPStatus(ai->ai_context, GStatus, Status, StatusSize); + +} + +//** ARPStatusComplete - ARP status complete handler. +// +// A routine called by the NDIS driver so that we can do postprocessing +// after a status event. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// +// Exit: Nothing. +// +void NDIS_API +ARPStatusComplete(NDIS_HANDLE Handle) +{ + +} + +extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus, + NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2); + +extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus, + NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext); +extern void NDIS_API ARPUnloadProtocol(void); + +#ifndef NT +NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = { + NDIS_MAJOR_VERSION, + NDIS_MINOR_VERSION, + 0, + ARPOAComplete, + ARPCAComplete, + ARPSendComplete, + ARPTDComplete, + ARPResetComplete, + ARPRequestComplete, + ARPRcv, + ARPRcvComplete, + ARPStatus, + ARPStatusComplete, +#ifdef CHICAGO + ARPBindAdapter, + ARPUnbindAdapter, + ARPUnloadProtocol, +#endif + { sizeof(TCP_NAME), + sizeof(TCP_NAME), + 0 + } +}; +#else // NT +NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = { + NDIS_MAJOR_VERSION, + NDIS_MINOR_VERSION, + 0, + ARPOAComplete, + ARPCAComplete, + ARPSendComplete, + ARPTDComplete, + ARPResetComplete, + ARPRequestComplete, + ARPRcv, + ARPRcvComplete, + ARPStatus, + ARPStatusComplete, + { sizeof(TCP_NAME), + sizeof(TCP_NAME), + 0 +#ifdef _PNP_POWER + }, + NULL, + ARPBindAdapter, + ARPUnbindAdapter, + NULL +#else + } +#endif +}; +#endif + +//* ARPReadNext - Read the next entry in the ARP table. +// +// Called by the GetInfo code to read the next ATE in the table. We assume +// the context passed in is valid, and the caller has the ARP TableLock. +// +// Input: Context - Pointer to a IPNMEContext. +// Interface - Pointer to interface for table to read on. +// Buffer - Pointer to an IPNetToMediaEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +ARPReadNext(void *Context, ARPInterface *Interface, void *Buffer) +{ + IPNMEContext *NMContext = (IPNMEContext *)Context; + IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *)Buffer; + CTELockHandle Handle; + ARPTableEntry *CurrentATE; + uint i; + ARPTable *Table = Interface->ai_ARPTbl; + uint AddrOffset; + + CurrentATE = NMContext->inc_entry; + + // Fill in the buffer. + CTEGetLock(&CurrentATE->ate_lock, &Handle); + IPNMEntry->inme_index = Interface->ai_index; + IPNMEntry->inme_physaddrlen = Interface->ai_addrlen; + + + switch (Interface->ai_media) { + case NdisMedium802_3: + AddrOffset = 0; + break; + case NdisMedium802_5: + AddrOffset = offsetof(struct TRHeader, tr_daddr); + break; + case NdisMediumFddi: + AddrOffset = offsetof(struct FDDIHeader, fh_daddr); + break; + case NdisMediumArcnet878_2: + AddrOffset = offsetof(struct ARCNetHeader, ah_daddr); + break; + default: + AddrOffset = 0; + break; + } + + CTEMemCopy(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset], + Interface->ai_addrlen); + IPNMEntry->inme_addr = CurrentATE->ate_dest; + + if (CurrentATE->ate_state == ARP_GOOD) + IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ? + INME_TYPE_STATIC : INME_TYPE_DYNAMIC); + else + IPNMEntry->inme_type = INME_TYPE_INVALID; + CTEFreeLock(&CurrentATE->ate_lock, Handle); + + // We've filled it in. Now update the context. + if (CurrentATE->ate_next != NULL) { + NMContext->inc_entry = CurrentATE->ate_next; + return TRUE; + } else { + // The next ATE is NULL. Loop through the ARP Table looking for a new + // one. + i = NMContext->inc_index + 1; + while (i < ARP_TABLE_SIZE) { + if ((*Table)[i] != NULL) { + NMContext->inc_entry = (*Table)[i]; + NMContext->inc_index = i; + return TRUE; + break; + } else + i++; + } + + NMContext->inc_index = 0; + NMContext->inc_entry = NULL; + return FALSE; + } + +} + +//* ARPValidateContext - Validate the context for reading an ARP table. +// +// Called to start reading an ARP table sequentially. We take in +// a context, and if the values are 0 we return information about the +// first route in the table. Otherwise we make sure that the context value +// is valid, and if it is we return TRUE. +// We assume the caller holds the ARPInterface lock. +// +// Input: Context - Pointer to a RouteEntryContext. +// Interface - Pointer to an interface +// Valid - Where to return information about context being +// valid. +// +// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set +// to TRUE if input context is valid +// +uint +ARPValidateContext(void *Context, ARPInterface *Interface, uint *Valid) +{ + IPNMEContext *NMContext = (IPNMEContext *)Context; + uint i; + ARPTableEntry *TargetATE; + ARPTableEntry *CurrentATE; + ARPTable *Table = Interface->ai_ARPTbl; + + i = NMContext->inc_index; + TargetATE = NMContext->inc_entry; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetATE == NULL) { + *Valid = TRUE; + do { + if ((CurrentATE = (*Table)[i]) != NULL) { + break; + } + i++; + } while (i < ARP_TABLE_SIZE); + + if (CurrentATE != NULL) { + NMContext->inc_index = i; + NMContext->inc_entry = CurrentATE; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < ARP_TABLE_SIZE) { + CurrentATE = (*Table)[i]; + while (CurrentATE != NULL) { + if (CurrentATE == TargetATE) { + *Valid = TRUE; + return TRUE; + break; + } else { + CurrentATE = CurrentATE->ate_next; + } + } + + } + + // If we get here, we didn't find the matching ATE. + *Valid = FALSE; + return FALSE; + + } + +} + +#define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr) + +//* ARPQueryInfo - ARP query information handler. +// +// Called to query information about the ARP table or statistics about the +// actual interface. +// +// Input: IFContext - Interface context (pointer to an ARPInterface). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// Context - Pointer to context block. +// +// Returns: Status of attempt to query information. +// +int +ARPQueryInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + ARPInterface *AI = (ARPInterface *)IFContext; + uint Offset = 0; + uint BufferSize = *Size; + CTELockHandle Handle; + uint ContextValid, DataLeft; + uint BytesCopied = 0; + uchar InfoBuff[sizeof(IFEntry)]; + uint Entity; + uint Instance; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) && + (Entity != IF_ENTITY || Instance != AI->ai_ifinst)) { + return TDI_INVALID_REQUEST; + } + + *Size = 0; // In case of an error. + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_id == ENTITY_TYPE_ID) { + // He's trying to see what type we are. + if (BufferSize >= sizeof(uint)) { + *(uint *)&InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP : + IF_MIB; + (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } + + // Might be able to handle this. + if (Entity == AT_ENTITY) { + // It's an address translation object. It could be a MIB object or + // an implementation specific object (the generic objects were handled + // above). + + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + ARPPArpAddr *PArpAddr; + + // It's an implementation specific ID. The only ones we handle + // are the PARP_COUNT_ID and the PARP_ENTRY ID. + + if (ID->toi_id == AT_ARP_PARP_COUNT_ID) { + // He wants to know the count. Just return that to him. + if (BufferSize >= sizeof(uint)) { + + CTEGetLock(&AI->ai_lock, &Handle); + + (void)CopyToNdis(Buffer, (uchar *)&AI->ai_parpcount, + sizeof(uint), &Offset); + + CTEFreeLock(&AI->ai_lock, Handle); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + + if (ID->toi_id != AT_ARP_PARP_ENTRY_ID) + return TDI_INVALID_PARAMETER; + + // It's for Proxy ARP entries. The context should be either NULL + // or a pointer to the next one to be read. + CTEGetLock(&AI->ai_lock, &Handle); + + PArpAddr = *(ARPPArpAddr **)Context; + + if (PArpAddr != NULL) { + ARPPArpAddr *CurrentPARP; + + // Loop through the P-ARP addresses on the interface, and + // see if we can find this one. + CurrentPARP = AI->ai_parpaddr; + while (CurrentPARP != NULL) { + if (CurrentPARP == PArpAddr) + break; + else + CurrentPARP = CurrentPARP->apa_next; + } + + // If we found a match, PARPAddr points to where to begin + // reading. Otherwise, fail the request. + if (CurrentPARP == NULL) { + // Didn't find a match, so fail the request. + CTEFreeLock(&AI->ai_lock, Handle); + return TDI_INVALID_PARAMETER; + } + } else + PArpAddr = AI->ai_parpaddr; + + // PARPAddr points to the next entry to put in the buffer, if + // there is one. + while (PArpAddr != NULL) { + if ((int)(BufferSize - BytesCopied) >= + (int)sizeof(ProxyArpEntry)) { + ProxyArpEntry *TempPArp; + + TempPArp = (ProxyArpEntry *)InfoBuff; + TempPArp->pae_status = PAE_STATUS_VALID; + TempPArp->pae_addr = PArpAddr->apa_addr; + TempPArp->pae_mask = PArpAddr->apa_mask; + BytesCopied += sizeof(ProxyArpEntry); + Buffer = CopyToNdis(Buffer, (uchar *)TempPArp, + sizeof(ProxyArpEntry), &Offset); + PArpAddr = PArpAddr->apa_next; + } else + break; + } + + // We're done copying. Free the lock and return the correct + // status. + CTEFreeLock(&AI->ai_lock, Handle); + *Size = BytesCopied; + **(ARPPArpAddr ***)&Context = PArpAddr; + return (PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW; + } + + if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) { + AddrXlatInfo *AXI; + + // It's for the count. Just return the number of entries in the + // table. + if (BufferSize >= sizeof(AddrXlatInfo)) { + *Size = sizeof(AddrXlatInfo); + AXI = (AddrXlatInfo *)InfoBuff; + AXI->axi_count = AI->ai_count; + AXI->axi_index = AI->ai_index; + (void)CopyToNdis(Buffer, (uchar *)AXI, sizeof(AddrXlatInfo), + &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + + if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) { + // He's trying to read the table. + // Make sure we have a valid context. + CTEGetLock(&AI->ai_ARPTblLock, &Handle); + DataLeft = ARPValidateContext(Context, AI, &ContextValid); + + // If the context is valid, we'll continue trying to read. + if (!ContextValid) { + CTEFreeLock(&AI->ai_ARPTblLock, Handle); + return TDI_INVALID_PARAMETER; + } + + while (DataLeft) { + // The invariant here is that there is data in the table to + // read. We may or may not have room for it. So DataLeft + // is TRUE, and BufferSize - BytesCopied is the room left + // in the buffer. + if ((int)(BufferSize - BytesCopied) >= + (int)sizeof(IPNetToMediaEntry)) { + DataLeft = ARPReadNext(Context, AI, InfoBuff); + BytesCopied += sizeof(IPNetToMediaEntry); + Buffer = CopyToNdis(Buffer, InfoBuff, + sizeof(IPNetToMediaEntry), &Offset); + } else + break; + + } + + *Size = BytesCopied; + + CTEFreeLock(&AI->ai_ARPTblLock, Handle); + return (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + } + + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + // He must be asking for interface level information. See if we support + // what he's asking for. + if (ID->toi_id == IF_MIB_STATS_ID) { + IFEntry *IFE = (IFEntry *)InfoBuff; + + + // He's asking for statistics. Make sure his buffer is at least big + // enough to hold the fixed part. + + if (BufferSize < IFE_FIXED_SIZE) { + return TDI_BUFFER_TOO_SMALL; + } + + // He's got enough to hold the fixed part. Build the IFEntry structure, + // and copy it to his buffer. + IFE->if_index = AI->ai_index; + switch (AI->ai_media) { + case NdisMedium802_3: + IFE->if_type = IF_TYPE_ETHERNET; + break; + case NdisMedium802_5: + IFE->if_type = IF_TYPE_TOKENRING; + break; + case NdisMediumFddi: + IFE->if_type = IF_TYPE_FDDI; + break; + case NdisMediumArcnet878_2: + default: + IFE->if_type = IF_TYPE_OTHER; + break; + } + IFE->if_mtu = AI->ai_mtu; + IFE->if_speed = AI->ai_speed; + IFE->if_physaddrlen = AI->ai_addrlen; + CTEMemCopy(IFE->if_physaddr,AI->ai_addr, AI->ai_addrlen); + IFE->if_adminstatus = (uint)AI->ai_adminstate; + IFE->if_operstatus = (uint)AI->ai_operstate; + IFE->if_lastchange = AI->ai_lastchange; + IFE->if_inoctets = AI->ai_inoctets; + IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX]; + IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX]; + IFE->if_indiscards = AI->ai_indiscards; + IFE->if_inerrors = AI->ai_inerrors; + IFE->if_inunknownprotos = AI->ai_uknprotos; + IFE->if_outoctets = AI->ai_outoctets; + IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX]; + IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX]; + IFE->if_outdiscards = AI->ai_outdiscards; + IFE->if_outerrors = AI->ai_outerrors; + IFE->if_outqlen = AI->ai_qlen; + IFE->if_descrlen = AI->ai_desclen; + Buffer = CopyToNdis(Buffer, (uchar *)IFE, IFE_FIXED_SIZE, &Offset); + + // See if he has room for the descriptor string. + if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) { + // He has room. Copy it. + if (AI->ai_desclen != 0) { + (void)CopyToNdis(Buffer, AI->ai_desc, AI->ai_desclen, &Offset); + } + *Size = IFE_FIXED_SIZE + AI->ai_desclen; + return TDI_SUCCESS; + } else { + // Not enough room to copy the desc. string. + *Size = IFE_FIXED_SIZE; + return TDI_BUFFER_OVERFLOW; + } + + } + + return TDI_INVALID_PARAMETER; + +} + +//* ARPSetInfo - ARP set information handler. +// +// The ARP set information handler. We support setting of an I/F admin +// status, and setting/deleting of ARP table entries. +// +// Input: Context - Pointer to I/F to set on. +// ID - The object ID +// Buffer - Pointer to buffer containing value to set. +// Size - Size in bytes of Buffer. +// +// Returns: Status of attempt to set information. +// +int +ARPSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle, EntryHandle; + int Status; + IFEntry *IFE = (IFEntry *)Buffer; + IPNetToMediaEntry *IPNME; + ARPTableEntry *PrevATE, *CurrentATE; + ARPTable *Table; + ENetHeader *Header; + uint Entity, Instance; + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) && + (Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) { + return TDI_INVALID_REQUEST; + } + + if (ID->toi_type != INFO_TYPE_PROVIDER) { + return TDI_INVALID_PARAMETER; + } + + // Might be able to handle this. + if (Entity == IF_ENTITY) { + + // It's for the I/F level, see if it's for the statistics. + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + if (ID->toi_id == IF_MIB_STATS_ID) { + // It's for the stats. Make sure it's a valid size. + if (Size >= IFE_FIXED_SIZE) { + // It's a valid size. See what he wants to do. + CTEGetLock(&Interface->ai_lock, &Handle); + switch (IFE->if_adminstatus) { + case IF_STATUS_UP: + // He's marking it up. If the operational state is + // alse up, mark the whole interface as up. + Interface->ai_adminstate = IF_STATUS_UP; + if (Interface->ai_operstate == IF_STATUS_UP) + Interface->ai_state = INTERFACE_UP; + Status = TDI_SUCCESS; + break; + case IF_STATUS_DOWN: + // He's taking it down. Mark both the admin state and + // the interface state down. + Interface->ai_adminstate = IF_STATUS_DOWN; + Interface->ai_state = INTERFACE_DOWN; + Status = TDI_SUCCESS; + break; + case IF_STATUS_TESTING: + // He's trying to cause up to do testing, which we + // don't support. Just return success. + Status = TDI_SUCCESS; + break; + default: + Status = TDI_INVALID_PARAMETER; + break; + } + CTEFreeLock(&Interface->ai_lock, Handle); + return Status; + } else + return TDI_INVALID_PARAMETER; + } else { + return TDI_INVALID_PARAMETER; + } + } + + // Not for the interface level. See if it's an implementation or protocol + // class. + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + ProxyArpEntry *PArpEntry; + ARPIPAddr *Addr; + IPAddr AddAddr; + IPMask Mask; + + // It's for the implementation. It should be the proxy-ARP ID. + if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry)) + return TDI_INVALID_PARAMETER; + + PArpEntry = (ProxyArpEntry *)Buffer; + AddAddr = PArpEntry->pae_addr; + Mask = PArpEntry->pae_mask; + + // See if he's trying to add or delete a proxy arp entry. + if (PArpEntry->pae_status == PAE_STATUS_VALID) { + // We're trying to add an entry. We won't allow an entry + // to be added that we believe to be invalid or conflicting + // with our local addresses. + + if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) || + IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) || + CLASSD_ADDR(AddAddr)) + return TDI_INVALID_PARAMETER; + + // Walk through the list of addresses on the interface, and see + // if they would match the AddAddr. If so, fail the request. + CTEGetLock(&Interface->ai_lock, &Handle); + + if (IsBCastOnIF(Interface, AddAddr & Mask)) { + CTEFreeLock(&Interface->ai_lock, Handle); + return TDI_INVALID_PARAMETER; + } + + Addr = &Interface->ai_ipaddr; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr)) + break; + } + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + if (Addr != NULL) + return TDI_INVALID_PARAMETER; + + // At this point, we believe we're ok. Try to add the address. + if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL)) + return TDI_SUCCESS; + else + return TDI_NO_RESOURCES; + } else { + if (PArpEntry->pae_status == PAE_STATUS_INVALID) { + // He's trying to delete a proxy ARP address. + if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask)) + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + + if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID && + Size >= sizeof(IPNetToMediaEntry)) { + // He does want to set an ARP table entry. See if he's trying to + // create or delete one. + + IPNME = (IPNetToMediaEntry *)Buffer; + if (IPNME->inme_type == INME_TYPE_INVALID) { + uint Index = ARP_HASH(IPNME->inme_addr); + + // We're trying to delete an entry. See if we can find it, + // and then delete it. + CTEGetLock(&Interface->ai_ARPTblLock, &Handle); + Table = Interface->ai_ARPTbl; + PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next); + CurrentATE = (*Table)[Index]; + while (CurrentATE != (ARPTableEntry *)NULL) { + if (CurrentATE->ate_dest == IPNME->inme_addr) { + // Found him. Break out of the loop. + break; + } else { + PrevATE = CurrentATE; + CurrentATE = CurrentATE->ate_next; + } + } + + if (CurrentATE != NULL) { + CTEGetLock(&CurrentATE->ate_lock, &EntryHandle); + RemoveARPTableEntry(PrevATE, CurrentATE); + Interface->ai_count--; + CTEFreeLock(&CurrentATE->ate_lock, EntryHandle); + + if (CurrentATE->ate_packet != NULL) + IPSendComplete(Interface->ai_context, CurrentATE->ate_packet, + NDIS_STATUS_SUCCESS); + + CTEFreeMem(CurrentATE); + Status = TDI_SUCCESS; + } else + Status = TDI_INVALID_PARAMETER; + + CTEFreeLock(&Interface->ai_ARPTblLock, Handle); + return Status; + } + + // We're not trying to delete. See if we're trying to create. + if (IPNME->inme_type != INME_TYPE_DYNAMIC && + IPNME->inme_type != INME_TYPE_STATIC) { + // Not creating, return an error. + return TDI_INVALID_PARAMETER; + } + + // Make sure he's trying to create a valid address. + if (IPNME->inme_physaddrlen != Interface->ai_addrlen) + return TDI_INVALID_PARAMETER; + + // We're trying to create an entry. Call CreateARPTableEntry to create + // one, and fill it in. + CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle); + if (CurrentATE == NULL) { + return TDI_NO_RESOURCES; + } + + // We've created or found an entry. Fill it in. + Header = (ENetHeader *)CurrentATE->ate_addr; + + switch (Interface->ai_media) { + case NdisMedium802_5: + { + TRHeader *Temp = (TRHeader *)Header; + + // Fill in the TR specific parts, and set the length to the + // size of a TR header. + + Temp->tr_ac = ARP_AC; + Temp->tr_fc = ARP_FC; + CTEMemCopy(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, + sizeof(SNAPHeader)); + + Header = (ENetHeader *)&Temp->tr_daddr; + CurrentATE->ate_addrlength = sizeof(TRHeader) + + sizeof(SNAPHeader); + } + break; + case NdisMedium802_3: + CurrentATE->ate_addrlength = sizeof(ENetHeader); + break; + case NdisMediumFddi: + { + FDDIHeader *Temp = (FDDIHeader *)Header; + + Temp->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, + sizeof(SNAPHeader)); + Header = (ENetHeader *)&Temp->fh_daddr; + CurrentATE->ate_addrlength = sizeof(FDDIHeader) + + sizeof(SNAPHeader); + } + break; + case NdisMediumArcnet878_2: + { + ARCNetHeader *Temp = (ARCNetHeader *)Header; + + Temp->ah_saddr = Interface->ai_addr[0]; + Temp->ah_daddr = IPNME->inme_physaddr[0]; + Temp->ah_prot = ARP_ARCPROT_IP; + CurrentATE->ate_addrlength = sizeof(ARCNetHeader); + } + break; + default: + DEBUGCHK; + break; + } + + + // Copy in the source and destination addresses. + + if (Interface->ai_media != NdisMediumArcnet878_2) { + CTEMemCopy(Header->eh_daddr, IPNME->inme_physaddr, + ARP_802_ADDR_LENGTH); + CTEMemCopy(Header->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + // Now fill in the Ethertype. + *(ushort *)&CurrentATE->ate_addr[CurrentATE->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } + + // If he's creating a static entry, mark it as always valid. Otherwise + // mark him as valid now. + if (IPNME->inme_type == INME_TYPE_STATIC) + CurrentATE->ate_valid = ALWAYS_VALID; + else + CurrentATE->ate_valid = CTESystemUpTime(); + + CurrentATE->ate_state = ARP_GOOD; + + CTEFreeLock(&CurrentATE->ate_lock, Handle); + return TDI_SUCCESS; + + } + + return TDI_INVALID_PARAMETER; + + +} + + +static uint ARPPackets = ARP_DEFAULT_PACKETS; +static uint ARPBuffers = ARP_DEFAULT_BUFFERS; + +#pragma BEGIN_INIT +//** ARPInit - Initialize the ARP module. +// +// This functions intializes all of the ARP module, including allocating +// the ARP table and any other necessary data structures. +// +// Entry: nothing. +// +// Exit: Returns 0 if we fail to init., !0 if we succeed. +// +int +ARPInit() +{ + NDIS_STATUS Status; // Status for NDIS calls. + +// BUGBUG - Get configuration information dynamically. + +#ifdef NT + RtlInitUnicodeString(&(ARPCharacteristics.Name), ARPName); +#else // NT + ARPCharacteristics.Name.Buffer = ARPName; +#endif // NT + + NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *) + &ARPCharacteristics, sizeof(ARPCharacteristics)); + + if (Status == NDIS_STATUS_SUCCESS) { + return(1); + } + else { + return(0); + } +} +#pragma END_INIT + +#ifndef CHICAGO +#pragma BEGIN_INIT +#else +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//* FreeARPInterface - Free an ARP interface +// +// Called in the event of some sort of initialization failure. We free all +// the memory associated with an ARP interface. +// +// Entry: Interface - Pointer to interface structure to be freed. +// +// Returns: Nothing. +// +void +FreeARPInterface(ARPInterface *Interface) +{ + NDIS_STATUS Status; + ARPBufferTracker *Tracker; + ARPTable *Table; // ARP table. + uint i; // Index variable. + ARPTableEntry *ATE; + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + CTEStopTimer(&Interface->ai_timer); + +// If we're bound to the adapter, close it now. + CTEInitBlockStruc(&Interface->ai_block); + + CTEGetLock(&Interface->ai_lock, &LockHandle); + if (Interface->ai_handle != (NDIS_HANDLE)NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + NdisCloseAdapter(&Status, Handle); + + if (Status == NDIS_STATUS_PENDING) + Status = CTEBlock(&Interface->ai_block); + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + } + + // First free any outstanding ARP table entries. + Table = Interface->ai_ARPTbl; + if (Table != NULL) { + for (i = 0; i < ARP_TABLE_SIZE;i++) { + while ((*Table)[i] != NULL) { + ATE = (*Table)[i]; + RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), + ate_next),ATE); + CTEFreeMem(ATE); + } + } + CTEFreeMem(Table); + } + + Interface->ai_ARPTbl = NULL; + + if (Interface->ai_ppool != (NDIS_HANDLE)NULL) + NdisFreePacketPool(Interface->ai_ppool); + + if (Interface->ai_bpool != (NDIS_HANDLE)NULL) + NdisFreeBufferPool(Interface->ai_bpool); + + Tracker = Interface->ai_buflist; + while (Tracker != NULL) { + Interface->ai_buflist = Tracker->abt_next; + NdisFreeBufferPool(Tracker->abt_handle); + CTEFreeMem(Tracker->abt_buffer); + CTEFreeMem(Tracker); + Tracker = Interface->ai_buflist; + } + + if (Interface->ai_bbbase != (uchar *)NULL) + CTEFreeMem(Interface->ai_bbbase); + + // Free the interface itself. + CTEFreeMem(Interface); +} + +//** ARPOpen - Open an adapter for reception. +// +// This routine is called when the upper layer is done initializing and wishes to +// begin receiveing packets. The adapter is actually 'open', we just call InitAdapter +// to set the packet filter and lookahead size. +// +// Input: Context - Interface pointer we gave to IP earlier. +// +// Returns: Nothing +// +void +ARPOpen(void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; + InitAdapter(Interface); // Set the packet filter - we'll begin receiving. +} + +//* ARPGetEList - Get the entity list. +// +// Called at init time to get an entity list. We fill our stuff in, and +// then call the interfaces below us to allow them to do the same. +// +// Input: EntityList - Pointer to entity list to be filled in. +// Count - Pointer to number of entries in the list. +// +// Returns Status of attempt to get the info. +// +int +ARPGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + ARPInterface *Interface = (ARPInterface *)Context; + uint ECount; + uint MyATBase; + uint MyIFBase; + uint i; + + ECount = *Count; + + // Walk down the list, looking for existing AT or IF entities, and + // adjust our base instance accordingly. + + MyATBase = 0; + MyIFBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == AT_ENTITY) + MyATBase = MAX(MyATBase, EntityList->tei_instance + 1); + else + if (EntityList->tei_entity == IF_ENTITY) + MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1); + } + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. We need one for the ICMP instance, + // and one for the CL_NL instance. + + if ((ECount + 2) > MAX_TDI_ENTITIES) + return FALSE; + + // At this point we've figure out our base instance. Save for later use. + Interface->ai_atinst = MyATBase; + Interface->ai_ifinst = MyIFBase; + + // Now fill it in. + EntityList->tei_entity = AT_ENTITY; + EntityList->tei_instance = MyATBase; + EntityList++; + EntityList->tei_entity = IF_ENTITY; + EntityList->tei_instance = MyIFBase; + *Count += 2; + + return TRUE; +} + + +extern uint UseEtherSNAP(PNDIS_STRING Name); +extern void GetAlwaysSourceRoute(uint *pArpAlwaysSourceRoute, uint *pIPAlwaysSourceRoute); +extern uint GetArpCacheLife(void); + + +//** ARPRegister - Register a protocol with the ARP module. +// +// We register a protocol for ARP processing. We also open the +// NDIS adapter here. +// +// Note that much of the information passed in here is unused, as +// ARP currently only works with IP. +// +// Entry: +// Adapter - Name of the adapter to bind to. +// IPContext - Value to be passed to IP on upcalls. +// +#ifndef _PNP_POWER +int +ARPRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn, + IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn, + IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound) +#else +int +ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface) +#endif +{ + ARPInterface *ai; // Pointer to interface struct. for this + // interface. + NDIS_STATUS Status, OpenStatus; // Status values. + uint i = 0; // Medium index. + NDIS_MEDIUM MediaArray[MAX_MEDIA]; + uchar sbsize; + uchar *buffer; // Pointer to our buffers. + uint mss; + uint speed; + uint Needed; + uint MacOpts; + uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize; + uint OID; + uint PF; + PNDIS_BUFFER Buffer; + + if ((ai = CTEAllocMem(sizeof(ARPInterface))) == (ARPInterface *)NULL) + return FALSE; // Couldn't allocate memory for this one. + +#ifdef _PNP_POWER + *Interface = ai; +#endif + + CTEMemSet(ai, 0, sizeof(ARPInterface)); + CTEInitTimer(&ai->ai_timer); + +#ifdef NT + ExInitializeSListHead(&ai->ai_sblist); +#endif + + + MediaArray[MEDIA_DIX] = NdisMedium802_3; + MediaArray[MEDIA_TR] = NdisMedium802_5; + MediaArray[MEDIA_FDDI] = NdisMediumFddi; + MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2; + + // Initialize this adapter interface structure. + ai->ai_state = INTERFACE_INIT; + ai->ai_adminstate = IF_STATUS_DOWN; + ai->ai_operstate = IF_STATUS_DOWN; + ai->ai_bcast = IP_LOCAL_BCST; + ai->ai_maxhdrs = ARP_DEFAULT_MAXHDRS; + +#ifndef _PNP_POWER + ai->ai_index = NumIFBound + 1; + ai->ai_context = IPContext; + Info->lip_context = ai; + Info->lip_transmit = ARPTransmit; + Info->lip_transfer = ARPXferData; + Info->lip_close = ARPClose; + Info->lip_addaddr = ARPAddAddr; + Info->lip_deladdr = ARPDeleteAddr; + Info->lip_invalidate = ARPInvalidate; + Info->lip_open = ARPOpen; + Info->lip_qinfo = ARPQueryInfo; + Info->lip_setinfo = ARPSetInfo; + Info->lip_getelist = ARPGetEList; + + Info->lip_index = ai->ai_index; +#endif + + // Initialize the locks. + CTEInitLock(&ai->ai_lock); + CTEInitLock(&ai->ai_ARPTblLock); + + GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute); + + ArpCacheLife = GetArpCacheLife(); + + if (!ArpCacheLife) { + ArpCacheLife = 1; + } + + ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME; + + // Allocate the buffer and packet pools. + NdisAllocatePacketPool(&Status, &ai->ai_ppool, ARPPackets, sizeof(struct PCCommon)); + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + NdisAllocateBufferPool(&Status, &ai->ai_bpool, ARPBuffers); + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + // Allocate the ARP table + if ((ai->ai_ARPTbl = (ARPTable *)CTEAllocMem(ARP_TABLE_SIZE * sizeof(ARPTableEntry *))) == + (ARPTable *)NULL) { + FreeARPInterface(ai); + return FALSE; + } + + // + // NULL out the pointers + // + CTEMemSet(ai->ai_ARPTbl, 0, ARP_TABLE_SIZE * sizeof(ARPTableEntry *)); + + CTEInitBlockStruc(&ai->ai_block); + + // Open the NDIS adapter. + NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray, + MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL); + + // Block for open to complete. + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&ai->ai_block); + + ai->ai_media = MediaArray[i]; // Fill in media type. + + // Open adapter completed. If it succeeded, we'll finish our intialization. + // If it failed, bail out now. + if (Status != NDIS_STATUS_SUCCESS) { + ai->ai_handle = NULL; + FreeARPInterface(ai); + return FALSE; + } + + // Read the local address. + switch (ai->ai_media) { + case NdisMedium802_3: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = ENET_BCAST_MASK; + bcastval = ENET_BCAST_VAL; + bcastoff = ENET_BCAST_OFF; + OID = OID_802_3_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_ENET; + hdrsize = sizeof(ENetHeader); + if (!UseEtherSNAP(Adapter)) { + snapsize = 0; + } else { + snapsize = sizeof(SNAPHeader); + sbsize += sizeof(SNAPHeader); + } + + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | + NDIS_PACKET_TYPE_MULTICAST; + break; + case NdisMedium802_5: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = TR_BCAST_MASK; + bcastval = TR_BCAST_VAL; + bcastoff = TR_BCAST_OFF; + OID = OID_802_5_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_TR; + hdrsize = sizeof(TRHeader); + snapsize = sizeof(SNAPHeader); + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; + break; + case NdisMediumFddi: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = FDDI_BCAST_MASK; + bcastval = FDDI_BCAST_VAL; + bcastoff = FDDI_BCAST_OFF; + OID = OID_FDDI_LONG_CURRENT_ADDR; + sbsize = ARP_MAX_MEDIA_FDDI; + hdrsize = sizeof(FDDIHeader); + snapsize = sizeof(SNAPHeader); + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | + NDIS_PACKET_TYPE_MULTICAST; + break; + case NdisMediumArcnet878_2: + addrlen = 1; + bcastmask = ARC_BCAST_MASK; + bcastval = ARC_BCAST_VAL; + bcastoff = ARC_BCAST_OFF; + OID = OID_ARCNET_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_ARC; + hdrsize = sizeof(ARCNetHeader); + snapsize = 0; + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; + break; + default: + DEBUGCHK; + FreeARPInterface(ai); + return FALSE; + } + + ai->ai_bcastmask = bcastmask; + ai->ai_bcastval = bcastval; + ai->ai_bcastoff = bcastoff; + ai->ai_addrlen = addrlen; + ai->ai_hdrsize = hdrsize; + ai->ai_snapsize = snapsize; + ai->ai_pfilter = PF; + + Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID, + ai->ai_addr, addrlen, NULL); + + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + +#ifndef _PNP_POWER + Info->lip_addrlen = addrlen; + Info->lip_addr = ai->ai_addr; +#endif + + // Read the maximum frame size. + if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL)) != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + // If this is token ring, figure out the RC len stuff now. + mss -= (uint)ai->ai_snapsize; + + if (ai->ai_media == NdisMedium802_5) { + mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort))); + } else { + if (ai->ai_media == NdisMediumFddi) { + mss = MIN(mss, ARP_FDDI_MSS); + } + } + + ai->ai_mtu = (ushort)mss; + +#ifndef _PNP_POWER + Info->lip_mss = mss; +#endif + + // Read the speed for local purposes. + if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL)) == NDIS_STATUS_SUCCESS) { + ai->ai_speed = speed * 100L; +#ifndef _PNP_POWER + Info->lip_speed = ai->ai_speed; +#endif + } + + // Read and save the options. + Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS, + &MacOpts, sizeof(MacOpts), NULL); + + if (Status != NDIS_STATUS_SUCCESS) +#ifndef _PNP_POWER + Info->lip_flags = 0; +#else + *Flags = 0; +#endif + else +#ifndef _PNP_POWER + Info->lip_flags = +#else + *Flags = +#endif + (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0; + + // Read and store the vendor description string. + Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_VENDOR_DESCRIPTION, &ai->ai_desc, 0, &Needed); + + if ((Status == NDIS_STATUS_INVALID_LENGTH) || + (Status == NDIS_STATUS_BUFFER_TOO_SHORT)) { + // We know the size we need. Allocate a buffer. + buffer = CTEAllocMem(Needed); + if (buffer != NULL) { + Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_VENDOR_DESCRIPTION, buffer, Needed, NULL); + if (Status == NDIS_STATUS_SUCCESS) { + ai->ai_desc = buffer; + ai->ai_desclen = Needed; + } + } + } + + // Allocate our small and big buffer pools. + + if ((sbsize & 0x3)) { + // + // Must 32 bit align the buffers so pointers to them will be aligned. + // + sbsize = ((sbsize >> 2) + 1) << 2; + } + + ai->ai_sbsize = sbsize; + + // Pre-prime the ARP header buffer list. + Buffer = GrowARPHeaders(ai); + if (Buffer != NULL) { + FreeARPBuffer(ai, Buffer); + } + + + if ((buffer = CTEAllocMem((sbsize+sizeof(ARPHeader)) * ARPPackets)) == (uchar *)NULL) { + FreeARPInterface(ai); + return FALSE; + } + + // Link big buffers into the list. + ai->ai_bbbase = buffer; + ai->ai_bblist = (uchar *)NULL; + for (i = 0; i < ARPPackets; i++) { + *(char **)&*buffer = ai->ai_bblist; + ai->ai_bblist = buffer; + buffer += sbsize+sizeof(ARPHeader); + } + + // Everything's set up, so get the ARP timer running. + CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai); + + return TRUE; + +} + +#ifndef CHICAGO +#pragma END_INIT +#endif + +#ifdef _PNP_POWER + +//* ARPDynRegister - Dynamically register IP. +// +// Called by IP when he's about done binding to register with us. Since we +// call him directly, we don't save his info here. We do keep his context +// and index number. +// +// Input: See ARPRegister +// +// Returns: Nothing. +// +int +ARPDynRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn, + IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn, + IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound) +{ + ARPInterface *Interface = (ARPInterface *)Info->lip_context; + + Interface->ai_context = IPContext; + Interface->ai_index = NumIFBound; + + return TRUE; +} + +//* ARPBindAdapter - Bind and initialize an adapter. +// +// Called in a PNP environment to initialize and bind an adapter. We open +// the adapter and get it running, and then we call up to IP to tell him +// about it. IP will initialize, and if all goes well call us back to start +// receiving. +// +// Input: RetStatus - Where to return the status of this call. +// BindContext - Handle to use for calling BindAdapterComplete. +// AdapterName - Pointer to name of adapter. +// SS1 - System specific 1 parameter. +// SS2 - System specific 2 parameter. +// +// Returns: Nothing. +// +void NDIS_API +ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext, + PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2) +{ + uint Flags; // MAC binding flags. + ARPInterface *Interface; // Newly created interface. + PNDIS_STRING ConfigName; // Name used by IP for config. info. + IP_STATUS Status; // State of IPAddInterface call. + LLIPBindInfo BindInfo; // Binding informatio for IP. + NDIS_HANDLE Handle ; + + CTERefillMem(); + + if (!OpenIFConfig(SS1, &Handle)) { + *RetStatus = NDIS_STATUS_FAILURE; + return; + } + + // If IsLLInterfaceValueNull is FALSE then this means that some other ARP module is + // used for this device so we skip it. + // + if (IsLLInterfaceValueNull(Handle) == FALSE) { + *RetStatus = NDIS_STATUS_FAILURE; + CloseIFConfig(Handle); + return ; + } + + CloseIFConfig(Handle); + + + // First, open the adapter and get the info. + if (!ARPRegister(AdapterName, &Flags, &Interface)) { + *RetStatus = NDIS_STATUS_FAILURE; + return; + } + + CTERefillMem(); + + // OK, we're opened the adapter. Call IP to tell him about it. + BindInfo.lip_context = Interface; + BindInfo.lip_transmit = ARPTransmit; + BindInfo.lip_transfer = ARPXferData; + BindInfo.lip_close = ARPClose; + BindInfo.lip_addaddr = ARPAddAddr; + BindInfo.lip_deladdr = ARPDeleteAddr; + BindInfo.lip_invalidate = ARPInvalidate; + BindInfo.lip_open = ARPOpen; + BindInfo.lip_qinfo = ARPQueryInfo; + BindInfo.lip_setinfo = ARPSetInfo; + BindInfo.lip_getelist = ARPGetEList; + BindInfo.lip_mss = Interface->ai_mtu; + BindInfo.lip_speed = Interface->ai_speed; + BindInfo.lip_flags = Flags; + BindInfo.lip_addrlen = Interface->ai_addrlen; + BindInfo.lip_addr = Interface->ai_addr; + + Status = IPAddInterface((PNDIS_STRING)SS1, SS2, Interface, ARPDynRegister, + &BindInfo); + + if (Status != IP_SUCCESS) { + // Need to close the binding. FreeARPInterface will do that, as well + // as freeing resources. + + FreeARPInterface(Interface); + *RetStatus = NDIS_STATUS_FAILURE; + } else + *RetStatus = NDIS_STATUS_SUCCESS; + +} + +//* ARPUnbindAdapter - Unbind from an adapter. +// +// Called when we need to unbind from an adapter. We'll call up to IP to tell +// him. When he's done, we'll free our memory and return. +// +// Input: RetStatus - Where to return status from call. +// ProtBindContext - The context we gave NDIS earlier - really a +// pointer to an ARPInterface structure. +// UnbindContext - Context for completeing this request. +// +// Returns: Nothing. +// +void NDIS_API +ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext, + NDIS_HANDLE UnbindContext) +{ + ARPInterface *Interface = (ARPInterface *)ProtBindContext; + NDIS_STATUS Status; // Status of close call. + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + // Shut him up, so we don't get any more frames. + Interface->ai_pfilter = 0; + DoNDISRequest(Interface, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), + NULL); + + // Mark him as down. + Interface->ai_state = INTERFACE_DOWN; + Interface->ai_adminstate = IF_STATUS_DOWN; + + // Now tell IP he's gone. We need to make sure that we don't tell him twice. + // To do this we set the context to NULL after we tell him the first time, + // and we check to make sure it's non-NULL before notifying him. + + if (Interface->ai_context != NULL) { + IPDelInterface(Interface->ai_context); + Interface->ai_context = NULL; + } + + // Finally, close him. We do this here so we can return a valid status. + + CTEGetLock(&Interface->ai_lock, &LockHandle); + + if (Interface->ai_handle != NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + CTEInitBlockStruc(&Interface->ai_block); + NdisCloseAdapter(&Status, Handle); + + // Block for close to complete. + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&Interface->ai_block); + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + Status = NDIS_STATUS_SUCCESS; + } + + *RetStatus = Status; + + if (Status == NDIS_STATUS_SUCCESS) { + + FreeARPInterface(Interface); + } +} + +extern ulong VIPTerminate; + +//* ARPUnloadProtocol - Unload. +// +// Called when we need to unload. All we do is call up to IP, and return. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void NDIS_API +ARPUnloadProtocol(void) +{ + NDIS_STATUS Status; + +#ifdef CHICAGO + + IPULUnloadNotify(); + + if (VIPTerminate) { + NdisDeregisterProtocol(&Status, ARPHandle); + CTEUnload(NULL); + } + +#endif + +} + +#endif + diff --git a/private/ntos/tdi/tcpip/ip/arp.h b/private/ntos/tdi/tcpip/ip/arp.h new file mode 100644 index 000000000..d294b2dce --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arp.h @@ -0,0 +1,21 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** ARP.H - Exports from ARP. +// +// This file contains the public definitons from ARP. +extern int ARPInit(void); +#ifndef _PNP_POWER +extern int ARPRegister(PNDIS_STRING, void *, IPRcvRtn, IPTxCmpltRtn, + IPStatusRtn, IPTDCmpltRtn, IPRcvCmpltRtn, struct LLIPBindInfo *, + uint); +#else +int +ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface); +#endif + + + diff --git a/private/ntos/tdi/tcpip/ip/arpdef.h b/private/ntos/tdi/tcpip/ip/arpdef.h new file mode 100644 index 000000000..6278fdb60 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arpdef.h @@ -0,0 +1,340 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** arpdef.h - ARP definitions +// +// This file containes all of the private ARP related definitions. + + +#define MEDIA_DIX 0 +#define MEDIA_TR 1 +#define MEDIA_FDDI 2 +#define MEDIA_ARCNET 3 + +#define MAX_MEDIA 4 + +#define INTERFACE_UP 0 // Interface is up. +#define INTERFACE_INIT 1 // Interface is initializing. +#define INTERFACE_DOWN 2 // Interface is down. + +#define LOOKAHEAD_SIZE 128 // A reasonable lookahead size + +// Definitions for state of an ATE. The 'RESOLVING' indicators must occur first. +#define ARP_RESOLVING_LOCAL 0 // Address is being resolved (on local ring, if TR) +#define ARP_RESOLVING_GLOBAL 1 // Address is being resolved globally. +#define ARP_RESOLVING ARP_RESOLVING_GLOBAL +#define ARP_GOOD 2 // ATE is good. +#define ARP_BAD 3 // ATE is bad. +#define ARP_FLOOD_RATE 1000L // No more than once a second. +#define ARP_802_ADDR_LENGTH 6 // Length of an 802 address. + +#define MIN_ETYPE 0x600 // Minimum valid Ethertype +#define SNAP_SAP 170 +#define SNAP_UI 3 + + +//* Structure of an Ethernet header. +struct ENetHeader { + uchar eh_daddr[ARP_802_ADDR_LENGTH]; + uchar eh_saddr[ARP_802_ADDR_LENGTH]; + ushort eh_type; +}; /* ENetHeader */ + +typedef struct ENetHeader ENetHeader; + +//* Structure of a token ring header. +struct TRHeader { + uchar tr_ac; + uchar tr_fc; + uchar tr_daddr[ARP_802_ADDR_LENGTH]; + uchar tr_saddr[ARP_802_ADDR_LENGTH]; +}; /* TRHeader */ +#define ARP_AC 0x10 +#define ARP_FC 0x40 +#define TR_RII 0x80 + +typedef struct TRHeader TRHeader; +struct RC { + uchar rc_blen; // Broadcast indicator and length. + uchar rc_dlf; // Direction and largest frame. +}; /* RC */ +#define RC_DIR 0x80 +#define RC_LENMASK 0x1f +#define RC_SRBCST 0xc2 // Single route broadcast RC. +#define RC_ARBCST 0x82 // All route broadcast RC. +#define RC_LMASK 0x1F // Mask for length field for route + // information +#define RC_LEN 0x2 // Length to put in the length bits + // when sending source routed + // frames +#define RC_BCST_LEN 0x70 // Length for a broadcast. +#define RC_LF_MASK 0x70 // Mask for length bits. + +typedef struct RC RC; + +//* Structure of source routing information. +struct SRInfo { + RC sri_rc; // Routing control info. + ushort sri_rd[1]; // Routing designators. +}; /* SRInfo */ + +#define ARP_MAX_RD 8 + +typedef struct SRInfo SRInfo; + +//* Structure of an FDDI header. +struct FDDIHeader { + uchar fh_pri; + uchar fh_daddr[ARP_802_ADDR_LENGTH]; + uchar fh_saddr[ARP_802_ADDR_LENGTH]; +}; /* FDDIHeader */ + +typedef struct FDDIHeader FDDIHeader; + +#define ARP_FDDI_PRI 0x57 +#define ARP_FDDI_MSS 4352 + +//* Structure of an ARCNET header. +struct ARCNetHeader { + uchar ah_saddr; + uchar ah_daddr; + uchar ah_prot; +}; /* ARCNetHeader */ + +typedef struct ARCNetHeader ARCNetHeader; + +//* Structure of a SNAP header. +struct SNAPHeader { + uchar sh_dsap; + uchar sh_ssap; + uchar sh_ctl; + uchar sh_protid[3]; + ushort sh_etype; +}; /* SNAPHeader */ + +typedef struct SNAPHeader SNAPHeader; + +#define ARP_MAX_MEDIA_ENET sizeof(ENetHeader) +#define ARP_MAX_MEDIA_TR (sizeof(TRHeader)+sizeof(RC)+(ARP_MAX_RD*sizeof(ushort))+sizeof(SNAPHeader)) +#define ARP_MAX_MEDIA_FDDI (sizeof(FDDIHeader)+sizeof(SNAPHeader)) +#define ARP_MAX_MEDIA_ARC sizeof(ARCNetHeader) + +#define ENET_BCAST_MASK 0x01 +#define TR_BCAST_MASK 0x80 +#define FDDI_BCAST_MASK 0x01 +#define ARC_BCAST_MASK 0xff + +#define ENET_BCAST_VAL 0x01 +#define TR_BCAST_VAL 0x80 +#define FDDI_BCAST_VAL 0x01 +#define ARC_BCAST_VAL 0x00 + +#define ENET_BCAST_OFF 0x00 +#define TR_BCAST_OFF offsetof(struct TRHeader, tr_daddr) +#define FDDI_BCAST_OFF offsetof(struct FDDIHeader, fh_daddr) +#define ARC_BCAST_OFF offsetof(struct ARCNetHeader, ah_daddr) + +//* Structure of an ARP table entry. +typedef struct ARPTableEntry { + struct ARPTableEntry *ate_next; // Next ATE in hash chain + ulong ate_valid; // Last time ATE was known to be valid. + IPAddr ate_dest; // IP address represented. + PNDIS_PACKET ate_packet; // Packet (if any) queued for resolution + RouteCacheEntry *ate_rce; // List of RCEs that reference this ATE. + DEFINE_LOCK_STRUCTURE(ate_lock) // Lock for this ATE. + uint ate_useticks; // Number of ticks left until this + // goes away. + uchar ate_addrlength; // Length of the address. + uchar ate_state; // State of this entry + uchar ate_addr[1]; // Address that maps to dest +} ARPTableEntry; + +#define ALWAYS_VALID 0xffffffff + +//* Structure of the ARP table. +#define ARP_TABLE_SIZE 32 +#define ARP_HASH(x) ((((uchar *)&(x))[3]) % ARP_TABLE_SIZE) +typedef ARPTableEntry *ARPTable[]; + +//* List structure for local representation of an IPAddress. +typedef struct ARPIPAddr { + struct ARPIPAddr *aia_next; // Next in list. + uint aia_age; + IPAddr aia_addr; // The address. + IPMask aia_mask; + void *aia_context; +} ARPIPAddr; + +#define ARPADDR_NOT_LOCAL 4 +#define ARPADDR_NEW_LOCAL 3 +#define ARPADDR_OLD_LOCAL 0 + +//* List structure for Proxy-ARP addresses. +typedef struct ARPPArpAddr { + struct ARPPArpAddr *apa_next; // Next in list. + IPAddr apa_addr; // The address. + IPMask apa_mask; // And the mask. +} ARPPArpAddr; + +//* List structure for a multicast IP address. +typedef struct ARPMCastAddr { + struct ARPMCastAddr *ama_next; // Next in list. + IPAddr ama_addr; // The (masked) address. + uint ama_refcnt; // Reference count for this address. +} ARPMCastAddr; + +#define ARP_MCAST_MASK 0xffff7f00 + +#define ARP_TIMER_TIME 1000L +#define ARP_RESOLVE_TIMEOUT 1000L +#define ARP_MIN_VALID_TIMEOUT 600000L + +#ifdef VXD +#define ARP_DEFAULT_MAXHDRS 100 +#else +#define ARP_DEFAULT_MAXHDRS 0xffffffff +#endif + +typedef struct ARPBufferTracker { + struct ARPBufferTracker *abt_next; + NDIS_HANDLE abt_handle; + uchar *abt_buffer; +} ARPBufferTracker; + +//* Structure of information we keep on a per-interface basis. +typedef struct ARPInterface { + void *ai_context; // Upper layer context info. + NDIS_HANDLE ai_handle; // NDIS bind handle. + NDIS_MEDIUM ai_media; // Media type. + NDIS_HANDLE ai_bpool; // Handle for buffer pool. + NDIS_HANDLE ai_ppool; // Handle for packet pool. + DEFINE_LOCK_STRUCTURE(ai_lock) // Lock for this structure. + DEFINE_LOCK_STRUCTURE(ai_ARPTblLock) // ARP Table lock for this structure. +#ifdef NT + SLIST_HEADER ai_sblist; // Free list of header buffers. +#else + PNDIS_BUFFER ai_sblist; // Free list of header buffers. +#endif + uchar *ai_bblist; // Free list of 'big' buffers. + ARPTable *ai_ARPTbl; // Pointer to the ARP table for this interface + ARPIPAddr ai_ipaddr; // Local IP address list. + ARPPArpAddr *ai_parpaddr; // Proxy ARP address list. + IPAddr ai_bcast; // Broadcast mask for this interface. + // SNMP required counters + uint ai_inoctets; // Input octets. + uint ai_inpcount[2]; // Count of nonunicast and unicast + // packets received. + uint ai_outoctets; // Output octets + uint ai_outpcount[2];// Count of nonunicast and unicast + // packets sent. + uint ai_qlen; // Output q length. + uchar ai_addr[ARP_802_ADDR_LENGTH]; // Local HW address. + uchar ai_sbsize; // Size of a small buffer + uchar ai_state; // State of the interface. Union of + // admin and operational states. + uchar ai_addrlen; // Length of ai_addr. + uchar ai_bcastmask; // Mask for checking unicast. + uchar ai_bcastval; // Value to check against. + uchar ai_bcastoff; // Offset in frame to check against. + uchar ai_hdrsize; // Size of 'typical' header. + uchar ai_snapsize; // Size of snap header, if any. + uchar ai_pad[2]; // PAD PAD + uint ai_pfilter; // Packet filter for this i/f. + uint ai_count; // Number of entries in the ARPTable. + uint ai_parpcount; // Number of proxy ARP entries. + CTETimer ai_timer; // ARP timer for this interface. + CTEBlockStruc ai_block; // Structure for blocking on. + ushort ai_mtu; // MTU for this interface. + uchar ai_adminstate; // Admin state. + uchar ai_operstate; // Operational state; + uint ai_speed; // Speed. + uint ai_lastchange; // Last change time. + uint ai_indiscards; // In discards. + uint ai_inerrors; // Input errors. + uint ai_uknprotos; // Unknown protocols received. + uint ai_outdiscards; // Output packets discarded. + uint ai_outerrors; // Output errors. + uint ai_desclen; // Length of desc. string. + uint ai_index; // Global I/F index ID. + uint ai_atinst; // AT instance number. + uint ai_ifinst; // IF instance number. + char *ai_desc; // Descriptor string. + ARPMCastAddr *ai_mcast; // Multicast list. + uint ai_mcastcnt; // Count of elements on mcast list. + void *ai_bbbase; // Base of big buffers. + uint ai_curhdrs; // Current number of headers. + uint ai_maxhdrs; // Maximum allowed number of headers. + ARPBufferTracker *ai_buflist; // List of header buffer handles. +} ARPInterface; + +typedef struct ARPNotifyStruct { + CTEEvent ans_event; + uint ans_shutoff; + IPAddr ans_addr; + uint ans_hwaddrlen; + uchar ans_hwaddr[1]; +} ARPNotifyStruct; + +//* NOTE: These two values MUST stay at 0 and 1. +#define AI_UCAST_INDEX 0 +#define AI_NONUCAST_INDEX 1 + +#define ARP_DEFAULT_PACKETS 10 // Default to this many packets. +#define ARP_DEFAULT_BUFFERS 50 // And this many buffers. + +#define ARP_HDRBUF_GROW_SIZE 32 // This many header buffers. + +//* Structure of information passed as context in RCE. +typedef struct ARPContext { + RouteCacheEntry *ac_next; // Next RCE in ARP table chain. + ARPTableEntry *ac_ate; // Back pointer to ARP table entry. +} ARPContext; + +typedef struct IPNMEContext { + uint inc_index; + ARPTableEntry *inc_entry; +} IPNMEContext; + +#ifdef NT +// +// This structure must be packed under NT. +// +#include +#endif // NT + +// Structure of an ARP header. +struct ARPHeader { + ushort ah_hw; // Hardware address space. + ushort ah_pro; // Protocol address space. + uchar ah_hlen; // Hardware address length. + uchar ah_plen; // Protocol address length. + ushort ah_opcode; // Opcode. + uchar ah_shaddr[ARP_802_ADDR_LENGTH]; // Source HW address. + IPAddr ah_spaddr; // Source protocol address. + uchar ah_dhaddr[ARP_802_ADDR_LENGTH]; // Destination HW address. + IPAddr ah_dpaddr; // Destination protocol address. +}; /* ARPHeader */ + +#ifdef NT +#include +#endif // NT + +typedef struct ARPHeader ARPHeader; + +#define ARP_ETYPE_IP 0x800 +#define ARP_ETYPE_ARP 0x806 +#define ARP_REQUEST 1 +#define ARP_RESPONSE 2 +#define ARP_HW_ENET 1 +#define ARP_HW_802 6 +#define ARP_HW_ARCNET 7 + +#define ARP_ARCPROT_ARP 0xd5 +#define ARP_ARCPROT_IP 0xd4 + +// The size we need to back off the buffer length because ARCNet address +// are one bytes instead of six. +#define ARCNET_ARPHEADER_ADJUSTMENT 10 diff --git a/private/ntos/tdi/tcpip/ip/dirs b/private/ntos/tdi/tcpip/ip/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/tcpip/ip/icmp.c b/private/ntos/tdi/tcpip/ip/icmp.c new file mode 100644 index 000000000..5faafa486 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/icmp.c @@ -0,0 +1,1698 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** icmp.c - IP ICMP routines. +// +// This module contains all of the ICMP related routines. +// + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "icmp.h" +#include "info.h" +#include "iproute.h" +#include "ipinit.h" +#include "ipxmit.h" +#include + +extern ProtInfo IPProtInfo[]; // Protocol information table. + +extern void *IPRegisterProtocol(uchar, void *, void *, void *, void *); +extern ULStatusProc FindULStatus(uchar); +extern uchar IPUpdateRcvdOptions(IPOptInfo *, IPOptInfo *, IPAddr, IPAddr); +extern void IPInitOptions(IPOptInfo *); +extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *); +extern IP_STATUS IPFreeOptions(IPOptInfo *); +extern uchar IPGetLocalAddr(IPAddr, IPAddr *); +void ICMPRouterTimer(NetTableEntry *); + +extern NDIS_HANDLE BufferPool; + +extern NetTableEntry *NetTableList; // Pointer to the net table list. +extern ProtInfo *RawPI; // Raw IP protinfo + +DEFINE_LOCK_STRUCTURE(ICMPHeaderLock) +ICMPHeader *ICMPHeaderList; +uint CurrentICMPHeaders; +uint MaxICMPHeaders; + +ICMPStats ICMPInStats; +ICMPStats ICMPOutStats; + + +#ifdef NT +#ifdef ALLOC_PRAGMA + +void ICMPInit(uint NumBuffers); + +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ); + +#pragma alloc_text(INIT, ICMPInit) +#pragma alloc_text(PAGE, ICMPEchoRequest) + +#endif // ALLOC_PRAGMA +#endif // NT + + +//* UpdateICMPStats - Update ICMP statistics. +// +// A routine to update the ICMP statistics. +// +// Input: Stats - Pointer to stat. structure to update (input or output). +// Type - Type of stat to update. +// +// Returns: Nothing. +// +void +UpdateICMPStats(ICMPStats *Stats, uchar Type) +{ + switch (Type) { + case ICMP_DEST_UNREACH: + Stats->icmps_destunreachs++; + break; + case ICMP_TIME_EXCEED: + Stats->icmps_timeexcds++; + break; + case ICMP_PARAM_PROBLEM: + Stats->icmps_parmprobs++; + break; + case ICMP_SOURCE_QUENCH: + Stats->icmps_srcquenchs++; + break; + case ICMP_REDIRECT: + Stats->icmps_redirects++; + break; + case ICMP_TIMESTAMP: + Stats->icmps_timestamps++; + break; + case ICMP_TIMESTAMP_RESP: + Stats->icmps_timestampreps++; + break; + case ICMP_ECHO: + Stats->icmps_echos++; + break; + case ICMP_ECHO_RESP: + Stats->icmps_echoreps++; + break; + case ADDR_MASK_REQUEST: + Stats->icmps_addrmasks++; + break; + case ADDR_MASK_REPLY: + Stats->icmps_addrmaskreps++; + break; + default: + break; + } + +} + +//** GetICMPBuffer - Get an ICMP buffer, and allocate an NDIS_BUFFER that maps it. +// +// A routine to allocate an ICMP buffer and map an NDIS_BUFFER to it. +// +// Entry: Size - Size in bytes header buffer should be mapped as. +// Buffer - Pointer to pointer to NDIS_BUFFER to return. +// +// Returns: Pointer to ICMP buffer if allocated, or NULL. +// +ICMPHeader * +GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + NDIS_STATUS Status; + + + CTEGetLock(&ICMPHeaderLock, &Handle); + + Header = (ICMPHeader **)ICMPHeaderList; + + if (Header == NULL) { + // Couldn't get a header from our free list. Try to allocate one. + Header = CTEAllocMem(sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8); + if (Header == NULL) { + CTEFreeLock(&ICMPHeaderLock, Handle); + return (ICMPHeader *) NULL; + } + CurrentICMPHeaders++; + } + else { + ICMPHeaderList = *Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + NdisAllocateBuffer(&Status, Buffer, BufferPool, Header, Size + + sizeof(IPHeader)); + + if (Status == NDIS_STATUS_SUCCESS) { + NdisBufferLength(*Buffer) = Size; + Header = (ICMPHeader **)((uchar *)Header + sizeof(IPHeader)); + + (*(ICMPHeader **)&Header)->ich_xsum = 0; + return (ICMPHeader *)Header; + } + + // Couldn't get an NDIS_BUFFER, free the ICMP buffer. + CTEGetLock(&ICMPHeaderLock, &Handle); + + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + return (ICMPHeader *)NULL; +} + +//** FreeICMPBuffer - Free an ICMP buffer. +// +// This routine puts an ICMP buffer back on our free list. +// +// Entry: Buffer - Pointer to NDIS_BUFFER to be freed. +// +// Returns: Nothing. +// +void +FreeICMPBuffer(PNDIS_BUFFER Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + uint Length; + + NdisQueryBuffer(Buffer, (PVOID *)&Header, &Length); + CTEGetLock(&ICMPHeaderLock, &Handle); + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + NdisFreeBuffer(Buffer); +} + +//** DeleteEC - Remove an EchoControl from an NTE, and return a pointer to it. +// +// This routine is called when we need to remove an echo control structure from +// an NTE. We walk the list of EC structures on the NTE, and if we find a match +// we remove it and return a pointer to it. +// +// Entry: NTE - Pointer to NTE to be searched. +// Seq - Seq. # identifting the EC. +// +// Returns: Pointer to the EC if it finds it. +// +EchoControl * +DeleteEC(NetTableEntry *NTE, ushort Seq) +{ + EchoControl *Prev, *Current; + CTELockHandle Handle; + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if (Current->ec_seq == Seq) { + Prev->ec_next = Current->ec_next; + break; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + return Current; + +} + +//** ICMPSendComplete< - Complete an ICMP send. +// +// This routine is called when an ICMP send completes. We free the header buffer, +// the data buffer if there is one, and the NDIS_BUFFER chain. +// +// Entry: DataPtr - Pointer to data buffer, if any. +// BufferChain - Pointer to NDIS_BUFFER chain. +// +// Returns: Nothing +// +void +ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain) +{ + PNDIS_BUFFER DataBuffer; + + NdisGetNextBuffer(BufferChain, &DataBuffer); + FreeICMPBuffer(BufferChain); + + if (DataBuffer != (PNDIS_BUFFER)NULL) { // We had data with this ICMP send. +#ifdef DEBUG + if (DataPtr == (void *)NULL) + DEBUGCHK; +#endif + CTEFreeMem(DataPtr); + NdisFreeBuffer(DataBuffer); + } + +} + +//* XsumBufChain - Checksum a chain of buffers. +// +// Called when we need to checksum an IPRcvBuf chain. +// +// Input: BufChain - Buffer chain to be checksummed. +// +// Returns: The checksum. +// +ushort +XsumBufChain(IPRcvBuf *BufChain) +{ + ulong CheckSum = 0; + + if (BufChain == NULL) + DEBUGCHK; + + do { + CheckSum += (ulong)xsum(BufChain->ipr_buffer, BufChain->ipr_size); + BufChain = BufChain->ipr_next; + } while (BufChain != NULL); + + // Fold the checksum down. + CheckSum = (CheckSum >> 16) + (CheckSum & 0xffff); + CheckSum += (CheckSum >> 16); + + return (ushort)CheckSum; +} + + +//** SendEcho - Send an ICMP Echo or Echo response. +// +// This routine sends an ICMP echo or echo response. The Echo/EchoResponse may +// carry data. If it does we'll copy the data here. The request may also have +// options. Options are not copied, as the IPTransmit routine will copy options. +// +// Entry: Dest - Destination to send to. +// Source - Source to send from. +// Type - Type of request (ECHO or ECHO_RESP) +// ID - ID of request. +// Seq - Seq. # of request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// OptInfo - Pointer to IP Options structure. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendEcho(IPAddr Dest, IPAddr Source, uchar Type, ushort ID, ushort Seq, + IPRcvBuf *Data, uint DataLength, IPOptInfo *OptInfo) +{ + uchar *DataBuffer = (uchar *)NULL; // Pointer to data buffer. + PNDIS_BUFFER HeaderBuffer, Buffer; // Buffers for our header and user data. + NDIS_STATUS Status; + ICMPHeader *Header; + ushort header_xsum; + IP_STATUS IStatus; // Status of transmit + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader), &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + +#ifdef DEBUG + if (Type != ICMP_ECHO_RESP && Type != ICMP_ECHO) + DEBUGCHK; +#endif + + Header->ich_type = Type; + Header->ich_code = 0; + *(ushort *)&Header->ich_param = ID; + *((ushort *)&Header->ich_param + 1) = Seq; + header_xsum = xsum(Header, sizeof(ICMPHeader)); + Header->ich_xsum = ~header_xsum; + + // If there's data, get a buffer and copy it now. If we can't do this fail the request. + if (DataLength != 0) { + ulong TempXsum; + uint BytesToCopy, CopyIndex; + + DataBuffer = CTEAllocMem(DataLength); + if (DataBuffer == (void *)NULL) { // Couldn't get a buffer + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + BytesToCopy = DataLength; + CopyIndex = 0; + do { + uint CopyLength; +#ifdef DEBUG + if (Data == NULL) { + DEBUGCHK; + break; + } +#endif + + CopyLength = MIN(BytesToCopy, Data->ipr_size); + + CTEMemCopy(DataBuffer + CopyIndex, Data->ipr_buffer, CopyLength); + Data = Data->ipr_next; + CopyIndex += CopyLength; + BytesToCopy -= CopyLength; + } while (BytesToCopy); + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, DataBuffer, DataLength); + if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get an NDIS_BUFFER + CTEFreeMem(DataBuffer); + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + // Compute rest of xsum. + TempXsum = (ulong)header_xsum + (ulong)xsum(DataBuffer, DataLength); + TempXsum = (TempXsum >> 16) + (TempXsum & 0xffff); + TempXsum += (TempXsum >> 16); + Header->ich_xsum = ~(ushort)TempXsum; + NDIS_BUFFER_LINKAGE(HeaderBuffer) = Buffer; + } + + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, DataBuffer, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Source, OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(DataBuffer, HeaderBuffer); + + return IStatus; +} + +//** SendICMPMsg - Send an ICMP message +// +// This is the general ICMP message sending routine, called for most ICMP sends besides +// echo. Basically, all we do is get a buffer, format the info, copy the input +// header, and send the message. +// +// Entry: Src - IPAddr of source. +// Dest - IPAddr of destination +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPMsg(IPAddr Src, IPAddr Dest, uchar Type, uchar Code, ulong Pointer, + uchar *Data, uchar DataLength) +{ + PNDIS_BUFFER HeaderBuffer; // Buffer for our header + ICMPHeader *Header; + IP_STATUS IStatus; // Status of transmit + IPOptInfo OptInfo; // Options for this transmit. + + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader) + DataLength, &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + + Header->ich_type = Type; + Header->ich_code = Code; + Header->ich_param = Pointer; + if (Data) + CTEMemCopy(Header + 1, Data, DataLength); + Header->ich_xsum = ~xsum(Header, sizeof(ICMPHeader) + DataLength); + + IPInitOptions(&OptInfo); + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, NULL, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Src, &OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(NULL, HeaderBuffer); + + return IStatus; + +} + +//** SendICMPErr - Send an ICMP error message +// +// This is the routine used to send an ICMP error message, such as Destination Unreachable. +// We examine the header to find the length of the data, and also make sure we're not +// replying to another ICMP error message or a broadcast message. Then we call SendICMPMsg +// to send it. +// +// Entry: Src - IPAddr of source. +// Header - Pointer to IP Header that caused the problem. +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPErr(IPAddr Src, IPHeader UNALIGNED *Header, uchar Type, uchar Code, + ulong Pointer) +{ + uchar HeaderLength; // Length in bytes if header. + uchar DType; + + HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + + if (Header->iph_protocol == PROT_ICMP) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *) + ((uchar *)Header + HeaderLength); + + if (ICH->ich_type != ICMP_ECHO) + return IP_SUCCESS; + } + + // Don't respond to sends to a broadcast destination. + DType = GetAddrType(Header->iph_dest); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) + return IP_SUCCESS; + + // Don't respond if the source address is bad. + DType = GetAddrType(Header->iph_src); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType) || + (IP_LOOPBACK(Header->iph_dest) && DType != DEST_LOCAL)) + return IP_SUCCESS; + + // Make sure the source we're sending from is good. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || GetAddrType(Src) != DEST_LOCAL) + return IP_SUCCESS; + + // Double check to make sure it's an initial fragment. + if ((Header->iph_offset & IP_OFFSET_MASK) != 0) + return IP_SUCCESS; + + return SendICMPMsg(Src, Header->iph_src, Type, Code, Pointer, (uchar *)Header, + (uchar)(HeaderLength + 8)); + +} + + +//** ICMPTimer - Timer for ICMP +// +// This is the timer routine called periodically by global IP timer. We walk through +// the list of pending pings, and if we find one that's timed out we remove it and +// call the finish routine. +// +// Entry: NTE - Pointer to NTE being timed out. +// +// Returns: Nothing +// +void +ICMPTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + EchoControl *TimeoutList = (EchoControl *)NULL; // Timed out entries. + EchoControl *Prev, *Current; + ulong Now = CTESystemUpTime(); + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if ((Current->ec_active) && (Current->ec_to < Now)) { // This one's timed out. + Prev->ec_next = Current->ec_next; + // Link him on timed out list. + Current->ec_next = TimeoutList; + TimeoutList = Current; + Current = Prev->ec_next; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + // Now go through the timed out entries, and call the completion routine. + while (TimeoutList != (EchoControl *)NULL) { + EchoRtn Rtn; + + Current = TimeoutList; + TimeoutList = Current->ec_next; + + Rtn = (EchoRtn)Current->ec_rtn; + (*Rtn)(Current, IP_REQ_TIMED_OUT, NULL, 0, NULL); + } + + // + // [BUGBUG] Disabled for 4.0 sp2 + // + // ICMPRouterTimer(NTE); + +} + +//* CompleteEcho - Complete an echo request. +// +// Called when we need to complete an echo request, either because of a response +// or a received ICMP error message. We look it up, and then call the completion routine. +// +// Input: Header - Pointer to ICMP header causing completion. +// Status - Final status of request. +// Data - Data to be returned, if any. +// DataSize - Size in bytes of data. +// OptInfo - Option info structure. +// +// Returns: Nothing. +// +void +CompleteEcho(ICMPHeader UNALIGNED *Header, IP_STATUS Status, IPRcvBuf *Data, uint DataSize, + IPOptInfo *OptInfo) +{ + ushort NTEContext; + EchoControl *EC; + EchoRtn Rtn; + NetTableEntry *NTE; + + // Look up and remove the matching echo control block. + NTEContext = (*(ushort UNALIGNED *)&Header->ich_param); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if (NTEContext == NTE->nte_context) + break; + + if (NTE == NULL) + return; // Bad context value. + + EC = DeleteEC(NTE, *(((ushort UNALIGNED *)&Header->ich_param) + 1)); + if (EC != (EchoControl *)NULL) { // Found a match. + Rtn = (EchoRtn)EC->ec_rtn; + (*Rtn)(EC, Status, Data, DataSize, OptInfo); + } + + +} + +//** ICMPStatus - ICMP status handling procedure. +// +// This is the procedure called during a status change, either from an incoming ICMP +// message or a hardware status change. ICMP ignores most of these, unless we get an +// ICMP status message that was caused be an echo request. In that case we will complete +// the corresponding echo request with the appropriate error code. +// +// Input: StatusType - Type of status (NET or HW) +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is net status, the original dest. of DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local or remote). +// Param - Additional information for status - i.e. the param field of +// an ICMP message. +// Data - Data pertaining to status - for net status, this is the first +// 8 bytes of the original DG. +// +// Returns: Nothing +// +void +ICMPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, IPAddr OrigSrc, IPAddr Src, + ulong Param, void *Data) +{ + if (StatusType == IP_NET_STATUS) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *)Data; + // ICH is the datagram that caused the message. + + if (ICH->ich_type == ICMP_ECHO) { // And it was an echo request. + IPRcvBuf RcvBuf; + + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = (uchar *)&Src; + RcvBuf.ipr_size = sizeof(IPAddr); + CompleteEcho(ICH, StatusCode, &RcvBuf, sizeof(IPAddr), NULL); + } + } + +} + +//* ICMPMapStatus - Map an ICMP error to an IP status code. +// +// Called by ICMP status when we need to map from an incoming ICMP error code and type +// to an ICMP status. +// +// Entry: Type - Type of ICMP error. +// Code - Subcode of error. +// +// Returns: Corresponding IP status. +// +IP_STATUS +ICMPMapStatus(uchar Type, uchar Code) +{ + switch (Type) { + + case ICMP_DEST_UNREACH: + switch (Code) { + case NET_UNREACH: + case HOST_UNREACH: + case PROT_UNREACH: + case PORT_UNREACH: + return IP_DEST_UNREACH_BASE + Code; + break; + case FRAG_NEEDED: + return IP_PACKET_TOO_BIG; + break; + case SR_FAILED: + return IP_BAD_ROUTE; + break; + case DEST_NET_UNKNOWN: + case SRC_ISOLATED: + case DEST_NET_ADMIN: + case NET_UNREACH_TOS: + return IP_DEST_NET_UNREACHABLE; + break; + case DEST_HOST_UNKNOWN: + case DEST_HOST_ADMIN: + case HOST_UNREACH_TOS: + return IP_DEST_HOST_UNREACHABLE; + break; + default: + return IP_DEST_NET_UNREACHABLE; + } + break; + case ICMP_TIME_EXCEED: + if (Code == TTL_IN_TRANSIT) + return IP_TTL_EXPIRED_TRANSIT; + else + return IP_TTL_EXPIRED_REASSEM; + break; + case ICMP_PARAM_PROBLEM: + return IP_PARAM_PROBLEM; + break; + case ICMP_SOURCE_QUENCH: + return IP_SOURCE_QUENCH; + break; + default: + return IP_GENERAL_FAILURE; + break; + } + +} + +void +SendRouterSolicitation(NetTableEntry *NTE) +{ + if (NTE->nte_rtrdiscovery) { + SendICMPMsg(NTE->nte_addr, NTE->nte_rtrdiscaddr, ICMP_ROUTER_SOLICITATION, + 0, 0, NULL, 0); + } +} + +//** ICMPRouterTimer - Timeout default gateway entries +// +// This is the router advertisement timeout handler. When a router +// advertisement is received, we add the routers to our default gateway +// list if applicable. We then run a timer on the entries and refresh +// the list as new advertisements are received. If we fail to hear an +// update for a router within the specified lifetime we will delete the +// route from our routing tables. +// + +void +ICMPRouterTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *temprtrentry; + IPRtrEntry *lastrtrentry = NULL; + uint SendIt = FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (--rtrentry->ire_lifetime == 0) { + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry->ire_next; + } else { + lastrtrentry->ire_next = rtrentry->ire_next; + } + temprtrentry = rtrentry; + rtrentry = rtrentry->ire_next; +// DbgPrint("DeleteRoute: RtrAddr = %08x\n",temprtrentry->ire_addr); + DeleteRoute(NULL_IP_ADDR, DEFAULT_MASK, + temprtrentry->ire_addr, NTE->nte_if); + CTEFreeMem(temprtrentry); + } else { + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + } + if (NTE->nte_rtrdisccount != 0) { + NTE->nte_rtrdisccount--; + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_SOLICITING) && + ((NTE->nte_rtrdisccount%SOLICITATION_INTERVAL) == 0)) { + SendIt = TRUE; + } + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_DELAYING) && + (NTE->nte_rtrdisccount == 0)) { + NTE->nte_rtrdisccount = (SOLICITATION_INTERVAL)*(MAX_SOLICITATIONS-1); + NTE->nte_rtrdiscstate = NTE_RTRDISC_SOLICITING; + SendIt = TRUE; + } + } + CTEFreeLock(&NTE->nte_lock, Handle); + if (SendIt) { + SendRouterSolicitation(NTE); + } + +} + +//** ProcessRouterAdvertisement - Process a router advertisement +// +// This is the router advertisement handler. When a router advertisement +// is received, we add the routers to our default gateway list if applicable. +// + +uint +ProcessRouterAdvertisement(IPAddr Src, IPAddr LocalAddr, NetTableEntry *NTE, + ICMPRouterAdHeader UNALIGNED *AdHeader, IPRcvBuf *RcvBuf, uint Size) +{ + uchar NumAddrs = AdHeader->irah_numaddrs; + uchar AddrEntrySize = AdHeader->irah_addrentrysize; + ushort Lifetime = net_short(AdHeader->irah_lifetime); + ICMPRouterAdAddrEntry UNALIGNED *RouterAddr = (ICMPRouterAdAddrEntry UNALIGNED *)RcvBuf->ipr_buffer; + uint i; + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *lastrtrentry = NULL; + int Update = FALSE; + +// DbgPrint("ProcessRouterAdvertisement: NumAddrs = %d\n",NumAddrs); +// DbgPrint("ProcessRouterAdvertisement: AddrEntrySize = %d\n",AddrEntrySize); +// DbgPrint("ProcessRouterAdvertisement: Lifetime = %d\n",Lifetime); + + if ((NumAddrs == 0) || (AddrEntrySize < 2)) // per rfc 1256 + return FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + for ( i=0; iirae_addr & NTE->nte_mask) != (NTE->nte_addr & NTE->nte_mask)) { + continue; + } + if (!IsRouteICMP(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, NTE->nte_if)) { + continue; + } + +// DbgPrint("ProcessRouterAdvertisement: RtrAddr = %08x\n",RouterAddr->irae_addr); +// DbgPrint("ProcessRouterAdvertisement: RtrPreference = %d\n",net_long(RouterAddr->irae_preference)); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (rtrentry->ire_addr == RouterAddr->irae_addr) { + rtrentry->ire_lifetime = Lifetime*2; + if (rtrentry->ire_preference != RouterAddr->irae_preference) { + rtrentry->ire_preference = RouterAddr->irae_preference; + Update = TRUE; + } + break; + } + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + + if (rtrentry == NULL) { + rtrentry = (IPRtrEntry *) CTEAllocMem(sizeof(IPRtrEntry)); + if (rtrentry == NULL) { + return FALSE; + } + rtrentry->ire_next = NULL; + rtrentry->ire_addr = RouterAddr->irae_addr; + rtrentry->ire_preference = RouterAddr->irae_preference; + rtrentry->ire_lifetime = Lifetime*2; + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry; + } else { + lastrtrentry->ire_next = rtrentry; + } + Update = TRUE; + } + + if (Update && (RouterAddr->irae_preference != (long)0x00000080)) { // per rfc 1256 +// DbgPrint("AddRoute: RtrAddr = %08x\n",RouterAddr->irae_addr); + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, + NTE->nte_if, NTE->nte_mss, + (uint)(1000-net_long(RouterAddr->irae_preference)), // invert for metric + IRE_PROTO_ICMP, ATYPE_OVERRIDE, NULL); + } + Update = FALSE; + } + CTEFreeLock(&NTE->nte_lock, Handle); + + return TRUE; +} + +//** ICMPRcv - Receive an ICMP datagram. +// +// Called by the main IP code when we receive an ICMP datagram. The action we +// take depends on what the DG is. For some DGs, we call upper layer status +// handlers. For Echo Requests, we call the echo responder. +// +// Entry: NTE - Pointer to NTE on which ICMP message was received. +// Dest - IPAddr of destionation. +// Src - IPAddr of source +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the +// packet +// IPHdr - Pointer to IP Header +// IPHdrLength - Bytes in Header. +// RcvBuf - ICMP message buffer. +// Size - Size in bytes of ICMP message. +// IsBCast - Boolean indicator of whether or not this came in +// as a bcast. +// Protocol - Protocol this came in on. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception +// +IP_STATUS +ICMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo) +{ + ICMPHeader UNALIGNED *Header; + void *Data; // Pointer to data received. + IPHeader UNALIGNED *IPH; // Pointer to IP Header in error messages. + uint HeaderLength; // Size of IP header. + ULStatusProc ULStatus; // Pointer to upper layer status procedure. + IPOptInfo NewOptInfo; + uchar DType; + uint PassUp = FALSE; + + + ICMPInStats.icmps_msgs++; + + DType = GetAddrType(Src); + if (Size < sizeof(ICMPHeader) || DType == DEST_INVALID || + IS_BCAST_DEST(DType) || (IP_LOOPBACK(Dest) && DType != DEST_LOCAL) || + XsumBufChain(RcvBuf) != (ushort)0xffff) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad checksum. + } + + Header = (ICMPHeader UNALIGNED *)RcvBuf->ipr_buffer; + + + RcvBuf->ipr_buffer += sizeof(ICMPHeader); + RcvBuf->ipr_size -= sizeof(ICMPHeader); + + // Set up the data pointer for most requests, i.e. those that take less + // than MIN_FIRST_SIZE data. + + if (Size -= sizeof(ICMPHeader)) + Data = (void *)(Header + 1); + else + Data = (void *)NULL; + + switch (Header->ich_type) { + + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEED: + case ICMP_PARAM_PROBLEM: + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + + if (Data == NULL || Size < sizeof(IPHeader)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // No data, error. + } + + IPH = (IPHeader UNALIGNED *)Data; + HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + if (Size < (HeaderLength + MIN_ERRDATA_LENGTH)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Not enough data for this + // ICMP message. + } + + // Make sure that the source address of the datagram that triggered + // the message is one of ours. + + if (GetAddrType(IPH->iph_src) != DEST_LOCAL) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad src in header. + } + + if (Header->ich_type != ICMP_REDIRECT) { + + UpdateICMPStats(&ICMPInStats, Header->ich_type); + + if (ULStatus = FindULStatus(IPH->iph_protocol)) { + (void)(*ULStatus)(IP_NET_STATUS, + ICMPMapStatus(Header->ich_type, Header->ich_code), + IPH->iph_dest, IPH->iph_src, Src, Header->ich_param, + (uchar *)IPH + HeaderLength); + } + if (Header->ich_code == FRAG_NEEDED) + RouteFragNeeded( + IPH, + (ushort)net_short( + *((ushort UNALIGNED *)&Header->ich_param + 1) + ) + ); + } else { + ICMPInStats.icmps_redirects++; + Redirect(NTE, Src, IPH->iph_dest, IPH->iph_src, + Header->ich_param); + } + + PassUp = TRUE; + + break; + + + case ICMP_ECHO_RESP: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echoreps++; + // Look up and remove the matching echo control block. + CompleteEcho(Header, IP_SUCCESS, RcvBuf, Size, OptInfo); + + PassUp = TRUE; + + break; + + case ICMP_ECHO: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echos++; + + // Create our new optinfo structure. + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_tos = OptInfo->ioi_tos; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + + // If we have options, we need to reverse them and update any + // record route info. We can use the option buffer supplied by the + // IP layer, since we're part of him. + if (OptInfo->ioi_options != (uchar *)NULL) + IPUpdateRcvdOptions(OptInfo, &NewOptInfo, Src, LocalAddr); + + + SendEcho(Src, LocalAddr, ICMP_ECHO_RESP, + *(ushort UNALIGNED *)&Header->ich_param, + *((ushort UNALIGNED *)&Header->ich_param + 1), + RcvBuf, Size, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + break; + + case ADDR_MASK_REQUEST: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_addrmasks++; + + Dest = Src; + + SendICMPMsg(LocalAddr, Dest, ADDR_MASK_REPLY, 0, Header->ich_param, + (uchar *)&NTE->nte_mask, sizeof(IPMask)); + break; + + case ICMP_ROUTER_ADVERTISEMENT: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + if (NTE->nte_rtrdiscovery) { + if (!ProcessRouterAdvertisement(Src, LocalAddr, NTE, + (ICMPRouterAdHeader *)&Header->ich_param, RcvBuf, Size)) + return IP_SUCCESS; // An error was returned + } + PassUp = TRUE; + break; + + case ICMP_ROUTER_SOLICITATION: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + PassUp = TRUE; + break; + + default: + PassUp = TRUE; + UpdateICMPStats(&ICMPInStats, Header->ich_type); + break; + } + + // + // Pass the packet up to the raw layer if applicable. + // + if (PassUp && (RawPI != NULL)) { + if (RawPI->pi_rcv != NULL) { + // + // Restore the original values. + // + RcvBuf->ipr_buffer -= sizeof(ICMPHeader); + RcvBuf->ipr_size += sizeof(ICMPHeader); + Size += sizeof(ICMPHeader); + Data = (void *) Header; + + (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr, + IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo); + } + } + + return IP_SUCCESS; +} + + +//** ICMPEcho - Send an echo to the specified address. +// +// Entry: ControlBlock - Pointer to an EchoControl structure. This structure +// must remain valid until the req. completes. +// Timeout - Time in milliseconds to wait for response. +// Data - Pointer to data to send with echo. +// DataSize - Size in bytes of data. +// Callback - Routine to call when request is responded to or times out. +// Dest - Address to be pinged. +// OptInfo - Pointer to opt info structure to use for ping. +// +// Returns: IP_STATUS of attempt to ping.. +// +IP_STATUS +ICMPEcho(EchoControl *ControlBlock, ulong Timeout, void *Data, uint DataSize, EchoRtn Callback, + IPAddr Dest, IPOptInfo *OptInfo) +{ + IPAddr Dummy; + NetTableEntry *NTE; + CTELockHandle Handle; + ushort Seq; + IP_STATUS Status; + IPOptInfo NewOptInfo; + IPRcvBuf RcvBuf; + uint MTU; + Interface *IF; + uchar DType; + EchoControl *Current; + + if (OptInfo->ioi_ttl == 0) + return IP_BAD_OPTION; + + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_ttl = OptInfo->ioi_ttl; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + NewOptInfo.ioi_tos = OptInfo->ioi_tos & 0xfc; + + if (OptInfo->ioi_optlength != 0) { + Status = IPCopyOptions(OptInfo->ioi_options, OptInfo->ioi_optlength, + &NewOptInfo); + + if (Status != IP_SUCCESS) + return Status; + } + + if (!IP_ADDR_EQUAL(NewOptInfo.ioi_addr, NULL_IP_ADDR)) + Dest = NewOptInfo.ioi_addr; + + DType = GetAddrType(Dest); + if (DType == DEST_INVALID) { + IPFreeOptions(&NewOptInfo); + return IP_BAD_DESTINATION; + } + + if ((IF = LookupNextHopWithBuffer(Dest, NULL_IP_ADDR, &Dummy, &MTU, 0x1, NULL, 0)) == NULL) { + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; // Don't know how to get there. + } + + // Loop through the NetTable, looking for a matching NTE. + CTEGetLock(&RouteTableLock, &Handle); + if (DHCPActivityCount != 0) + NTE = NULL; + else + NTE = BestNTEForIF(Dummy, IF); + CTEFreeLock(&RouteTableLock, Handle); + +#ifdef _PNP_POWER + // We're done with the interface, so dereference it. + DerefIF(IF); +#endif + + if (NTE == NULL) { + // Couldn't find a matching NTE. This is very bad. + //DEBUGCHK; + + DbgPrint("ICMP: Failed to find NTE when going to %x\n",Dest); + + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; + } + + // Figure out the timeout. + ControlBlock->ec_to = CTESystemUpTime() + Timeout; + ControlBlock->ec_rtn = Callback; + ControlBlock->ec_active = 0; // Prevent from timing out until sent + CTEGetLock(&NTE->nte_lock, &Handle); + // Link onto ping list, and get seq. # */ + Seq = ++NTE->nte_icmpseq; + ControlBlock->ec_seq = Seq; + ControlBlock->ec_next = NTE->nte_echolist; + NTE->nte_echolist = ControlBlock; + CTEFreeLock(&NTE->nte_lock, Handle); + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = Data; + RcvBuf.ipr_size = DataSize; + Status = SendEcho(Dest, NTE->nte_addr, ICMP_ECHO, NTE->nte_context, + Seq, &RcvBuf, DataSize, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + + if (Status != IP_PENDING && Status != IP_SUCCESS) { // We had an error on the send. + if (DeleteEC(NTE, Seq) != (EchoControl *)NULL) + return Status; // We found it. + } + + // + // If the request is still pending, activate the timer + // + CTEGetLock(&NTE->nte_lock, &Handle); + + for ( + Current = NTE->nte_echolist; + Current != (EchoControl *)NULL; + Current = Current->ec_next + ) { + if (Current == ControlBlock) { + ControlBlock->ec_active = 1; // start the timer + break; + } + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + return IP_PENDING; + +} + + +//** ICMPEchoRequest - Common dispatch routine for echo requests +// +// This is the routine called by the OS-specific code on behalf of a user to issue an +// echo request. +// +// Entry: InputBuffer - Pointer to an ICMP_ECHO_REQUEST structure. +// InputBufferLength - Size in bytes of the InputBuffer. +// ControlBlock - Pointer to an EchoControl structure. This +// structure must remain valid until the +// request completes. +// Callback - Routine to call when request is responded to +// or times out. +// +// Returns: IP_STATUS of attempt to ping. +// +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ) +{ + PICMP_ECHO_REQUEST requestBuffer; + struct IPOptInfo optionInfo; + PUCHAR endOfRequestBuffer; + IP_STATUS status; + + +#ifdef NT + + PAGED_CODE(); + +#endif //NT + + requestBuffer = (PICMP_ECHO_REQUEST) InputBuffer; + endOfRequestBuffer = ((PUCHAR) requestBuffer) + InputBufferLength; + + // + // Validate the request. + // + if (InputBufferLength < sizeof(ICMP_ECHO_REQUEST)) { + status = IP_BUF_TOO_SMALL; + goto common_echo_exit; + } + + if (requestBuffer->DataSize > 0) { + if ( (requestBuffer->DataOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->DataOffset + + requestBuffer->DataSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + if (requestBuffer->OptionsSize > 0) { + if ( (requestBuffer->OptionsOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->OptionsOffset + + requestBuffer->OptionsSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + // + // Copy the options to a local structure. + // + if (requestBuffer->OptionsValid) { + optionInfo.ioi_optlength = requestBuffer->OptionsSize; + + if (requestBuffer->OptionsSize > 0) { + optionInfo.ioi_options = ((uchar *) requestBuffer) + + requestBuffer->OptionsOffset; + } + else { + optionInfo.ioi_options = NULL; + } + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = requestBuffer->Ttl; + optionInfo.ioi_tos = requestBuffer->Tos; + optionInfo.ioi_flags = requestBuffer->Flags; + } + else { + optionInfo.ioi_optlength = 0; + optionInfo.ioi_options = NULL; + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = DEFAULT_TTL; + optionInfo.ioi_tos = 0; + optionInfo.ioi_flags = 0; + } + + status = ICMPEcho( + ControlBlock, + requestBuffer->Timeout, + ((uchar *)requestBuffer) + requestBuffer->DataOffset, + requestBuffer->DataSize, + Callback, + (IPAddr) requestBuffer->Address, + &optionInfo + ); + +common_echo_exit: + + return(status); + +} // ICMPEchoRequest + + +//** ICMPEchoComplete - Common completion routine for echo requests +// +// This is the routine is called by the OS-specific code to process an +// ICMP echo response. +// +// Entry: OutputBuffer - Pointer to an ICMP_ECHO_REPLY structure. +// OutputBufferLength - Size in bytes of the OutputBuffer. +// Status - The status of the reply. +// Data - The reply data (may be NULL). +// DataSize - The amount of reply data. +// OptionInfo - A pointer to the reply options +// +// Returns: The number of bytes written to the output buffer +// +ulong +ICMPEchoComplete( + EchoControl *ControlBlock, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PICMP_ECHO_REPLY replyBuffer; + IPRcvBuf *dataBuffer; + uchar optionsLength; + uchar *tmp; + ulong bytesReturned = sizeof(ICMP_ECHO_REPLY); + + + replyBuffer = (PICMP_ECHO_REPLY) ControlBlock->ec_replybuf; + dataBuffer = (IPRcvBuf *) Data; + + if (OptionInfo != NULL) { + optionsLength = OptionInfo->ioi_optlength; + } + else { + optionsLength = 0; + } + + // + // Initialize the reply buffer + // + replyBuffer->Options.OptionsSize = 0; + replyBuffer->Options.OptionsData = (unsigned char FAR *) (replyBuffer + 1); + replyBuffer->DataSize = 0; + replyBuffer->Data = replyBuffer->Options.OptionsData; + + if ( (Status != IP_SUCCESS) && (DataSize == 0)) { + // + // Timed out or internal error. + // + replyBuffer->Reserved = 0; // indicate no replies. + replyBuffer->Status = Status; + } + else { + if (Status != IP_SUCCESS) { + // + // A message other than an echo reply was received. + // The IP Address of the system that reported the error is + // in the data buffer. There is no other data. + // + CTEAssert(dataBuffer->ipr_size == sizeof(IPAddr)); + + CTEMemCopy( + &(replyBuffer->Address), + dataBuffer->ipr_buffer, + sizeof(IPAddr) + ); + + DataSize = 0; + dataBuffer = NULL; + } + // else { + // + // BUGBUG - we currently depend on the fact that the destination + // address is still in the request buffer. The reply address + // should just be a parameter to this function. In NT, this + // just works since the input and output buffers are the same. + // In the VXD, the destination address must be put into the + // reply buffer by the OS-specific code. + // } + + // + // Check that the reply buffer is large enough to hold all the data. + // + if ( ControlBlock->ec_replybuflen < + (sizeof(ICMP_ECHO_REPLY) + DataSize + optionsLength) + ) { + // + // Not enough space to hold the reply. + // + replyBuffer->Reserved = 0; // indicate no replies + replyBuffer->Status = IP_BUF_TOO_SMALL; + } + else { + replyBuffer->Reserved = 1; // indicate one reply + replyBuffer->Status = Status; + replyBuffer->RoundTripTime = CTESystemUpTime() - + ControlBlock->ec_starttime; + + // + // Copy the reply options. + // + if (OptionInfo != NULL) { + replyBuffer->Options.Ttl = OptionInfo->ioi_ttl; + replyBuffer->Options.Tos = OptionInfo->ioi_tos; + replyBuffer->Options.Flags = OptionInfo->ioi_flags; + replyBuffer->Options.OptionsSize = optionsLength; + + if (optionsLength > 0) { + + CTEMemCopy( + replyBuffer->Options.OptionsData, + OptionInfo->ioi_options, + optionsLength + ); + } + } + + // + // Copy the reply data + // + replyBuffer->DataSize = (ushort) DataSize; + replyBuffer->Data = replyBuffer->Options.OptionsData + + replyBuffer->Options.OptionsSize; + + if (DataSize > 0) { + uint bytesToCopy; + + CTEAssert(Data != NULL); + + tmp = replyBuffer->Data; + + while (DataSize) { + CTEAssert(dataBuffer != NULL); + + bytesToCopy = (DataSize > dataBuffer->ipr_size) ? + dataBuffer->ipr_size : DataSize; + + CTEMemCopy( + tmp, + dataBuffer->ipr_buffer, + bytesToCopy + ); + + tmp += bytesToCopy; + DataSize -= bytesToCopy; + dataBuffer = dataBuffer->ipr_next; + } + } + + bytesReturned += replyBuffer->DataSize + optionsLength; + + // + // Convert the kernel pointers to offsets from start of reply buffer. + // + replyBuffer->Options.OptionsData = (unsigned char FAR *) + (((unsigned long) replyBuffer->Options.OptionsData) - + ((unsigned long) replyBuffer)); + + replyBuffer->Data = (void FAR *) + (((unsigned long) replyBuffer->Data) - + ((unsigned long) replyBuffer)); + } + } + + return(bytesReturned); +} + + +#ifdef VXD + +struct _pending_echo { + EchoControl ControlBlock; + CTEBlockStruc BlockStruc; +}; + +typedef struct _pending_echo PendingEcho; + + +//** VXDEchoComplete - OS-specific icmp echo completion routine. +// +// This routine is called by the OS-indepenent code to process a completed +// ICMP echo request. It calls common code to package the response. +// +// Entry: Context - A pointer to an EchoControl structure. +// Status - The status of the request +// Data - A pointer to the response data. +// DataSize - The amount of response data. +// OptionInfo - A pointer to the options contained in the reply. +// +// Returns: Nothing. +// +void +VXDEchoComplete( + void *Context, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PendingEcho *pendingEcho; + EchoControl *controlBlock; + ulong bytesReturned; + + + controlBlock = (EchoControl *) Context; + + bytesReturned = ICMPEchoComplete( + controlBlock, + Status, + Data, + DataSize, + OptionInfo + ); + + // + // The request thread will copy the returned byte count from + // the control block. + // + controlBlock->ec_replybuflen = bytesReturned; + + // + // Signal waiting request thread. + // + pendingEcho = STRUCT_OF(PendingEcho, controlBlock, ControlBlock); + CTESignal(&(pendingEcho->BlockStruc), Status); + + return; +} + + +//** VXDEchoRequest - Send an echo to the specified address. +// +// This routine dispatches an echo request on the VXD platform to the +// common code +// +// Entry: InBuf - Pointer to input buffer. +// InBufLen - Pointer to input buffer length. +// OutBuf - Pointer to output buffer. +// OutBufLen - Pointer to output buffer length. +// +// Returns: DWORD Win32 completion status. +// +ULONG +VXDEchoRequest( + void * InBuf, + ulong * InBufLen, + void * OutBuf, + ulong * OutBufLen + ) +{ + IP_STATUS ipStatus; + PendingEcho pendingEcho; + + pendingEcho.ControlBlock.ec_starttime = CTESystemUpTime(); + pendingEcho.ControlBlock.ec_replybuf = OutBuf; + pendingEcho.ControlBlock.ec_replybuflen = *OutBufLen; + CTEInitBlockStruc(&pendingEcho.BlockStruc); + + ipStatus = ICMPEchoRequest( + InBuf, + *InBufLen, + &(pendingEcho.ControlBlock), + VXDEchoComplete + ); + + if (ipStatus == IP_PENDING) { + ipStatus = CTEBlock(&(pendingEcho.BlockStruc)); + + if (ipStatus == IP_SUCCESS) { + PICMP_ECHO_REQUEST requestBuffer; + PICMP_ECHO_REPLY replyBuffer; + + // + // BUGBUG: + // + // It is necessary to copy the original destination address into + // the reply buffer because the src address is not provided to + // the completion routine for an echo response. This is the only + // reason why the signalling status is the reply status instead + // of just success. + // + requestBuffer = (PICMP_ECHO_REQUEST) InBuf; + replyBuffer = (PICMP_ECHO_REPLY) OutBuf; + + replyBuffer->Address = requestBuffer->Address; + } + + // + // The ioctl is considered successful as long as we can submit + // the request. The status of the request is contained in the + // reply buffer. The completion routine stuffed the return + // buffer length back into the control block. + // + ipStatus = IP_SUCCESS; + *OutBufLen = pendingEcho.ControlBlock.ec_replybuflen; + } + else { + // + // An internal error of some kind occurred. Since the VXD can + // return the IP_STATUS directly, do so. + // + CTEAssert(ipStatus != IP_SUCCESS); + + *OutBufLen = 0; + } + + return(ipStatus); +} + +#endif // VXD + + +#pragma BEGIN_INIT +//** ICMPInit - Initialize ICMP. +// +// This routine initializes ICMP. All we do is allocate and link up some header buffers, +/// and register our protocol with IP. +// +// Entry: NumBuffers - Number of ICMP buffers to allocate. +// +// Returns: Nothing +// +void +ICMPInit(uint NumBuffers) +{ + ICMPHeader **IHP; // Pointer to current ICMP header. + + + CTEInitLock(&ICMPHeaderLock); + MaxICMPHeaders = NumBuffers; + CurrentICMPHeaders = 0; + ICMPHeaderList= (ICMPHeader *)NULL; + + while (NumBuffers--) { + IHP = (ICMPHeader **) CTEAllocMem( + sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8 + ); + + if (IHP == (ICMPHeader **)NULL) { + break; + } + + *IHP = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)IHP; + CurrentICMPHeaders++; + } + + IPRegisterProtocol(PROT_ICMP, ICMPRcv, ICMPSendComplete, ICMPStatus, NULL); + +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/icmp.h b/private/ntos/tdi/tcpip/ip/icmp.h new file mode 100644 index 000000000..f45dac536 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/icmp.h @@ -0,0 +1,70 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** icmp.h - IP ICMP header. +// +// This module contains private ICMP definitions. +// + +#define PROT_ICMP 1 + +#define ICMP_ECHO_RESP 0 +#define ICMP_ECHO 8 +#define ICMP_TIMESTAMP 13 +#define ICMP_TIMESTAMP_RESP 14 + +#define MIN_ERRDATA_LENGTH 8 // Minimum amount of data we need. + +// Structure of an ICMP header. + +struct ICMPHeader { + uchar ich_type; // Type of ICMP packet. + uchar ich_code; // Subcode of type. + ushort ich_xsum; // Checksum of packet. + ulong ich_param; // Type-specific parameter field. +}; /* ICMPHeader */ + +struct ICMPRouterAdHeader { + uchar irah_numaddrs; // Number of addresses + uchar irah_addrentrysize; // Address Entry Size + ushort irah_lifetime; // Lifetime +}; /* ICMPRouterAdHeader */ + +struct ICMPRouterAdAddrEntry { + IPAddr irae_addr; // Router Address + long irae_preference; // Preference Level +}; /* ICMPRouterAdAddrEntry */ + +/*NOINC*/ +typedef struct ICMPHeader ICMPHeader; +typedef struct ICMPRouterAdHeader ICMPRouterAdHeader; +typedef struct ICMPRouterAdAddrEntry ICMPRouterAdAddrEntry; + +typedef void (*EchoRtn)(void *, IP_STATUS, void *, uint, IPOptInfo *); +/*INC*/ + +struct EchoControl { + struct EchoControl *ec_next; // Next control structure in list. + ulong ec_to; // Timeout + void *ec_rtn; // Pointer to routine to call when completing request. + ushort ec_seq; // Seq. # of this ping request. + uchar ec_active; // Set when packet has been sent + uchar ec_pad; // Pad. + ulong ec_starttime; // time request was issued + void *ec_replybuf; // buffer to store replies + ulong ec_replybuflen; // size of reply buffer +}; /* EchoControl */ + +/*NOINC*/ +typedef struct EchoControl EchoControl; +/*INC*/ + +extern ICMPHeader *GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer); +extern void FreeICMPBuffer(PNDIS_BUFFER Buffer); +extern void ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain); + + + diff --git a/private/ntos/tdi/tcpip/ip/igmp.c b/private/ntos/tdi/tcpip/ip/igmp.c new file mode 100644 index 000000000..340f0380f --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/igmp.c @@ -0,0 +1,799 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** igmp.c - IP multicast routines. +// +// This file contains all the routines related to the IGMP protocol. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "igmp.h" +#include "icmp.h" +#include "ipxmit.h" +#include "llipif.h" +#include "iproute.h" + + +#define IGMP_QUERY 0x11 //Membership query +#define IGMP_REPORT_V1 0x12 //Version 1 membership report +#define IGMP_REPORT_V2 0x16 //Version 2 membership report +#define IGMP_LEAVE 0x17 //Leave Group + + +#define IGMPV1 2 //IGMP version 1 +#define IGMPV2 3 //IGMP version 2 + +// +// undefine for 4.0 sp2 +// +#undef IGMPV2 + +#define ALL_HOST_MCAST 0x010000E0 + +#define MAX_DELAY_TICKS 20 //used when sending a report after a + //mcast group has been added. The + //report is sent at a interval of + //500 msecs to 9.5 secs + +// +// The following values are used to initialize counters that keep time in +// 1/2 a sec. +// +#define MAX_DELAY_IGMPV1_QUERY_RESP 20 //10 secs + +// +// The amount of time we stay in the "IGMPV1 Router Present" state in the +// absence of an IGMPV1 query +// +#define VERSION1_ROUTER_TIMEOUT 800 //400 secs + +int RandomValue; +int Seed; + +// Structure of an IGMP header. +typedef struct IGMPHeader { + uchar igh_vertype; // Type of igmp message + uchar igh_rsvd; // max. resp. time for igmpv2 messages; will be 0 + // for igmpv1 messages + ushort igh_xsum; + IPAddr igh_addr; +} IGMPHeader; + + +typedef struct IGMPBlockStruct { + struct IGMPBlockStruct *ibs_next; + CTEBlockStruc ibs_block; +} IGMPBlockStruct; + +void *IGMPProtInfo; + +IGMPBlockStruct *IGMPBlockList; +uchar IGMPBlockFlag; + +DEFINE_LOCK_STRUCTURE(IGMPLock) + +extern ProtInfo *RawPI; // Raw IP protinfo + +extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *); +extern void IPInitOptions(IPOptInfo *); +extern void *IPRegisterProtocol(uchar Protocol, void *RcvHandler, + void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler); + + +#ifdef NT + +// +// All of the init code can be discarded +// +#ifdef ALLOC_PRAGMA + +uint IGMPInit(void); + +#pragma alloc_text(INIT, IGMPInit) + +#endif // ALLOC_PRAGMA + +#endif // NT + +//* IGMPRandomTicks - Generate a random value of timer ticks. +// +// A random number routine to generate a random number of timer ticks, +// between 1 and time (in units of half secs) passed. The random number +// algorithm is adapted from the book 'System Simulation' by Geoffrey Gordon. +// +// Input: Nothing. +// +// Returns: A random value between 1 and TimeDelayInHalfSec. +// +uint +IGMPRandomTicks(uint TimeDelayInHalfSec) +{ + + RandomValue = RandomValue * 1220703125; + + if (RandomValue < 0) { + RandomValue += 2147483647; // inefficient, but avoids warnings. + RandomValue++; + } + + // Not sure if RandomValue can get to 0, but if it does the algorithm + // degenerates, so fix this if it happens. + if (RandomValue == 0) + RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1; + + return (uint)(((uint)RandomValue % TimeDelayInHalfSec) + 1); +} + + +//* FindIGMPAddr - Find an mcast entry on an NTE. +// +// Called to search an NTE for an IGMP entry for a given class D address. +// We walk down the chain on the NTE looking for it. If we find it, +// we return a pointer to it and the one immediately preceding it. If we +// don't find it we return NULL. We assume the caller has taken the lock +// on the NTE before calling us. +// +// Input: NTE - NTE on which to search. +// Addr - Class D address to find. +// PrevPtr - Where to return pointer to preceding entry. +// +// Returns: Pointer to matching IGMPAddr structure if found, or NULL if not +// found. +// +IGMPAddr * +FindIGMPAddr(NetTableEntry *NTE, IPAddr Addr, IGMPAddr **PrevPtr) +{ + IGMPAddr *Current, *Temp; + + Temp = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next); + Current = NTE->nte_igmplist; + + while (Current != NULL) { + if (IP_ADDR_EQUAL(Current->iga_addr, Addr)) { + // Found a match, so return it. + *PrevPtr = Temp; + break; + } + Temp = Current; + Current = Current->iga_next; + } + + return Current; + +} + +//** IGMPRcv - Receive an IGMP datagram. +// +// Called by IP when we receive an IGMP datagram. We validate it to make sure +// it's reasonable. Then if it it's a query for a group to which we belong +// we'll start a response timer. If it's a report to a group to which we belong +// we'll stop any running timer. +// +// The IGMP header is only 8 bytes long, and so should always fit in exactly +// one IP rcv buffer. We check this to make sure, and if it takes multiple +// buffers we discard it. +// +// Entry: NTE - Pointer to NTE on which IGMP message was received. +// Dest - IPAddr of destination (should be a Class D address). +// Src - IPAddr of source +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the +// packet +// IPHdr - Pointer to the IP Header. +// IPHdrLength - Bytes in IPHeader. +// RcvBuf - Pointer to IP receive buffer chain. +// Size - Size in bytes of IGMP message. +// IsBCast - Boolean indicator of whether or not this came in +// as a bcast (should always be true). +// Protocol - Protocol this came in on. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception +IP_STATUS +IGMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo) +{ + IGMPHeader UNALIGNED *IGH; + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + uchar DType; + uint ReportingDelayInHalfSec; + + + CTEAssert(CLASSD_ADDR(Dest)); + CTEAssert(IsBCast); + + // Make sure we're running at least level 2 of IGMP support. + if (IGMPLevel != 2) + return IP_SUCCESS; + + // Discard packets with invalid or broadcast source addresses. + DType = GetAddrType(Src); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) + return IP_SUCCESS; + + // Check the size to make sure it's valid. + if (Size != sizeof(IGMPHeader) || RcvBuf->ipr_size != sizeof(IGMPHeader)) + return IP_SUCCESS; + + // Now get the pointer to the header, and validate the xsum. + IGH = (IGMPHeader UNALIGNED *)RcvBuf->ipr_buffer; + + if (xsum(IGH, sizeof(IGMPHeader)) != 0xffff) { + // Bad checksum, so fail. + return IP_SUCCESS; + } + + // If we sent it, don't process this message. + if (IP_ADDR_EQUAL(Src, LocalAddr)) + return IP_SUCCESS; + + // OK, we may need to process this. See if we are a member of the + // destination group. If we aren't, there's no need to proceed further. + CTEGetLock(&NTE->nte_lock, &Handle); + + if (NTE->nte_flags & NTE_VALID) { + // + // The NTE is valid. Demux on type. + // + switch (IGH->igh_vertype) { + + case IGMP_QUERY: + + // + // If it is an IGMPV1 query, set the timer value for staying in + // igmpv1 mode + // +#ifdef IGMPV2 + if (IGH->igh_rsvd == 0) { + // + // Since for any interface we always get notified with + // same NTE, locking the NTE is fine. We don't have to + // lock the interface structure + // + if (NTE->nte_if->IgmpVersion == IGMPV2) { + NTE->nte_if->IgmpVersion = IGMPV1; + } + NTE->nte_if->IgmpVer1Timeout = VERSION1_ROUTER_TIMEOUT; + ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP; + } + else { + ReportingDelayInHalfSec = IGH->igh_rsvd * 5; //field's unit are in 100ms + } +#else + ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP; +#endif + + // + // This is a query. Walk our list and set a random report timer for + // all those class D addresses that don't already have one running + // (except for the all host's address). + // + for (AddrPtr = NTE->nte_igmplist; AddrPtr != NULL; AddrPtr = AddrPtr->iga_next) { + if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) { + if (AddrPtr->iga_timer == 0) { + AddrPtr->iga_timer = IGMPRandomTicks(ReportingDelayInHalfSec); + } + } + } + + break; + + case IGMP_REPORT_V1: + case IGMP_REPORT_V2: + // + // This is a report. Check it's validity and see if we have a + // report timer running for that address. If we do, stop it. + // Make sure the destination address matches the address in the + // IGMP header. + // + if (IP_ADDR_EQUAL(Dest, IGH->igh_addr)) { + // The addresses match. See if we have a membership in this + // group. + AddrPtr = FindIGMPAddr(NTE, IGH->igh_addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found a matching class D address. Stop the timer. + AddrPtr->iga_timer = 0; + } + } + + break; + + default: + break; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + // + // Pass the packet up to the raw layer if applicable. + // + if (RawPI != NULL) { + if (RawPI->pi_rcv != NULL) { + (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr, + IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo); + } + } + + return IP_SUCCESS; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + return IP_SUCCESS; +} + +//* SendIGMPReport - Send an IGMP report. +// +// Called when we want to send an IGMP report for some reason. For this +// purpose we steal ICMP buffers. What we'll do is get one, fill it in, +// and send it. +// +// Input: Dest - Destination to send to. +// Src - Source to send from. +// +// Returns: Nothing. +// +void +SendIGMPReport(uint ChangeType, uint IgmpVersion, IPAddr Dest, IPAddr Src) +{ + IGMPHeader *IGH; + PNDIS_BUFFER Buffer; + IPOptInfo OptInfo; // Options for this transmit. + IP_STATUS Status; + int ReportType; + + CTEAssert(CLASSD_ADDR(Dest)); + CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)); + + // Make sure we never send a report for the all-hosts mcast address. + if (IP_ADDR_EQUAL(Dest, ALL_HOST_MCAST)) { + DEBUGCHK; + return; + } + // + // If the report to be sent is a "Leave Group" report but we have + // detected an igmp v1 router on this net, do not send the report + // +#ifdef IGMPV2 + if (IgmpVersion == IGMPV1) { + if (ChangeType == IGMP_DELETE) { + return; + } else { +#endif + ReportType = IGMP_REPORT_V1; +#ifdef IGMPV2 + } + } else { + if (ChangeType == IGMP_DELETE) { + ReportType = IGMP_LEAVE; + } else { + ReportType = IGMP_REPORT_V2; + } + } +#endif + + IGH = (IGMPHeader *)GetICMPBuffer(sizeof(IGMPHeader), &Buffer); + if (IGH != NULL) { + // We got the buffer. Fill it in and send it. + IGH->igh_vertype = ReportType; + IGH->igh_rsvd = 0; + IGH->igh_xsum = 0; + IGH->igh_addr = Dest; + IGH->igh_xsum = ~xsum(IGH, sizeof(IGMPHeader)); + + IPInitOptions(&OptInfo); + OptInfo.ioi_ttl = 1; + + Status = IPTransmit(IGMPProtInfo, NULL, Buffer, sizeof(IGMPHeader), + Dest, Src, &OptInfo, NULL, PROT_IGMP); + + if (Status != IP_PENDING) + ICMPSendComplete(NULL, Buffer); + } + +} + +//* IGMPAddrChange - Change the IGMP address list on an NTE. +// +// Called to add or delete an IGMP address. We're given the relevant NTE, +// the address, and the action to be performed. We validate the NTE, the +// address, and the IGMP level, and then attempt to perform the action. +// +// There are a bunch of strange race conditions that can occur during adding/ +// deleting addresses, related to trying to add the same address twice and +// having it fail, or adding and deleting the same address simultaneously. Most +// of these happen because we have to free the lock to call the interface, +// and the call to the interface can fail. To prevent this we serialize all +// access to this routine. Only one thread of execution can go through here +// at a time, all others are blocked. +// +// Input: NTE - NTE with list to be altered. +// Addr - Address affected. +// ChangeType - Type of change - IGMP_ADD, IGMP_DELETE, +// IGMP_DELETE_ALL. +// +// Returns: IP_STATUS of attempt to perform action. +// +IP_STATUS +IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, uint ChangeType) +{ + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + IP_STATUS Status; + Interface *IF; + uint AddrAdded; + IGMPBlockStruct Block; + IGMPBlockStruct *BlockPtr; + uint IgmpVersion; + + // First make sure we're at level 2 of IGMP support. + + if (IGMPLevel != 2) + return IP_BAD_REQ; + + CTEInitBlockStruc(&Block.ibs_block); + + // Make sure we're the only ones in this routine. If someone else is + // already here, block. + + CTEGetLock(&IGMPLock, &Handle); + if (IGMPBlockFlag) { + + // Someone else is already here. Walk down the block list, and + // put ourselves on the end. Then free the lock and block on our + // IGMPBlock structure. + BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next); + while (BlockPtr->ibs_next != NULL) + BlockPtr = BlockPtr->ibs_next; + + Block.ibs_next = NULL; + BlockPtr->ibs_next = &Block; + CTEFreeLock(&IGMPLock, Handle); + CTEBlock(&Block.ibs_block); + } else { + // Noone else here, set the flag so noone else gets in and free the + // lock. + IGMPBlockFlag = 1; + CTEFreeLock(&IGMPLock, Handle); + } + + // Now we're in the routine, and we won't be reentered here by another + // thread of execution. Make sure everything's valid, and figure out + // what to do. + + Status = IP_SUCCESS; + + // Now get the lock on the NTE and make sure it's valid. + CTEGetLock(&NTE->nte_lock, &Handle); + if ((NTE->nte_flags & NTE_VALID) || ChangeType == IGMP_DELETE_ALL) { + // The NTE is valid. Try to find an existing IGMPAddr structure + // that matches the input address. + AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr); + IF = NTE->nte_if; + +#ifdef IGMPV2 + IgmpVersion = IF->IgmpVersion; +#else + IgmpVersion = IGMPV1; +#endif + // Now figure out the action to be performed. + switch (ChangeType) { + + case IGMP_ADD: + + // We're to add this. If AddrPtr is NULL, we'll need to + // allocate memory and link the new IGMP address in. Otherwise + // we can just increment the reference count on the existing + // address structure. + if (AddrPtr == NULL) { + // AddrPtr is NULL, i.e. the address doesn't currently + // exist. Allocate memory for it, then try to add the + // address locally. + + CTEFreeLock(&NTE->nte_lock, Handle); + + // If this is not a class D address, fail the request. + if (!CLASSD_ADDR(Addr)) { + Status = IP_BAD_REQ; + break; + } + + AddrPtr = CTEAllocMem(sizeof(IGMPAddr)); + if (AddrPtr != NULL) { + + // Got memory. Try to add the address locally. + AddrAdded = (*IF->if_addaddr)(IF->if_lcontext, + LLIP_ADDR_MCAST, Addr, 0, NULL); + + // See if we added it succesfully. If we did, fill in + // the stucture and link it in. + + if (AddrAdded) { + + AddrPtr->iga_addr = Addr; + AddrPtr->iga_refcnt = 1; + AddrPtr->iga_timer = 0; + + CTEGetLock(&NTE->nte_lock, &Handle); + AddrPtr->iga_next = NTE->nte_igmplist; + NTE->nte_igmplist = AddrPtr; + CTEFreeLock(&NTE->nte_lock, Handle); + + if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) { + // This isn't the all host address, so send a + // report for it. + AddrPtr->iga_timer = IGMPRandomTicks(MAX_DELAY_TICKS); + SendIGMPReport(ChangeType, IgmpVersion, Addr, + NTE->nte_addr); + } + } else { + // Couldn't add the local address. Free the memory + // and fail the request. + CTEFreeMem(AddrPtr); + Status = IP_NO_RESOURCES; + } + + } else { + Status = IP_NO_RESOURCES; + } + } else { + // Already have this one. Bump his count. + (AddrPtr->iga_refcnt)++; + CTEFreeLock(&NTE->nte_lock, Handle); + } + break; + case IGMP_DELETE: + + // This is a delete request. If we didn't find the requested + // address, fail the request. Otherwise dec his refcnt, and if + // it goes to 0 delete the address locally. + if (AddrPtr != NULL) { + // Have one. We won't let the all-hosts mcast address go + // away, but for other's we'll check to see if it's time + // to delete them. + if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST) && + --(AddrPtr->iga_refcnt) == 0) { + // This one is to be deleted. Pull him from the + // list, and call the lower interface to delete him. + PrevPtr->iga_next = AddrPtr->iga_next; + CTEFreeLock(&NTE->nte_lock, Handle); + + CTEFreeMem(AddrPtr); + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST, + Addr, 0); + // + // Send a report to indicate that we are leaving the + // group + // + +#ifdef IGMPV2 + SendIGMPReport(ChangeType, IgmpVersion, Addr, + NTE->nte_addr); +#endif + } else + CTEFreeLock(&NTE->nte_lock, Handle); + } else { + CTEFreeLock(&NTE->nte_lock, Handle); + Status = IP_BAD_REQ; + } + break; + case IGMP_DELETE_ALL: + // We've been called to delete all of this addresses, + // regardless of their reference count. This should only + // happen when the NTE is going away. + AddrPtr = NTE->nte_igmplist; + NTE->nte_igmplist = NULL; + CTEFreeLock(&NTE->nte_lock, Handle); + + // Walk down the list, deleteing each one. + while (AddrPtr != NULL) { + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST, + AddrPtr->iga_addr, 0); +#ifdef IGMPV2 + if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) { + SendIGMPReport(IGMP_DELETE, IgmpVersion, AddrPtr->iga_addr, NTE->nte_addr); + } +#endif + PrevPtr = AddrPtr; + AddrPtr = AddrPtr->iga_next; + CTEFreeMem(PrevPtr); + } + + // All done. + break; + default: + DEBUGCHK; + break; + } + } else { + // NTE isn't valid. + CTEFreeLock(&NTE->nte_lock, Handle); + Status = IP_BAD_REQ; + } + + + // We finished the request, and Status contains the completion status. + // If there are any pending blocks for this routine, signal the next + // one now. Otherwise clear the block flag. + CTEGetLock(&IGMPLock, &Handle); + if ((BlockPtr = IGMPBlockList) != NULL) { + // Someone is blocking. Pull him from the list and signal him. + IGMPBlockList = BlockPtr->ibs_next; + CTEFreeLock(&IGMPLock, Handle); + + CTESignal(&BlockPtr->ibs_block, IP_SUCCESS); + } else { + // No one blocking, just clear the flag. + IGMPBlockFlag = 0; + CTEFreeLock(&IGMPLock, Handle); + } + + return Status; + +} + +//* IGMPTimer - Handle an IGMP timer event. +// +// This function is called every 500 ms. by IP. If we're at level 2 of +// IGMP functionality we run down the NTE looking for running timers. If +// we find one, we see if it has expired and if so we send an +// IGMP report. +// +// Input: NTE - Pointer to NTE to check. +// +// Returns: Nothing. +// +void +IGMPTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + uint IgmpVersion; + + if (IGMPLevel == 2) { + // We are doing IGMP. Run down the addresses active on this NTE. + CTEGetLock(&NTE->nte_lock, &Handle); + + // + // if we haven't heard any query or report from an igmpv1 router or + // host during timeout period, revert to igmpv2. No need to check + // whether NTE is valid or not + // +#ifdef IGMPV2 + if ((NTE->nte_if->IgmpVer1Timeout != 0) && (--(NTE->nte_if->IgmpVer1Timeout) == 0)) { + NTE->nte_if->IgmpVersion = IGMPV2; + } +#endif + PrevPtr = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next); + AddrPtr = PrevPtr->iga_next; + while (AddrPtr != NULL) { + // We have one. See if it's running. + if (AddrPtr->iga_timer != 0) { + // It's running. See if it's expired. + if (--(AddrPtr->iga_timer) == 0 && NTE->nte_flags & NTE_VALID) { + // It's expired. Increment the ref count so it + // doesn't go away while we're here, and send a report. + AddrPtr->iga_refcnt++; +#ifdef IGMPV2 + IgmpVersion = NTE->nte_if->IgmpVersion; +#else + IgmpVersion = IGMPV1; +#endif + + CTEFreeLock(&NTE->nte_lock, Handle); + + SendIGMPReport(IGMP_ADD, IgmpVersion, AddrPtr->iga_addr, + NTE->nte_addr); + // Now get the lock, and decrement the refcnt. If it goes + // to 0, it's been deleted so we need to free it. + CTEGetLock(&NTE->nte_lock, &Handle); + if (--(AddrPtr->iga_refcnt) == 0) { + // It's been deleted. + PrevPtr->iga_next = AddrPtr->iga_next; + CTEFreeMem(AddrPtr); + AddrPtr = PrevPtr->iga_next; + continue; + } + } + } + // Either the timer isn't running or hasn't fired. Try the next + // one. + PrevPtr = AddrPtr; + AddrPtr = AddrPtr->iga_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + } + +} + +//* InitIGMPForNTE - Called to do per-NTE initialization. +// +// Called when an NTE becomes valid. If we're at level 2, we put the all-host +// mcast on the list and add the address to the interface. +// +// Input: NTE - NTE on which to act. +// +// Returns: Nothing. +// +void +InitIGMPForNTE(NetTableEntry *NTE) +{ + if (IGMPLevel == 2) { + IGMPAddrChange(NTE, ALL_HOST_MCAST, IGMP_ADD); + if (NTE->nte_rtrdiscovery && (NTE->nte_rtrdiscaddr == ALL_ROUTER_MCAST)) { + IGMPAddrChange(NTE, ALL_ROUTER_MCAST, IGMP_ADD); + } + } + if (Seed == 0) { + // No random seed yet. + Seed = (int)NTE->nte_addr; + + // Make sure the inital value is odd, and less than 9 decimal digits. + RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1; + } +} + +//* StopIGMPForNTE - Called to do per-NTE shutdown. +// +// Called when we're shutting down and NTE, and want to stop IGMP on hi, +// +// Input: NTE - NTE on which to act. +// +// Returns: Nothing. +// +void +StopIGMPForNTE(NetTableEntry *NTE) +{ + if (IGMPLevel == 2) { + IGMPAddrChange(NTE, NULL_IP_ADDR, IGMP_DELETE_ALL); + } +} + +#pragma BEGIN_INIT + +//** IGMPInit - Initialize IGMP. +// +// This bit of code initializes IGMP generally. There is also some amount +// of work done on a per-NTE basis that we do when each one is initialized. +// +// Input: Nothing. +/// +// Returns: TRUE if we init, FALSE if we don't. +// +uint +IGMPInit(void) +{ + + if (IGMPLevel != 2) + return TRUE; + + CTEInitLock(&IGMPLock); + IGMPBlockList = NULL; + IGMPBlockFlag = 0; + Seed = 0; + + // We fake things a little bit. We register our receive handler, but + // since we steal buffers from ICMP we register the ICMP send complete + // handler. + IGMPProtInfo = IPRegisterProtocol(PROT_IGMP, IGMPRcv, ICMPSendComplete, + NULL, NULL); + + if (IGMPProtInfo != NULL) + return TRUE; + else + return FALSE; + +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/igmp.h b/private/ntos/tdi/tcpip/ip/igmp.h new file mode 100644 index 000000000..ea7aefce5 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/igmp.h @@ -0,0 +1,42 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IGMP.H - IP multicast definitions. +// +// This file contains definitions related to IP multicast. + +#define PROT_IGMP 2 + +extern uint IGMPLevel; + +// Structure used for local mcast address tracking. +typedef struct IGMPAddr { + struct IGMPAddr *iga_next; + IPAddr iga_addr; + uint iga_refcnt; + uint iga_timer; +} IGMPAddr; + +#define IGMP_ADD 0 +#define IGMP_DELETE 1 +#define IGMP_DELETE_ALL 2 + +#define IGMPV1 2 //IGMP version 1 +#define IGMPV2 3 //IGMP version 2 + +// +// disable for 4.0 sp2 +// +#undef IGMPV2 + + +extern void InitIGMPForNTE(NetTableEntry *NTE); +extern void StopIGMPForNTE(NetTableEntry *NTE); +extern IP_STATUS IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, + uint ChangeType); +extern void IGMPTimer(NetTableEntry *NTE); + + diff --git a/private/ntos/tdi/tcpip/ip/info.c b/private/ntos/tdi/tcpip/ip/info.c new file mode 100644 index 000000000..a866c0cba --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/info.c @@ -0,0 +1,609 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INFO.C - Routines for querying and setting IP information. +// +// This file contains the code for dealing with Query/Set information +// calls. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "ipdef.h" +#include "info.h" +#include "tdi.h" +#include "tdiinfo.h" +#include "llinfo.h" +#include "tdistat.h" +#include "iproute.h" +#include "igmp.h" +#include "ipfilter.h" +#include "iprtdef.h" + +extern Interface *IFList; +extern NetTableEntry *NetTableList; +extern uint LoopIndex; // Index of loopback I/F. +extern uint DefaultTTL; +extern uint NumIF; +extern uint NumNTE; +extern RouteInterface DummyInterface; // Dummy interface. + +EXTERNAL_LOCK(RouteTableLock) + +extern uint RTEReadNext(void *Context, void *Buffer); +extern uint RTValidateContext(void *Context, uint *Valid); +extern uint RTReadNext(void *Context, void *Buffer); + +uint IPInstance; +uint ICMPInstance; + +//* CopyToNdis - Copy a flat buffer to an NDIS_BUFFER chain. +// +// A utility function to copy a flat buffer to an NDIS buffer chain. We +// assume that the NDIS_BUFFER chain is big enough to hold the copy amount; +// in a debug build we'll debugcheck if this isn't true. We return a pointer +// to the buffer where we stopped copying, and an offset into that buffer. +// This is useful for copying in pieces into the chain. +// +// Input: DestBuf - Destination NDIS_BUFFER chain. +// SrcBuf - Src flat buffer. +// Size - Size in bytes to copy. +// StartOffset - Pointer to start of offset into first buffer in +// chain. Filled in on return with the offset to +// copy into next. +// +// Returns: Pointer to next buffer in chain to copy into. +// +PNDIS_BUFFER +CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset) +{ + uint CopySize; + uchar *DestPtr; + uint DestSize; + uint Offset = *StartOffset; + uchar *VirtualAddress; + uint Length; + + CTEAssert(DestBuf != NULL); + CTEAssert(SrcBuf != NULL); + + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + CTEAssert(Length >= Offset); + DestPtr = VirtualAddress + Offset; + DestSize = Length - Offset; + + for (;;) { + CopySize = MIN(Size, DestSize); + CTEMemCopy(DestPtr, SrcBuf, CopySize); + + DestPtr += CopySize; + SrcBuf += CopySize; + + if ((Size -= CopySize) == 0) + break; + + if ((DestSize -= CopySize) == 0) { + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + CTEAssert(DestBuf != NULL); + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + DestPtr = VirtualAddress; + DestSize = Length; + } + } + + *StartOffset = DestPtr - VirtualAddress; + + return DestBuf; + +} + + +//* IPQueryInfo - IP query information handler. +// +// Called by the upper layer when it wants to query information about us. +// We take in an ID, a buffer and length, and a context value, and return +// whatever information we can. +// +// Input: ID - Pointer to ID structure. +// Buffer - Pointer to buffer chain. +// Size - Pointer to size in bytes of buffer. On return, filled +// in with bytes read. +// Context - Pointer to context value. +// +// Returns: TDI_STATUS of attempt to read information. +// +long +IPQueryInfo(TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context) +{ + uint BufferSize = *Size; + uint BytesCopied = 0; + uint Offset = 0; + TDI_STATUS Status; + ushort NTEContext; + uchar InfoBuff[sizeof(IPRouteEntry)]; + IPAddrEntry *AddrEntry; + NetTableEntry *CurrentNTE; + uint Valid, DataLeft; + CTELockHandle Handle; + Interface *LowerIF; + IPInterfaceInfo *IIIPtr; + uint LLID = 0; + uint Entity; + uint Instance; + IPAddr IFAddr; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // See if it's something we might handle. + + if (Entity != CL_NL_ENTITY && Entity != ER_ENTITY) { + // We need to pass this down to the lower layer. Loop through until + // we find one that takes it. If noone does, error out. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_qinfo)(LowerIF->if_lcontext, ID, Buffer, + Size, Context); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // If we get here, noone took it. Return an error. + return TDI_INVALID_REQUEST; + + } + + if ((Entity == CL_NL_ENTITY && Instance != IPInstance) || + Instance != ICMPInstance) + return TDI_INVALID_REQUEST; + + // The request is for us. + *Size = 0; // Set to 0 in case of an error. + + // Make sure it's something we support. + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) { + // He's trying to see what type we are. + if (BufferSize >= sizeof(uint)) { + *(uint *)&InfoBuff[0] = (Entity == CL_NL_ENTITY) ? CL_NL_IP : + ER_ICMP; + (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } else + if (ID->toi_class != INFO_CLASS_PROTOCOL || + ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + // If it's ICMP, just copy the statistics. + if (Entity == ER_ENTITY) { + + // It is ICMP. Make sure the ID is valid. + if (ID->toi_id != ICMP_MIB_STATS_ID) + return TDI_INVALID_PARAMETER; + + // He wants the stats. Copy what we can. + if (BufferSize < sizeof(ICMPSNMPInfo)) + return TDI_BUFFER_TOO_SMALL; + + Buffer = CopyToNdis(Buffer, (uchar *)&ICMPInStats, sizeof(ICMPStats), + &Offset); + (void)CopyToNdis(Buffer, (uchar *)&ICMPOutStats, sizeof(ICMPStats), + &Offset); + + *Size = sizeof(ICMPSNMPInfo); + return TDI_SUCCESS; + } + + // It's not ICMP. We need to figure out what it is, and take the + // appropriate action. + + switch (ID->toi_id) { + + case IP_MIB_STATS_ID: + if (BufferSize < sizeof(IPSNMPInfo)) + return TDI_BUFFER_TOO_SMALL; + IPSInfo.ipsi_numif = NumIF; + IPSInfo.ipsi_numaddr = NumNTE; + IPSInfo.ipsi_defaultttl = DefaultTTL; + IPSInfo.ipsi_forwarding = ForwardPackets ? IP_FORWARDING : + IP_NOT_FORWARDING; + CopyToNdis(Buffer, (uchar *)&IPSInfo, sizeof(IPSNMPInfo), &Offset); + BytesCopied = sizeof(IPSNMPInfo); + Status = TDI_SUCCESS; + break; + case IP_MIB_ADDRTABLE_ENTRY_ID: + // He wants to read the address table. Figure out where we're + // starting from, and if it's valid begin copying from there. + NTEContext = *(ushort *)Context; + CurrentNTE = NetTableList; + + if (NTEContext != 0) { + for (;CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) + if (CurrentNTE->nte_context == NTEContext) + break; + if (CurrentNTE == NULL) + return TDI_INVALID_PARAMETER; + } + + AddrEntry = (IPAddrEntry *)InfoBuff; + for (; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { + if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPAddrEntry)) { + // We have room to copy it. Build the entry, and copy + // it. + if (CurrentNTE->nte_flags & NTE_ACTIVE) { + if (CurrentNTE->nte_flags & NTE_VALID) { + AddrEntry->iae_addr = CurrentNTE->nte_addr; + AddrEntry->iae_mask = CurrentNTE->nte_mask; + } else { + AddrEntry->iae_addr = NULL_IP_ADDR; + AddrEntry->iae_mask = NULL_IP_ADDR; + } + + AddrEntry->iae_index = CurrentNTE->nte_if->if_index; + AddrEntry->iae_bcastaddr = + *(int *)&(CurrentNTE->nte_if->if_bcast) & 1; + AddrEntry->iae_reasmsize = 0xffff; + AddrEntry->iae_context = CurrentNTE->nte_context; + Buffer = CopyToNdis(Buffer, (uchar *)AddrEntry, + sizeof(IPAddrEntry), &Offset); + BytesCopied += sizeof(IPAddrEntry); + } + } else + break; + } + + if (CurrentNTE == NULL) + Status = TDI_SUCCESS; + else { + Status = TDI_BUFFER_OVERFLOW; + **(ushort **)&Context = CurrentNTE->nte_context; + } + + break; + case IP_MIB_RTTABLE_ENTRY_ID: + // Make sure we have a valid context. + CTEGetLock(&RouteTableLock, &Handle); + DataLeft = RTValidateContext(Context, &Valid); + + // If the context is valid, we'll continue trying to read. + if (!Valid) { + CTEFreeLock(&RouteTableLock, Handle); + return TDI_INVALID_PARAMETER; + } + + while (DataLeft) { + // The invariant here is that there is data in the table to + // read. We may or may not have room for it. So DataLeft + // is TRUE, and BufferSize - BytesCopied is the room left + // in the buffer. + if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPRouteEntry)) { + DataLeft = RTReadNext(Context, InfoBuff); + BytesCopied += sizeof(IPRouteEntry); + Buffer = CopyToNdis(Buffer, InfoBuff, sizeof(IPRouteEntry), + &Offset); + } else + break; + + } + + CTEFreeLock(&RouteTableLock, Handle); + Status = (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + break; + case IP_INTFC_INFO_ID: + + IFAddr = *(IPAddr *)Context; + // Loop through the NTE table, looking for a match. + for (CurrentNTE = NetTableList; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { + if ((CurrentNTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(CurrentNTE->nte_addr, IFAddr)) + break; + } + if (CurrentNTE == NULL) { + Status = TDI_INVALID_PARAMETER; + break; + } + + if (BufferSize < offsetof(IPInterfaceInfo, iii_addr)) { + Status = TDI_BUFFER_TOO_SMALL; + break; + } + + // We have the NTE. Get the interface, fill in a structure, + // and we're done. + LowerIF = CurrentNTE->nte_if; + IIIPtr = (IPInterfaceInfo *)InfoBuff; + IIIPtr->iii_flags = LowerIF->if_flags & IF_FLAGS_P2P ? + IP_INTFC_FLAG_P2P : 0; + IIIPtr->iii_mtu = LowerIF->if_mtu; + IIIPtr->iii_speed = LowerIF->if_speed; + IIIPtr->iii_addrlength = LowerIF->if_addrlen; + BytesCopied = offsetof(IPInterfaceInfo, iii_addr); + if (BufferSize >= (offsetof(IPInterfaceInfo, iii_addr) + + LowerIF->if_addrlen)) { + Status = TDI_SUCCESS; + Buffer = CopyToNdis(Buffer, InfoBuff, + offsetof(IPInterfaceInfo, iii_addr), &Offset); + CopyToNdis(Buffer, LowerIF->if_addr, LowerIF->if_addrlen, + &Offset); + BytesCopied += LowerIF->if_addrlen; + } else { + Status = TDI_BUFFER_TOO_SMALL; + } + break; + + default: + return TDI_INVALID_PARAMETER; + break; + } + + *Size = BytesCopied; + return Status; +} + +//* IPSetInfo - IP set information handler. +// +// Called by the upper layer when it wants to set an object, which could +// be a route table entry, an ARP table entry, or something else. +// +// Input: ID - Pointer to ID structure. +// Buffer - Pointer to buffer containing element to set.. +// Size - Pointer to size in bytes of buffer. +// +// Returns: TDI_STATUS of attempt to read information. +// +long +IPSetInfo(TDIObjectID *ID, void *Buffer, uint Size) +{ + uint Entity; + uint Instance; + Interface *LowerIF; + Interface *OutIF; + uint MTU; + IPRouteEntry *IRE; + NetTableEntry *OutNTE, *LocalNTE; + IP_STATUS Status; + IPAddr FirstHop, Dest, NextHop; + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // If it's not for us, pass it down. + if (Entity != CL_NL_ENTITY) { + // We need to pass this down to the lower layer. Loop through until + // we find one that takes it. If noone does, error out. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_setinfo)(LowerIF->if_lcontext, ID, Buffer, + Size); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // If we get here, noone took it. Return an error. + return TDI_INVALID_REQUEST; + } + + if (Instance != IPInstance) + return TDI_INVALID_REQUEST; + + // We're identified as the entity. Make sure the ID is correct. + if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID) { + NetTableEntry *TempNTE; + + // This is an attempt to set a route table entry. Make sure the + // size if correct. + if (Size < sizeof(IPRouteEntry)) + return TDI_INVALID_PARAMETER; + + IRE = (IPRouteEntry *)Buffer; + + OutNTE = NULL; + LocalNTE = NULL; + + Dest = IRE->ire_dest; + NextHop = IRE->ire_nexthop; + + // Make sure that the nexthop is sensible. We don't allow nexthops + // to be broadcast or invalid or loopback addresses. + if (IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR) || IP_LOOPBACK(NextHop) || + CLASSD_ADDR(NextHop) || CLASSE_ADDR(NextHop)) + return TDI_INVALID_PARAMETER; + + // Also make sure that the destination we're routing to is sensible. + // Don't allow routes to be added to Class D or E or loopback + // addresses. + if (IP_LOOPBACK(Dest) || CLASSD_ADDR(Dest) || CLASSE_ADDR(Dest)) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_index == LoopIndex) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_index != INVALID_IF_INDEX) { + + // First thing to do is to find the outgoing NTE for specified + // interface, and also make sure that it matches the destination + // if the destination is one of my addresses. + for (TempNTE = NetTableList; TempNTE != NULL; + TempNTE = TempNTE->nte_next) { + if (OutNTE == NULL && IRE->ire_index == TempNTE->nte_if->if_index) + OutNTE = TempNTE; + if (IP_ADDR_EQUAL(NextHop, TempNTE->nte_addr) && + (TempNTE->nte_flags & NTE_VALID)) + LocalNTE = TempNTE; + + // Don't let a route be set through a broadcast address. + if (IsBCastOnNTE(NextHop, TempNTE) != DEST_LOCAL) + return TDI_INVALID_PARAMETER; + + // Don't let a route to a broadcast address be added or deleted. + if (IsBCastOnNTE(Dest, TempNTE) != DEST_LOCAL) + return TDI_INVALID_PARAMETER; + } + + // At this point OutNTE points to the outgoing NTE, and LocalNTE + // points to the NTE for the local address, if this is a direct route. + // Make sure they point to the same interface, and that the type is + // reasonable. + if (OutNTE == NULL) + return TDI_INVALID_PARAMETER; + + if (LocalNTE != NULL) { + // He's routing straight out a local interface. The interface for + // the local address must match the interface passed in, and the + // type must be DIRECT (if we're adding) or INVALID (if we're + // deleting). + if (LocalNTE->nte_if->if_index != IRE->ire_index) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_type != IRE_TYPE_DIRECT && + IRE->ire_type != IRE_TYPE_INVALID) + return TDI_INVALID_PARAMETER; + + OutNTE = LocalNTE; + } + + + // Figure out what the first hop should be. If he's routing straight + // through a local interface, or the next hop is equal to the + // destination, then the first hop is IPADDR_LOCAL. Otherwise it's the + // address of the gateway. + if (LocalNTE != NULL) + FirstHop = IPADDR_LOCAL; + else + if (IP_ADDR_EQUAL(Dest, NextHop)) + FirstHop = IPADDR_LOCAL; + else + FirstHop = NextHop; + + MTU = OutNTE->nte_mss; + OutIF = OutNTE->nte_if; + + } else { + OutIF = (Interface *)&DummyInterface; + MTU = DummyInterface.ri_if.if_mtu - sizeof(IPHeader); + if (IP_ADDR_EQUAL(Dest, NextHop)) + FirstHop = IPADDR_LOCAL; + else + FirstHop = NextHop; + } + + // We've done the validation. See if he's adding or deleting a route. + if (IRE->ire_type != IRE_TYPE_INVALID) { + // He's adding a route. + Status = AddRoute(Dest, IRE->ire_mask, FirstHop, OutIF, + MTU, IRE->ire_metric1, IRE->ire_proto, + ATYPE_OVERRIDE, IRE->ire_context); + + } else { + // He's deleting a route. + Status = DeleteRoute(Dest, IRE->ire_mask, FirstHop, OutIF); + } + + if (Status == IP_SUCCESS) + return TDI_SUCCESS; + else + if (Status == IP_NO_RESOURCES) + return TDI_NO_RESOURCES; + else + return TDI_INVALID_PARAMETER; + + } else { + if (ID->toi_id == IP_MIB_STATS_ID) { + IPSNMPInfo *Info = (IPSNMPInfo *)Buffer; + + // Setting information about TTL and/or routing. + if (Info->ipsi_defaultttl > 255 || (!RouterConfigured && + Info->ipsi_forwarding == IP_FORWARDING)) { + return TDI_INVALID_PARAMETER; + } + + DefaultTTL = Info->ipsi_defaultttl; + ForwardPackets = Info->ipsi_forwarding == IP_FORWARDING ? TRUE : + FALSE; + + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + +} +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif + +//* IPGetEList - Get the entity list. +// +// Called at init time to get an entity list. We fill our stuff in, and +// then call the interfaces below us to allow them to do the same. +// +// Input: EntityList - Pointer to entity list to be filled in. +// Count - Pointer to number of entries in the list. +// +// Returns Status of attempt to get the info. +// +long +IPGetEList(TDIEntityID *EList, uint *Count) +{ + uint ECount; + uint MyIPBase; + uint MyERBase; + int Status; + uint i; + Interface *LowerIF; + TDIEntityID *EntityList; + + ECount = *Count; + EntityList = EList; + + // Walk down the list, looking for existing CL_NL or ER entities, and + // adjust our base instance accordingly. + + MyIPBase = 0; + MyERBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == CL_NL_ENTITY) + MyIPBase = MAX(MyIPBase, EntityList->tei_instance + 1); + else + if (EntityList->tei_entity == ER_ENTITY) + MyERBase = MAX(MyERBase, EntityList->tei_instance + 1); + } + + // At this point we've figure out our base instance. Save for later use. + IPInstance = MyIPBase; + ICMPInstance = MyERBase; + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. We need one for the ICMP instance, + // and one for the CL_NL instance. + + if ((ECount + 2) > MAX_TDI_ENTITIES) + return TDI_REQ_ABORTED; + + // Now fill it in. + EntityList->tei_entity = CL_NL_ENTITY; + EntityList->tei_instance = IPInstance; + EntityList++; + EntityList->tei_entity = ER_ENTITY; + EntityList->tei_instance = ICMPInstance; + *Count += 2; + + // Loop through the interfaces, querying each of them. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_getelist)(LowerIF->if_lcontext, EList, Count); + if (!Status) + return TDI_BUFFER_TOO_SMALL; + } + + return TDI_SUCCESS; +} + +#ifndef CHICAGO +#pragma END_INIT +#endif diff --git a/private/ntos/tdi/tcpip/ip/info.h b/private/ntos/tdi/tcpip/ip/info.h new file mode 100644 index 000000000..e8de293e0 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/info.h @@ -0,0 +1,22 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#include "ipinfo.h" + +extern IPSNMPInfo IPSInfo; +extern ICMPStats ICMPInStats; +extern ICMPStats ICMPOutStats; + +typedef struct RouteEntryContext { + uint rec_index; + struct RouteTableEntry *rec_rte; +} RouteEntryContext; + +extern long IPQueryInfo(struct TDIObjectID *ID, PNDIS_BUFFER Buffer, + uint *Size, void *Context); +extern long IPSetInfo(struct TDIObjectID *ID, void *Buffer, uint Size); +extern long IPGetEList(struct TDIEntityID *Buffer, uint *Count); + diff --git a/private/ntos/tdi/tcpip/ip/init.c b/private/ntos/tdi/tcpip/ip/init.c new file mode 100644 index 000000000..3f032a50a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/init.c @@ -0,0 +1,3509 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** Init.c - IP VxD init routines. +// +// All C init routines are located in this file. We get +// config. information, allocate structures, and generally get things going. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "llipif.h" +#include "arp.h" +#include "info.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipxmit.h" +#include "igmp.h" +#include "icmp.h" +#include + +#ifdef NT +#include +#include +#endif + + +#define NUM_IP_NONHDR_BUFFERS 50 + +#define DEFAULT_RA_TIMEOUT 60 + +#define DEFAULT_ICMP_BUFFERS 5 + +extern IPConfigInfo *IPGetConfig(void); +extern void IPFreeConfig(IPConfigInfo *); +extern int IsIPBCast(IPAddr, uchar); + +extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle); +extern void CloseIFConfig(NDIS_HANDLE Handle); + +// The IPRcv routine. +extern void IPRcv(void *, void *, uint , uint , NDIS_HANDLE , uint , uint ); +// The transmit complete routine. +extern void IPSendComplete(void *, PNDIS_PACKET , NDIS_STATUS ); +// Status indication routine. +extern void IPStatus(void *, NDIS_STATUS, void *, uint); +// Transfer data complete routine. +extern void IPTDComplete(void *, PNDIS_PACKET , NDIS_STATUS , uint ); + +extern void IPRcvComplete(void); + +extern void ICMPInit(uint); +extern uint IGMPInit(void); +extern void ICMPTimer(NetTableEntry *); +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); +extern void TDUserRcv(void *, PNDIS_PACKET, NDIS_STATUS, uint); +extern void FreeRH(ReassemblyHeader *); +extern PNDIS_PACKET GrowIPPacketList(void); +extern PNDIS_BUFFER FreeIPPacket(PNDIS_PACKET Packet); + +extern ulong GetGMTDelta(void); +extern ulong GetTime(void); +extern ulong GetUnique32BitValue(void); + +extern void NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, + ushort IPContext, PVOID *Handle, PNDIS_STRING ConfigName, uint Added); + +uint IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn); + +extern NDIS_HANDLE BufferPool; +EXTERNAL_LOCK(HeaderLock) +#ifdef NT +extern SLIST_HEADER PacketList; +extern SLIST_HEADER HdrBufList; +#endif + +extern NetTableEntry *LoopNTE; + +extern uchar RouterConfigured; + +//NetTableEntry *NetTable; // Pointer to the net table. + +NetTableEntry *NetTableList; // List of NTEs. +int NumNTE; // Number of NTEs. + +AddrTypeCache ATCache[ATC_SIZE]; +uint ATCIndex; + +uchar RATimeout; // Number of seconds to time out a + // reassembly. +ushort NextNTEContext; // Next NTE context to use. + +#if 0 +DEFINE_LOCK_STRUCTURE(PILock) +#endif + +ProtInfo IPProtInfo[MAX_IP_PROT]; // Protocol information table. +ProtInfo *LastPI; // Last protinfo structure looked at. +int NextPI; // Next PI field to be used. +ProtInfo *RawPI = NULL; // Raw IP protinfo + +ulong TimeStamp; +ulong TSFlag; + +uint DefaultTTL; +uint DefaultTOS; +uchar TrRii = TR_RII_ALL; + +// Interface *IFTable[MAX_IP_NETS]; +Interface *IFList; // List of interfaces active. +Interface *FirstIF; // First 'real' IF. +ulong NumIF; + +#ifdef _PNP_POWER +#define BITS_PER_WORD 32 +ulong IFBitMask[(MAX_TDI_ENTITIES / BITS_PER_WORD) + 1]; +#endif // _PNP_POWER + +IPSNMPInfo IPSInfo; +uint DHCPActivityCount = 0; +uint IGMPLevel; + +#ifdef NT + +#ifndef _PNP_POWER + +extern NameMapping *AdptNameTable; +extern DriverRegMapping *DriverNameTable; + +#endif // _PNP_POWER + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ); + +#else // NT + +#ifndef _PNP_POWER + +extern NameMapping AdptNameTable[]; +extern DriverRegMapping DriverNameTable[]; + +#endif // _PNP_POWER + +#endif // NT + +#ifndef _PNP_POWER +extern uint NumRegDrivers; +uint MaxIPNets = 0; +#endif // _PNP_POWER + +uint InterfaceSize; // Size of a net interface. +NetTableEntry *DHCPNTE = NULL; + + +#ifdef NT + +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +void InitTimestamp(); +int InitNTE(NetTableEntry *NTE); +int InitInterface(NetTableEntry *NTE); +LLIPRegRtn GetLLRegPtr(PNDIS_STRING Name); +LLIPRegRtn FindRegPtr(PNDIS_STRING Name); +uint IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr); +void CleanAdaptTable(); +void OpenAdapters(); +int IPInit(); + +#if 0 // BUGBUG: These can eventually be made init time only. + +#pragma alloc_text(INIT, IPGetInfo) +#pragma alloc_text(INIT, IPTimeout) + +#endif // 0 + +#pragma alloc_text(INIT, InitTimestamp) +#ifndef _PNP_POWER +#pragma alloc_text(INIT, InitNTE) +#pragma alloc_text(INIT, InitInterface) +#endif +#pragma alloc_text(INIT, CleanAdaptTable) +#pragma alloc_text(INIT, OpenAdapters) +#pragma alloc_text(INIT, IPRegisterDriver) +#pragma alloc_text(INIT, GetLLRegPtr) +#pragma alloc_text(INIT, FindRegPtr) +#pragma alloc_text(INIT, IPInit) + + +// +// Pagable code +// +uint +IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask, + ushort *NTEContext, ulong *NTEInstance); + +#pragma alloc_text(PAGE, IPAddDynamicNTE) + +#endif // ALLOC_PRAGMA + +extern PDRIVER_OBJECT IPDriverObject; + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +// +// Debugging macros +// +#if DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#else // DBG + +#define TCPTRACE(many_args) + +#endif // DBG + + +// SetIFContext - Set the context on a particular interface. +// +// A routine to set the filter context on a particular interface. +// +// Input: Index - Interface index of i/f to be set. +// Context - Context to set. +// +// Returns: Status of attempt. +// +IP_STATUS +SetIFContext(uint Index, INTERFACE_CONTEXT *Context) +{ + Interface *IF; + + // Walk the list, looking for a matching index. + for (IF = IFList; IF != NULL; IF = IF->if_next) { + if (IF->if_index == Index) { + IF->if_filtercontext = Context; + break; + } + } + + // If we found one, return success. Otherwise fail. + if (IF != NULL) { + return IP_SUCCESS; + } else { + return IP_GENERAL_FAILURE; + } +} + +// SetFilterPtr - A routine to set the filter pointer. +// +// This routine sets the IP forwarding filter callout. +// +// Input: FilterPtr - Pointer to routine to call when filtering. May +// be NULL. +// +// Returns: IP_SUCCESS. +// +IP_STATUS +SetFilterPtr(IPPacketFilterPtr FilterPtr) +{ + Interface *IF; + + // + // If the pointer is being set to NULL, means filtering is + // being turned off. Remove all the contexts we have + // + + if(FilterPtr == NULL) + { + + for (IF = IFList; IF != NULL; IF = IF->if_next) + { + IF->if_filtercontext = NULL; + } + } + + ForwardFilterPtr = FilterPtr; + + return IP_SUCCESS; +} + +// SetMapRoutePtr - A routine to set the dial on demand callout pointer. +// +// This routine sets the IP dial on demand callout. +// +// Input: MapRoutePtr - Pointer to routine to call when we need to bring +// up a link. May be NULL +// +// Returns: IP_SUCCESS. +// +IP_STATUS +SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr) +{ + DODCallout = MapRoutePtr; + return IP_SUCCESS; +} + +#endif // NT + + +//** SetDHCPNTE +// +// Routine to identify which NTE is currently being DHCP'ed. We take as input +// an nte_context. If the context is less than the max NTE context, we look +// for a matching NTE and if we find him we save a pointer. If we don't we +// fail. If the context > max NTE context we're disabling DHCPing, and +// we NULL out the save pointer. +// +// Input: Context - NTE context value. +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +SetDHCPNTE(uint Context) +{ + CTELockHandle Handle; + NetTableEntry *NTE; + ushort NTEContext; + uint RetCode; + + CTEGetLock(&RouteTableLock, &Handle); + + if (Context <= 0xffff) { + // We're setting the DHCP NTE. Look for one matching the context. + + NTEContext = (ushort)Context; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (NTE != LoopNTE && NTE->nte_context == NTEContext) { + // Found one. Save it and break out. + DHCPNTE = NTE; + break; + } + } + + RetCode = (NTE != NULL); + } else { + // The context is invalid, so we're deleting the DHCP NTE. + DHCPNTE = NULL; + RetCode = TRUE; + } + + CTEFreeLock(&RouteTableLock, Handle); + + return RetCode; +} + + +//** SetDHCPNTE +// +// Routine for upper layers to call to check if the IPContext value passed +// up to a RcvHandler identifies an interface that is currently being +// DHCP'd. +// +// Input: Context - Pointer to an NTE +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +IsDHCPInterface(void *IPContext) +{ +// CTELockHandle Handle; + uint RetCode; + NetTableEntry *NTE = (NetTableEntry *) IPContext; + + +// CTEGetLock(&RouteTableLock, &Handle); + + if (DHCPNTE == NTE) { + RetCode = TRUE; + } + else { + RetCode = FALSE; + } + +// CTEFreeLock(&RouteTableLock, Handle); + + return(RetCode); +} + + +//** CloseNets - Close active nets. +// +// Called when we need to close some lower layer interfaces. +// +// Entry: Nothing +// +// Returns: Nothing +// +void +CloseNets(void) +{ + NetTableEntry *nt; + + for (nt = NetTableList; nt != NULL; nt = nt->nte_next) + (*nt->nte_if->if_close)(nt->nte_if->if_lcontext); // Call close routine for this net. +} + +//** IPRegisterProtocol - Register a protocol with IP. +// +// Called by upper layer software to register a protocol. The UL supplies +// pointers to receive routines and a protocol value to be used on xmits/receives. +// +// Entry: +// Protocol - Protocol value to be returned. +// RcvHandler - Receive handler to be called when frames for Protocol are received. +// XmitHandler - Xmit. complete handler to be called when frames from Protocol are completed. +// StatusHandler - Handler to be called when status indication is to be delivered. +// +// Returns: +// Pointer to ProtInfo, +// +void * +IPRegisterProtocol(uchar Protocol, void *RcvHandler, void *XmitHandler, + void *StatusHandler, void *RcvCmpltHandler) +{ + ProtInfo *PI = (ProtInfo *)NULL; + int i; + int Incr; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + + // First check to see if it's already registered. If it is just replace it. + for (i = 0; i < NextPI; i++) + if (IPProtInfo[i].pi_protocol == Protocol) { + PI = &IPProtInfo[i]; + Incr = 0; + break; + } + + if (PI == (ProtInfo *)NULL) { + if (NextPI >= MAX_IP_PROT) { +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return NULL; + } + + PI = &IPProtInfo[NextPI]; + Incr = 1; + + if (Protocol == PROTOCOL_ANY) { + RawPI = PI; + } + } + + PI->pi_protocol = Protocol; + PI->pi_rcv = RcvHandler; + PI->pi_xmitdone = XmitHandler; + PI->pi_status = StatusHandler; + PI->pi_rcvcmplt = RcvCmpltHandler; + NextPI += Incr; + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + +#ifndef _PNP_POWER +#ifdef SECFLTR + + // + // If this was a registration, call the status routine of each protocol + // to inform it of all existing interfaces. Yes, this is a hack, but + // it will work until PnP is turned on in NT. + // + // It is assumed that none of the upper layer status routines call back + // into IP. + // + // Note that we don't hold any locks here since no one manipulates the + // NTE list in a non-PNP build during the init phase. + // + if (StatusHandler != NULL) { + NetTableEntry *NTE; + NDIS_HANDLE ConfigHandle = NULL; + int i; + + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ( !(IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) && + !(IP_LOOPBACK_ADDR(NTE->nte_addr)) + ) + { + // + // Open a configuration key + // + if (!OpenIFConfig(&(NTE->nte_if->if_configname), &ConfigHandle)) + { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } + + (* ((ULStatusProc) StatusHandler))(IP_HW_STATUS, IP_ADDR_ADDED, + NTE->nte_addr, NULL_IP_ADDR, NULL_IP_ADDR, 0, ConfigHandle ); + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + ConfigHandle = NULL; + } + } + } + } + +#endif // SECFLTR +#endif // _PNP_POWER + + return PI; +} + +//** IPSetMCastAddr - Set/Delete a multicast address. +// +// Called by an upper layer protocol or client to set or delete an IP multicast +// address. +// +// Input: Address - Address to be set/deleted. +// IF - IP Address of interface to set/delete on. +// Action - TRUE if we're setting, FALSE if we're deleting. +// +// Returns: IP_STATUS of set/delete attempt. +// +IP_STATUS +IPSetMCastAddr(IPAddr Address, IPAddr IF, uint Action) +{ + NetTableEntry *LocalNTE; + + // Don't let him do this on the loopback address, since we don't have a + // route table entry for class D address on the loopback interface and + // we don't want a packet with a loopback source address to show up on + // the wire. + if (IP_LOOPBACK_ADDR(IF)) + return IP_BAD_REQ; + + for (LocalNTE = NetTableList; LocalNTE != NULL; + LocalNTE = LocalNTE->nte_next) { + if (LocalNTE != LoopNTE && ((LocalNTE->nte_flags & NTE_VALID) && + (IP_ADDR_EQUAL(IF, NULL_IP_ADDR) || + IP_ADDR_EQUAL(IF, LocalNTE->nte_addr)))) + break; + } + + if (LocalNTE == NULL) { + // Couldn't find a matching NTE. + return IP_BAD_REQ; + } + + return IGMPAddrChange(LocalNTE, Address, Action ? IGMP_ADD : IGMP_DELETE); + + +} + +//** IPGetAddrType - Return the type of a address. +// +// Called by the upper layer to determine the type of a remote address. +// +// Input: Address - The address in question. +// +// Returns: The DEST type of the address. +// +uchar +IPGetAddrType(IPAddr Address) +{ + return GetAddrType(Address); +} + +//** IPGetLocalMTU - Return the MTU for a local address +// +// Called by the upper layer to get the local MTU for a local address. +// +// Input: LocalAddr - Local address in question. +// MTU - Where to return the local MTU. +// +// Returns: TRUE if we found the MTU, FALSE otherwise. +// +uchar +IPGetLocalMTU(IPAddr LocalAddr, ushort *MTU) +{ + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (IP_ADDR_EQUAL(NTE->nte_addr, LocalAddr) && + (NTE->nte_flags & NTE_VALID)) { + *MTU = NTE->nte_mss; + return TRUE; + } + } + + // Special case in case the local address is a loopback address other than + // 127.0.0.1. + if (IP_LOOPBACK_ADDR(LocalAddr)) { + *MTU = LoopNTE->nte_mss; + return TRUE; + } + + return FALSE; + +} + +//** IPUpdateRcvdOptions - Update options for use in replying. +// +// A routine to update options for use in a reply. We reverse any source route options, +// and optionally update the record route option. We also return the index into the +// options of the record route options (if we find one). The options are assumed to be +// correct - no validation is performed on them. We fill in the caller provided +// IPOptInfo with the new option buffer. +// +// Input: Options - Pointer to option info structure with buffer to be reversed. +// NewOptions - Pointer to option info structure to be filled in. +// Src - Source address of datagram that generated the options. +// LocalAddr - Local address responding. If this != NULL_IP_ADDR, then +// record route and timestamp options will be updated with this +// address. +// +// +// Returns: Index into options of record route option, if any. +// +IP_STATUS +IPUpdateRcvdOptions(IPOptInfo *OldOptions, IPOptInfo *NewOptions, IPAddr Src, IPAddr LocalAddr) +{ + uchar Length, Ptr; + uchar i; // Index variable + IPAddr UNALIGNED *LastAddr; // First address in route. + IPAddr UNALIGNED *FirstAddr; // Last address in route. + IPAddr TempAddr; // Temp used in exchange. + uchar *Options, OptLength; + OptIndex Index; // Optindex used by UpdateOptions. + + Options = CTEAllocMem(OptLength = OldOptions->ioi_optlength); + + if (!Options) + return IP_NO_RESOURCES; + + CTEMemCopy(Options, OldOptions->ioi_options, OptLength); + Index.oi_srindex = MAX_OPT_SIZE; + Index.oi_rrindex = MAX_OPT_SIZE; + Index.oi_tsindex = MAX_OPT_SIZE; + + NewOptions->ioi_flags &= ~IP_FLAG_SSRR; + + i = 0; + while(i < OptLength) { + if (Options[i] == IP_OPT_EOL) + break; + + if (Options[i] == IP_OPT_NOP) { + i++; + continue; + } + + Length = Options[i+IP_OPT_LENGTH]; + switch (Options[i]) { + case IP_OPT_SSRR: + NewOptions->ioi_flags |= IP_FLAG_SSRR; + case IP_OPT_LSRR: + // Have a source route. We save the last gateway we came through as + // the new address, reverse the list, shift the list forward one address, + // and set the Src address as the last gateway in the list. + + // First, check for an empty source route. If the SR is empty + // we'll skip most of this. + if (Length != (MIN_RT_PTR - 1)) { + // A non empty source route. + // First reverse the list in place. + Ptr = Options[i+IP_OPT_PTR] - 1 - sizeof(IPAddr); + LastAddr = (IPAddr *)(&Options[i + Ptr]); + FirstAddr = (IPAddr *)(&Options[i + IP_OPT_PTR + 1]); + NewOptions->ioi_addr = *LastAddr; // Save Last address as + // first hop of new route. + while (LastAddr > FirstAddr) { + TempAddr = *LastAddr; + *LastAddr-- = *FirstAddr; + *FirstAddr++ = TempAddr; + } + + // Shift the list forward one address. We'll copy all but + // one IP address. + CTEMemCopy(&Options[i + IP_OPT_PTR + 1], + &Options[i + IP_OPT_PTR + 1 + sizeof(IPAddr)], + Length - (sizeof(IPAddr) + (MIN_RT_PTR -1))); + + // Set source as last address of route. + *(IPAddr UNALIGNED *)(&Options[i + Ptr]) = Src; + } + + Options[i+IP_OPT_PTR] = MIN_RT_PTR; // Set pointer to min legal value. + i += Length; + break; + case IP_OPT_RR: + // Save the index in case LocalAddr is specified. If it isn't specified, + // reset the pointer and zero the option. + Index.oi_rrindex = i; + if (LocalAddr == NULL_IP_ADDR) { + CTEMemSet(&Options[i+MIN_RT_PTR-1], 0, Length - (MIN_RT_PTR-1)); + Options[i+IP_OPT_PTR] = MIN_RT_PTR; + } + i += Length; + break; + case IP_OPT_TS: + Index.oi_tsindex = i; + + // We have a timestamp option. If we're not going to update, reinitialize + // it for next time. For the 'unspecified' options, just zero the buffer. + // For the 'specified' options, we need to zero the timestamps without + // zeroing the specified addresses. + if (LocalAddr == NULL_IP_ADDR) { // Not going to update, reinitialize. + uchar Flags; + + Options[i+IP_OPT_PTR] = MIN_TS_PTR; // Reinitialize pointer. + Flags = Options[i+IP_TS_OVFLAGS] & IP_TS_FLMASK; // Get option type. + Options[i+IP_TS_OVFLAGS] = Flags; // Clear overflow count. + switch (Flags) { + uchar j; + ulong UNALIGNED *TSPtr; + + // The unspecified types. Just clear the buffer. + case TS_REC_TS: + case TS_REC_ADDR: + CTEMemSet(&Options[i+MIN_TS_PTR-1], 0, Length - (MIN_TS_PTR-1)); + break; + + // We have a list of addresses specified. Just clear the timestamps. + case TS_REC_SPEC: + // j starts off as the offset in bytes from start of buffer to + // first timestamp. + j = MIN_TS_PTR-1+sizeof(IPAddr); + // TSPtr points at timestamp. + TSPtr = (ulong UNALIGNED *)&Options[i+j]; + + // Now j is offset of end of timestamp being zeroed. + j += sizeof(ulong); + while (j <= Length) { + *TSPtr++ = 0; + j += sizeof(ulong); + } + break; + default: + break; + } + } + i += Length; + break; + + default: + i += Length; + break; + } + + } + + if (LocalAddr != NULL_IP_ADDR) { + UpdateOptions(Options, &Index, LocalAddr); + } + + NewOptions->ioi_optlength = OptLength; + NewOptions->ioi_options = Options; + return IP_SUCCESS; + +} + +//* ValidRouteOption - Validate a source or record route option. +// +// Called to validate that a user provided source or record route option is good. +// +// Entry: Option - Pointer to option to be checked. +// NumAddr - NumAddr that need to fit in option. +// BufSize - Maximum size of option. +// +// Returns: 1 if option is good, 0 if not. +// +uchar +ValidRouteOption(uchar *Option, uint NumAddr, uint BufSize) +{ + if (Option[IP_OPT_LENGTH] < (3 + (sizeof(IPAddr)*NumAddr)) || + Option[IP_OPT_LENGTH] > BufSize || + ((Option[IP_OPT_LENGTH] - 3) % sizeof(IPAddr))) // Routing options is too small. + return 0; + + if (Option[IP_OPT_PTR] != MIN_RT_PTR) // Pointer isn't correct. + return 0; + + return 1; +} + +//** IPInitOptions - Initialize an option buffer. +// +// Called by an upper layer routine to initialize an option buffer. We fill +// in the default values for TTL, TOS, and flags, and NULL out the options +// buffer and size. +// +// Input: Options - Pointer to IPOptInfo structure. +// +// Returns: Nothing. +// +void +IPInitOptions(IPOptInfo *Options) +{ + Options->ioi_addr = NULL_IP_ADDR; + + Options->ioi_ttl = (uchar)DefaultTTL; + Options->ioi_tos = (uchar)DefaultTOS; + Options->ioi_flags = 0; + + Options->ioi_options = (uchar *)NULL; + Options->ioi_optlength = 0; + +} + +//** IPCopyOptions - Copy the user's options into IP header format. +// +// This routine takes an option buffer supplied by an IP client, validates it, and +// creates an IPOptInfo structure that can be passed to the IP layer for transmission. This +// includes allocating a buffer for the options, munging any source route +// information into the real IP format. +// +// Note that we never lock this structure while we're using it. This may cause transitory +// incosistencies while the structure is being updated if it is in use during the update. +// This shouldn't be a problem - a packet or too might get misrouted, but it should +// straighten itself out quickly. If this is a problem the client should make sure not +// to call this routine while it's in the IPTransmit routine. +// +// Entry: Options - Pointer to buffer of user supplied options. +// Size - Size in bytes of option buffer +// OptInfoPtr - Pointer to IPOptInfo structure to be filled in. +// +// Returns: A status, indicating whether or not the options were valid and copied. +// +IP_STATUS +IPCopyOptions(uchar *Options, uint Size, IPOptInfo *OptInfoPtr) +{ + uchar *TempOptions; // Buffer of options we'll build + uint TempSize; // Size of options. + IP_STATUS TempStatus; // Temporary status + uchar OptSeen = 0; // Indicates which options we've seen. + + + OptInfoPtr->ioi_addr = NULL_IP_ADDR; + + OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR; + + if (Size == 0) { + CTEAssert(FALSE); + OptInfoPtr->ioi_options = (uchar *)NULL; + OptInfoPtr->ioi_optlength = 0; + return IP_SUCCESS; + } + + + // Option size needs to be rounded to multiple of 4. + if ((TempOptions = CTEAllocMem(((Size & 3) ? (Size & ~3) + 4 : Size))) == (uchar *)NULL) + return IP_NO_RESOURCES; // Couldn't get a buffer, return error. + + CTEMemSet(TempOptions, 0, ((Size & 3) ? (Size & ~3) + 4 : Size)); + + // OK, we have a buffer. Loop through the provided buffer, copying options. + TempSize = 0; + TempStatus = IP_PENDING; + while (Size && TempStatus == IP_PENDING) { + uint SRSize; // Size of a source route option. + + switch (*Options) { + case IP_OPT_EOL: + TempStatus = IP_SUCCESS; + break; + case IP_OPT_NOP: + TempOptions[TempSize++] = *Options++; + Size--; + break; + case IP_OPT_SSRR: + if (OptSeen & (OPT_LSRR | OPT_SSRR)) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + OptInfoPtr->ioi_flags |= IP_FLAG_SSRR; + OptSeen |= OPT_SSRR; // Fall through to LSRR code. + case IP_OPT_LSRR: + if ( (*Options == IP_OPT_LSRR) && + (OptSeen & (OPT_LSRR | OPT_SSRR)) + ) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + if (*Options == IP_OPT_LSRR) + OptSeen |= OPT_LSRR; + if (!ValidRouteOption(Options, 2, Size)) { + TempStatus = IP_BAD_OPTION; + break; + } + + // Option is valid. Copy the first hop address to NewAddr, and move all + // of the other addresses forward. + TempOptions[TempSize++] = *Options++; // Copy option type. + SRSize = *Options++; + Size -= SRSize; + SRSize -= sizeof(IPAddr); + TempOptions[TempSize++] = SRSize; + TempOptions[TempSize++] = *Options++; // Copy pointer. + OptInfoPtr->ioi_addr = *(IPAddr UNALIGNED *)Options; + Options += sizeof(IPAddr); // Point to address beyond first hop. + CTEMemCopy(&TempOptions[TempSize], Options, SRSize - 3); + TempSize += (SRSize - 3); + Options += (SRSize - 3); + break; + case IP_OPT_RR: + if (OptSeen & OPT_RR) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + OptSeen |= OPT_RR; + if (!ValidRouteOption(Options, 1, Size)) { + TempStatus = IP_BAD_OPTION; + break; + } + SRSize = Options[IP_OPT_LENGTH]; + CTEMemCopy(&TempOptions[TempSize], Options, SRSize); + TempSize += SRSize; + Options += SRSize; + Size -= SRSize; + break; + case IP_OPT_TS: + { + uchar Overflow, Flags; + + if (OptSeen & OPT_TS) { + TempStatus = IP_BAD_OPTION; // We've already seen a time stamp + break; + } + OptSeen |= OPT_TS; + Flags = Options[IP_TS_OVFLAGS] & IP_TS_FLMASK; + Overflow = (Options[IP_TS_OVFLAGS] & IP_TS_OVMASK) >> 4; + + if (Overflow || (Flags != TS_REC_TS && Flags != TS_REC_ADDR && + Flags != TS_REC_SPEC)) { + TempStatus = IP_BAD_OPTION; // Bad flags or overflow value. + break; + } + + SRSize = Options[IP_OPT_LENGTH]; + if (SRSize > Size || SRSize < 8 || + Options[IP_OPT_PTR] != MIN_TS_PTR) { + TempStatus = IP_BAD_OPTION; // Option size isn't good. + break; + } + CTEMemCopy(&TempOptions[TempSize], Options, SRSize); + TempSize += SRSize; + Options += SRSize; + Size -= SRSize; + } + break; + default: + TempStatus = IP_BAD_OPTION; // Unknown option, error. + break; + } + } + + if (TempStatus == IP_PENDING) // We broke because we hit the end of the buffer. + TempStatus = IP_SUCCESS; // that's OK. + + if (TempStatus != IP_SUCCESS) { // We had some sort of an error. + CTEFreeMem(TempOptions); + return TempStatus; + } + + // Check the option size here to see if it's too big. We check it here at the end + // instead of at the start because the option size may shrink if there are source route + // options, and we don't want to accidentally error out a valid option. + TempSize = (TempSize & 3 ? (TempSize & ~3) + 4 : TempSize); + if (TempSize > MAX_OPT_SIZE) { + CTEFreeMem(TempOptions); + return IP_OPTION_TOO_BIG; + } + OptInfoPtr->ioi_options = TempOptions; + OptInfoPtr->ioi_optlength = TempSize; + + return IP_SUCCESS; + +} + +//** IPFreeOptions - Free options we're done with. +// +// Called by the upper layer when we're done with options. All we need to do is free +// the options. +// +// Input: OptInfoPtr - Pointer to IPOptInfo structure to be freed. +// +// Returns: Status of attempt to free options. +// +IP_STATUS +IPFreeOptions(IPOptInfo *OptInfoPtr) +{ + if (OptInfoPtr->ioi_options) { + // We have options to free. Save the pointer and zero the structure field before + // freeing the memory to try and present race conditions with it's use. + uchar *TempPtr = OptInfoPtr->ioi_options; + + OptInfoPtr->ioi_options = (uchar *)NULL; + CTEFreeMem(TempPtr); + OptInfoPtr->ioi_optlength = 0; + OptInfoPtr->ioi_addr = NULL_IP_ADDR; + OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR; + } + return IP_SUCCESS; +} + + +//BUGBUG - After we're done testing, move BEGIN_INIT up here. + +//** ipgetinfo - Return pointers to our NetInfo structures. +// +// Called by upper layer software during init. time. The caller +// passes a buffer, which we fill in with pointers to NetInfo +// structures. +// +// Entry: +// Buffer - Pointer to buffer to be filled in. +// Size - Size in bytes of buffer. +// +// Returns: +// Status of command. +// +IP_STATUS +IPGetInfo(IPInfo *Buffer, int Size) +{ + if (Size < sizeof(IPInfo)) + return IP_BUF_TOO_SMALL; // Not enough buffer space. + + Buffer->ipi_version = IP_DRIVER_VERSION; + Buffer->ipi_hsize = sizeof(IPHeader); + Buffer->ipi_xmit = IPTransmit; + Buffer->ipi_protreg = IPRegisterProtocol; + Buffer->ipi_openrce = OpenRCE; + Buffer->ipi_closerce = CloseRCE; + Buffer->ipi_getaddrtype = IPGetAddrType; + Buffer->ipi_getlocalmtu = IPGetLocalMTU; + Buffer->ipi_getpinfo = IPGetPInfo; + Buffer->ipi_checkroute = IPCheckRoute; + Buffer->ipi_initopts = IPInitOptions; + Buffer->ipi_updateopts = IPUpdateRcvdOptions; + Buffer->ipi_copyopts = IPCopyOptions; + Buffer->ipi_freeopts = IPFreeOptions; + Buffer->ipi_qinfo = IPQueryInfo; + Buffer->ipi_setinfo = IPSetInfo; + Buffer->ipi_getelist = IPGetEList; + Buffer->ipi_setmcastaddr = IPSetMCastAddr; + Buffer->ipi_invalidsrc = InvalidSourceAddress; + Buffer->ipi_isdhcpinterface = IsDHCPInterface; + + return IP_SUCCESS; + +} + +//** IPTimeout - IP timeout handler. +// +// The timeout routine called periodically to time out various things, such as entries +// being reassembled and ICMP echo requests. +// +// Entry: Timer - Timer being fired. +// Context - Pointer to NTE being time out. +// +// Returns: Nothing. +// +void +IPTimeout(CTEEvent *Timer, void *Context) +{ + NetTableEntry *NTE = STRUCT_OF(NetTableEntry, Timer, nte_timer); + CTELockHandle NTEHandle; + ReassemblyHeader *PrevRH, *CurrentRH, *TempList = (ReassemblyHeader *)NULL; + + ICMPTimer(NTE); + IGMPTimer(NTE); + if (Context) { + CTEGetLock(&NTE->nte_lock, &NTEHandle); + PrevRH = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next); + CurrentRH = PrevRH->rh_next; + while (CurrentRH) { + if (--CurrentRH->rh_ttl == 0) { // This guy timed out. + PrevRH->rh_next = CurrentRH->rh_next; // Take him out. + CurrentRH->rh_next = TempList; // And save him for later. + TempList = CurrentRH; + IPSInfo.ipsi_reasmfails++; + } else + PrevRH = CurrentRH; + + CurrentRH = PrevRH->rh_next; + } + + // We've run the list. If we need to free anything, do it now. This may + // include sending an ICMP message. + CTEFreeLock(&NTE->nte_lock, NTEHandle); + while (TempList) { + CurrentRH = TempList; + TempList = CurrentRH->rh_next; + // If this wasn't sent to a bcast address and we already have the first fragment, + // send a time exceeded message. + if (CurrentRH->rh_headersize != 0) + SendICMPErr(NTE->nte_addr, (IPHeader *)CurrentRH->rh_header, ICMP_TIME_EXCEED, + TTL_IN_REASSEM, 0); + FreeRH(CurrentRH); + } + + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NULL); + } else + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NTE); + +} + +//* IPpSetNTEAddr - Set the IP address of an NTE. +// +// Called by the DHCP client to set or delete the IP address of an NTE. We +// make sure he's specifiying a valid NTE, then mark it up or down as needed, +// notify the upper layers of the change if necessary, and then muck with +// the routing tables. +// +// Input: Context - Context of NTE to alter. +// Addr - IP address to set. +// Mask - Subnet mask for Addr. +// +// Returns: TRUE if we changed the address, FALSE otherwise. +// +IP_STATUS +IPpSetNTEAddr(NetTableEntry *NTE, IPAddr Addr, IPMask Mask, + CTELockHandle *RouteTableHandle, SetAddrControl *ControlBlock, SetAddrRtn Rtn) +{ + Interface *IF; + uint (*CallFunc)(struct RouteTableEntry *, void *, void *); + + IF = NTE->nte_if; + DHCPActivityCount++; + + if (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) { + // We're deleting an address. + if (NTE->nte_flags & NTE_VALID) { + // The address is currently valid. Fix that. + + NTE->nte_flags &= ~NTE_VALID; + + // + // If the old address is in the ATCache, flush it out. + // + FlushATCache(NTE->nte_addr); + + if (--(IF->if_ntecount) == 0) { + // This is the last one, so we'll need to delete relevant + // routes. + CallFunc = DeleteRTEOnIF; + } else + CallFunc = InvalidateRCEOnIF; + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + StopIGMPForNTE(NTE); + + // Now call the upper layers, and tell them that address is + // gone. We really need to do something about locking here. +#ifdef _PNP_POWER + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, FALSE); + +#else // _PNP_POWER + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, NULL, FALSE); + +#endif // _PNP_POWER + + // Call RTWalk to take the appropriate action on the RTEs. + RTWalk(CallFunc, IF, NULL); + + // Delete the route to the address itself. + DeleteRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, + LoopNTE->nte_if); + + // Tell the lower interface this address is gone. + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_LOCAL, NTE->nte_addr, + NULL_IP_ADDR); + + CTEGetLock(&RouteTableLock, RouteTableHandle); + } + + DHCPActivityCount--; + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + return IP_SUCCESS; + } else { + uint Status; + + // We're not deleting, we're setting the address. + if (!(NTE->nte_flags & NTE_VALID)) { + uint index; + + // The address is invalid. Save the info, mark him as valid, + // and add the routes. + NTE->nte_addr = Addr; + NTE->nte_mask = Mask; + NTE->nte_flags |= NTE_VALID; + IF->if_ntecount++; + index = IF->if_index; + + // + // If the new address is in the ATCache, flush it out, otherwise + // TdiOpenAddress may fail. + // + FlushATCache(Addr); + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + if (AddNTERoutes(NTE)) + Status = TRUE; + else + Status = FALSE; + + // Need to tell the lower layer about it. + if (Status) { + Interface *IF = NTE->nte_if; + + ControlBlock->sac_rtn = Rtn; + Status = (*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL, + Addr, Mask, ControlBlock ); + } + + if (Status == FALSE) { + // Couldn't add the routes. Recurively mark this NTE as down. + IPSetNTEAddr(NTE->nte_context, NULL_IP_ADDR, 0, NULL, NULL); + } else { + InitIGMPForNTE(NTE); + + // Now call the upper layers, and tell them that address is + // is here. We really need to do something about locking here. +#ifdef _PNP_POWER + +#ifdef SECFLTR + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, + NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle, + &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, + NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle, + NULL, TRUE); +#endif // SECFLTR + +#else // _PNP_POWER + +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, NULL, TRUE); + +#endif // SECFLTR +#endif // _PNP_POWER + +#ifdef NT + if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(Addr), + net_long(Mask), + index + ); + } +#endif // NT + + if ( (Status != IP_PENDING) && (Rtn != NULL) ) { + (*Rtn)(ControlBlock, IP_SUCCESS); + } + } + + CTEGetLock(&RouteTableLock, RouteTableHandle); + NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY; + NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING; + } else + Status = FALSE; + + DHCPActivityCount--; + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + if (Status) { + return IP_PENDING; + } else { + return IP_GENERAL_FAILURE; + } + } +} + +//* IPSetNTEAddr - Set the IP address of an NTE. +// +// Wrapper routine for IPpSetNTEAddr +// +// Input: Context - Context of NTE to alter. +// Addr - IP address to set. +// Mask - Subnet mask for Addr. +// +// Returns: TRUE if we changed the address, FALSE otherwise. +// +uint +IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn) +{ + CTELockHandle Handle; + uint Status; + NetTableEntry *NTE; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if (NTE->nte_context == Context) + break; + + if (NTE == NULL || NTE == LoopNTE) { + // Can't alter the loopback NTE, or one we didn't find. + CTEFreeLock(&RouteTableLock, Handle); + return IP_GENERAL_FAILURE; + } + + Status = IPpSetNTEAddr(NTE, Addr, Mask, &Handle, ControlBlock, Rtn); + + return(Status); +} + + +#pragma BEGIN_INIT + +extern NetTableEntry *InitLoopback(IPConfigInfo *); + +//** InitTimestamp - Intialize the timestamp for outgoing packets. +// +// Called at initialization time to setup our first timestamp. The timestamp we use +// is the in ms since midnite GMT at which the system started. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +InitTimestamp() +{ + ulong GMTDelta; // Delta in ms from GMT. + ulong Now; // Milliseconds since midnight. + + TimeStamp = 0; + + if ((GMTDelta = GetGMTDelta()) == 0xffffffff) { // Had some sort of error. + TSFlag = 0x80000000; + return; + } + + if ((Now = GetTime()) > (24L*3600L*1000L)) { // Couldn't get time since midnight. + TSFlag = net_long(0x80000000); + return; + } + + TimeStamp = Now + GMTDelta - CTESystemUpTime(); + TSFlag = 0; + +} +#pragma END_INIT + +#ifndef CHICAGO +#pragma BEGIN_INIT +#else +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//** InitNTE - Initialize an NTE. +// +// This routine is called during initialization to initialize an NTE. We +// allocate memory, NDIS resources, etc. +// +// +// Entry: NTE - Pointer to NTE to be initalized. +// +// Returns: 0 if initialization failed, non-zero if it succeeds. +// +int +InitNTE(NetTableEntry *NTE) +{ + Interface *IF; + NetTableEntry *PrevNTE; + + NTE->nte_ralist = NULL; + NTE->nte_echolist = NULL; + + // + // Taken together, the context and instance numbers uniquely identify + // a network entry, even across boots of the system. The instance number + // will have to become dynamic if contexts are ever reused. + // + NTE->nte_context = NextNTEContext++; + NTE->nte_rtrlist = NULL; + NTE->nte_instance = GetUnique32BitValue(); + + // Now link him on the IF chain, and bump the count. + IF = NTE->nte_if; + PrevNTE = STRUCT_OF(NetTableEntry, &IF->if_nte, nte_ifnext); + while (PrevNTE->nte_ifnext != NULL) + PrevNTE = PrevNTE->nte_ifnext; + + PrevNTE->nte_ifnext = NTE; + NTE->nte_ifnext = NULL; + + if (NTE->nte_flags & NTE_VALID) { + IF->if_ntecount++; + } + + CTEInitTimer(&NTE->nte_timer); + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, (void *)NULL); + return TRUE; +} + +//** InitInterface - Initialize with an interface. +// +// Called when we need to initialize with an interface. We set the appropriate NTE +// info, then register our local address and any appropriate broadcast addresses +// with the interface. We assume the NTE being initialized already has an interface +// pointer set up for it. We also allocate at least one TD buffer for use on the interface. +// +// Input: NTE - NTE to initialize with the interface. +// +// Returns: TRUE is we succeeded, FALSE if we fail. +// +int +InitInterface(NetTableEntry *NTE) +{ + IPMask netmask = IPNetMask(NTE->nte_addr); + uchar *TDBuffer; // Pointer to tdbuffer + PNDIS_PACKET Packet; + NDIS_HANDLE TDbpool; // Handle for TD buffer pool. + NDIS_HANDLE TDppool; + PNDIS_BUFFER TDBufDesc; // Buffer descriptor for TDBuffer. + NDIS_STATUS Status; + Interface *IF; // Interface for this NTE. + CTELockHandle Handle; + + + IF = NTE->nte_if; + + CTEAssert(NTE->nte_mss > sizeof(IPHeader)); + CTEAssert(IF->if_mtu > 0); + + NTE->nte_mss = MIN((NTE->nte_mss - sizeof(IPHeader)), IF->if_mtu); + + CTERefillMem(); + + // Allocate resources needed for xfer data calls. The TD buffer has to be as large + // as any frame that can be received, even though our MSS may be smaller, because we + // can't control what might be sent at us. + TDBuffer = CTEAllocMem(IF->if_mtu); + if (TDBuffer == (uchar *)NULL) + return FALSE; + + NdisAllocatePacketPool(&Status, &TDppool, 1, sizeof(TDContext)); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisAllocatePacket(&Status, &Packet, TDppool); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(TDContext)); + + NdisAllocateBufferPool(&Status, &TDbpool, 1); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisAllocateBuffer(&Status,&TDBufDesc, TDbpool, TDBuffer, + (IF->if_mtu + sizeof(IPHeader))); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisChainBufferAtFront(Packet, TDBufDesc); + + ((TDContext *)Packet->ProtocolReserved)->tdc_buffer = TDBuffer; + + + if (NTE->nte_flags & NTE_VALID) { + + // Add our local IP address. + if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL, + NTE->nte_addr, NTE->nte_mask, NULL)) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; // Couldn't add local address. + } + } + + // Set up the broadcast addresses for this interface, iff we're the + // 'primary' NTE on the interface. + if (NTE->nte_flags & NTE_PRIMARY) { + + if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_BCAST, + NTE->nte_if->if_bcast, 0, NULL)) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; // Couldn't add broadcast address. + } + } + + if (IF->if_llipflags & LIP_COPY_FLAG) { + NTE->nte_flags |= NTE_COPY; + } + + CTEGetLock(&IF->if_lock, &Handle); + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link = IF->if_tdpacket; + IF->if_tdpacket = Packet; + CTEFreeLock(&IF->if_lock, Handle); + + return TRUE; +} + +#ifndef _PNP_POWER +//* CleanAdaptTable - Clean up the adapter name table. +// +// +void +CleanAdaptTable() +{ + int i = 0; + + while (AdptNameTable[i].nm_arpinfo != NULL) { + CTEFreeMem(AdptNameTable[i].nm_arpinfo); + CTEFreeString(&AdptNameTable[i].nm_name); + if (AdptNameTable[i].nm_driver.Buffer != NULL) + CTEFreeString(&AdptNameTable[i].nm_driver); + i++; + } +} + + +//* OpenAdapters - Clean up the adapter name table. +// +// Used at the end of initialization. We loop through and 'open' all the adapters. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +OpenAdapters() +{ + int i = 0; + LLIPBindInfo *ABI; + + while ((ABI = AdptNameTable[i++].nm_arpinfo) != NULL) { + (*(ABI->lip_open))(ABI->lip_context); + } +} + + +//* IPRegisterDriver - Called during init time to register a driver. +// +// Called during init time when we have a non-LAN (or non-ARPable) driver +// that wants to register with us. We try to find a free slot in the table +// to register him. +// +// Input: Name - Pointer to the name of the driver to be registered. +// Ptr - Pointer to driver's registration function. +// +// Returns: TRUE if we succeeded, FALSE if we fail. +// +uint +IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr) +{ + uint i; + + CTERefillMem(); + + // First, find a slot for him. + for (i = 0; i < MaxIPNets; i++) { + if (DriverNameTable[i].drm_driver.Buffer == NULL) { + // Found a slot. Try and allocate and copy a string for him. + if (!CTEAllocateString(&DriverNameTable[i].drm_driver, + CTELengthString(Name))) + return FALSE; + // Got the space. Copy the string and the pointer. + CTECopyString(&DriverNameTable[i].drm_driver, Name); + DriverNameTable[i].drm_regptr = Ptr; + NumRegDrivers++; + return TRUE; + } + } + + +} + + +#ifdef NT + +//* GetLLRegPtr - Called during init time to get a lower driver's registration +// routine. +// +// Called during init time to locate the registration function of a +// non-LAN (or non-ARPable) driver. +// +// Input: Name - Pointer to the name of the driver to be registered. +// +// Returns: A pointer to the driver's registration routine or NULL on failure. +// +LLIPRegRtn +GetLLRegPtr(PNDIS_STRING Name) +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + LLIPIF_REGISTRATION_DATA registrationData; + IO_STATUS_BLOCK ioStatusBlock; + PIRP irp; + KEVENT ioctlEvent; +extern POBJECT_TYPE *IoDeviceObjectType; + + + registrationData.RegistrationFunction = NULL; + + KeInitializeEvent(&ioctlEvent, SynchronizationEvent, FALSE); + + status = IoGetDeviceObjectPointer( + Name, + SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, + &fileObject, + &deviceObject + ); + + if (status != STATUS_SUCCESS) { + CTEPrint("IP failed to open the lower layer driver\n"); + return(NULL); + } + + // + // Reference the device object. + // + ObReferenceObject(deviceObject); + + // + // IoGetDeviceObjectPointer put a reference on the file object. + // + ObDereferenceObject(fileObject); + + irp = IoBuildDeviceIoControlRequest( + IOCTL_LLIPIF_REGISTER, + deviceObject, + NULL, // input Buffer + 0, // input buffer length + ®istrationData, + sizeof(LLIPIF_REGISTRATION_DATA), + FALSE, // not an InternalDeviceControl + &ioctlEvent, + &ioStatusBlock + ); + + if (irp == NULL) { + ObDereferenceObject(deviceObject); + return(NULL); + } + + status = IoCallDriver(deviceObject, irp); + + if (status == STATUS_PENDING) { + status = KeWaitForSingleObject( + &ioctlEvent, + Executive, + KernelMode, + FALSE, // not alertable + NULL // no timeout + ); + + } + + ObDereferenceObject(deviceObject); + + if (status != STATUS_SUCCESS) { + return(NULL); + } + + if (registrationData.RegistrationFunction != NULL) { + // + // Cache the driver registration for future reference. + // + IPRegisterDriver(Name, registrationData.RegistrationFunction); + } + + return(registrationData.RegistrationFunction); + +} // GetLLRegPtr + +#endif // NT +#endif // _PNP_POWER + + +#ifndef _PNP_POWER + +//* FindRegPtr - Find a driver's registration routine. +// +// Called during init time when we have a non-LAN (or non-ARPable) driver to +// register with. We take in the driver name, and try to find a registration +// pointer for the driver. +// +// Input: Name - Pointer to the name of the driver to be found. +// +// Returns: Pointer to the registration routine, or NULL if there is none. +// +LLIPRegRtn +FindRegPtr(PNDIS_STRING Name) +{ + uint i; + + for (i = 0; i < NumRegDrivers; i++) { + if (CTEEqualString(&(DriverNameTable[i].drm_driver), Name)) + return (LLIPRegRtn)(DriverNameTable[i].drm_regptr); + } + +#ifdef NT + // + // For NT, we open the lower driver and issue an IOCTL to get a pointer to + // its registration function. We then cache this in the table for future + // reference. + // + return(GetLLRegPtr(Name)); +#else + return NULL; +#endif // NT +} + +#endif // _PNP_POWER + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + +//* FreeNets - Free nets we have allocated. +// +// Called during init time if initialization fails. We walk down our list +// of nets, and free them. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +FreeNets(void) +{ + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + CTEFreeMem(NTE); +} + +#ifdef CHICAGO +#pragma END_INIT +#pragma code_seg("_LTEXT", "LCODE") +#endif + + +#ifdef _PNP_POWER + +extern uint GetGeneralIFConfig(IFGeneralConfig *GConfigInfo, NDIS_HANDLE Handle); +extern IFAddrList *GetIFAddrList(uint *NumAddr, NDIS_HANDLE Handle); + + +#ifdef CHICAGO + +extern void RequestDHCPAddr(ushort context); + +#define MAX_NOTIFY_CLIENTS 8 + +typedef void (*AddrNotifyRtn)(IPAddr Addr, IPMask Mask, void *Context, + ushort IPContext, uint Added); + +AddrNotifyRtn AddrNotifyTable[MAX_NOTIFY_CLIENTS]; + +typedef void (*InterfaceNotifyRtn)(ushort Context, uint Added); + +InterfaceNotifyRtn InterfaceNotifyTable[MAX_NOTIFY_CLIENTS]; + +//* RegisterAddrNotify - Register an address notify routine. +// +// A routine called to register an address notify routine. +// +// Input: Rtn - Routine to register. +// Register - True to register, False to deregister. +// +// Returns: TRUE if we succeed, FALSE if we don't/ +// +uint +RegisterAddrNotify(AddrNotifyRtn Rtn, uint Register) +{ + uint i; + AddrNotifyRtn NewRtn, OldRtn; + + if (Register) { + NewRtn = Rtn; + OldRtn = NULL; + } else { + NewRtn = NULL; + OldRtn = Rtn; + } + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (AddrNotifyTable[i] == OldRtn) { + AddrNotifyTable[i] = NewRtn; + return TRUE; + } + } + + return FALSE; +} + + +//* NotifyInterfaceChange - Notify clients of a change in an interface. +// +// Called when we want to notify registered clients that an interface has come +// or gone. We loop through our InterfaceNotify table, calling each one. +// +// Input: Context - Context for interface that has changed. +// Added - True if the interface is coming, False if it's +// going. +// +// Returns: Nothing. +// +void +NotifyInterfaceChange(ushort IPContext, uint Added) +{ + uint i; + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (InterfaceNotifyTable[i] != NULL) + (*(InterfaceNotifyTable[i]))(IPContext, Added); + } +} + +//* RegisterInterfaceNotify - Register an interface notify routine. +// +// A routine called to register an interface notify routine. +// +// Input: Rtn - Routine to register. +// Register - True to register, False to deregister. +// +// Returns: TRUE if we succeed, FALSE if we don't/ +// +uint +RegisterInterfaceNotify(InterfaceNotifyRtn Rtn, uint Register) +{ + uint i; + InterfaceNotifyRtn NewRtn, OldRtn; + + if (Register) { + NewRtn = Rtn; + OldRtn = NULL; + } else { + NewRtn = NULL; + OldRtn = Rtn; + } + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (InterfaceNotifyTable[i] == OldRtn) { + InterfaceNotifyTable[i] = NewRtn; + return TRUE; + } + } + + return FALSE; +} + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We loop through our AddrNotify table, calling each one. +// +// Input: Addr - Addr that has changed. +// Mask - Mask that has changed. +// Context - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) +{ + uint i; + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (AddrNotifyTable[i] != NULL) + (*(AddrNotifyTable[i]))(Addr, Mask, Context, IPContext, Added); + } +} + + +#else // CHICAGO + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We call TDI to perform this function. +// +// Input: Addr - Addr that has changed. +// Mask - Mask that has changed. +// Context - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) +{ + uchar Address[sizeof(TA_ADDRESS) + sizeof(TDI_ADDRESS_IP)]; + PTA_ADDRESS AddressPtr; + PTDI_ADDRESS_IP IPAddressPtr; + NTSTATUS Status; + +#ifdef SECFLTR + + IP_STATUS StatusType; + NDIS_HANDLE ConfigHandle = NULL; + int i; + ULStatusProc StatProc; + +#endif // SECFLTR + + + AddressPtr = (PTA_ADDRESS)Address; + + AddressPtr->AddressLength = sizeof(TDI_ADDRESS_IP); + AddressPtr->AddressType = TDI_ADDRESS_TYPE_IP; + + IPAddressPtr = (PTDI_ADDRESS_IP)AddressPtr->Address; + + CTEMemSet(IPAddressPtr, 0, sizeof(TDI_ADDRESS_IP)); + + IPAddressPtr->in_addr = Addr; + +#ifdef SECFLTR + + // + // Call the status entrypoint of the transports so they can + // adjust their security filters. + // + if (Added) { + StatusType = IP_ADDR_ADDED; + + // + // Open a configuration key + // + if (!OpenIFConfig(ConfigName, &ConfigHandle)) { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } + } + else { + StatusType = IP_ADDR_DELETED; + } + + for ( i = 0; i < NextPI; i++) { + StatProc = IPProtInfo[i].pi_status; + if (StatProc != NULL) + (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR, + NULL_IP_ADDR, 0, ConfigHandle ); + } + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + } + +#endif // SECFLTR + + // + // Notify any interested parties via TDI. The transports all register + // for this notification as well. + // + if (Added) { + Status = TdiRegisterNetAddress(AddressPtr, Handle); + if (Status != STATUS_SUCCESS) { + *Handle = NULL; + } + } else { + if (*Handle != NULL) { + TdiDeregisterNetAddress(*Handle); + *Handle = NULL; + } + } + +} + +#endif // CHICAGO + + +//* IPAddNTE - Add a new NTE to an interface +// +// Called to create a new network entry on an interface. +// +// Input: GConfigInfo - Configuration information for the interface +// PNPContext - The PNP context value associated with the interface +// RegRtn - Routine to call to register with ARP. +// BindInfo - Pointer to NDIS bind information. +// IF - The interface on which to create the NTE. +// NewAddr - The address of the new NTE. +// NewMask - The subnet mask for the new NTE. +// IsPrimary - TRUE if this NTE is the primary one on the interface +// IsDynamic - TRUE if this NTE is being created on an +// existing interface instead of a new one. +// +// Returns: A pointer to the new NTE if the operation succeeds. +// NULL if the operation fails. +// +NetTableEntry * +IPAddNTE(IFGeneralConfig *GConfigInfo, void * PNPContext, LLIPRegRtn RegRtn, + LLIPBindInfo *BindInfo, Interface *IF, IPAddr NewAddr, IPMask NewMask, + uint IsPrimary, uint IsDynamic) +{ + NetTableEntry *NTE, *PrevNTE; + CTELockHandle Handle; + + + // If the address is invalid we're done. Fail the request. + if (CLASSD_ADDR(NewAddr) || CLASSE_ADDR(NewAddr)) { + return NULL; + } + + // See if we have an inactive NTE on the NetTableList. If we do, we'll + // just recycle that. We will pull him out of the list. This is not + // strictly MP safe, since other people could be walking the list while + // we're doing this without holding a lock, but it should be harmless. + // The removed NTE is marked as invalid, and his next pointer will + // be nulled, so anyone walking the list might hit the end too soon, + // but that's all. The memory is never freed, and the next pointer is + // never pointed at freed memory. + + CTEGetLock(&RouteTableLock, &Handle); + + PrevNTE = STRUCT_OF(NetTableEntry, &NetTableList, nte_next); + for (NTE = NetTableList; NTE != NULL; PrevNTE = NTE, NTE = NTE->nte_next) + if (!(NTE->nte_flags & NTE_ACTIVE)) { + PrevNTE->nte_next = NTE->nte_next; + NTE->nte_next = NULL; + NumNTE--; + break; + } + + CTEFreeLock(&RouteTableLock, Handle); + + // See if we got one. + if (NTE == NULL) { + // Didn't get one. Try to allocate one. + NTE = CTEAllocMem(sizeof(NetTableEntry)); + if (NTE == NULL) + return NULL; + CTEMemSet(NTE, 0, sizeof(NetTableEntry)); + } + + // Initialize the address and mask stuff + NTE->nte_addr = NewAddr; + NTE->nte_mask = NewMask; + NTE->nte_mss = MAX(GConfigInfo->igc_mtu, 68); + NTE->nte_rtrdiscaddr = GConfigInfo->igc_rtrdiscaddr; + NTE->nte_rtrdiscstate = NTE_RTRDISC_UNINIT; + NTE->nte_rtrdisccount = 0; + NTE->nte_rtrdiscovery = (uchar)GConfigInfo->igc_rtrdiscovery; + NTE->nte_rtrlist = NULL; + NTE->nte_pnpcontext = PNPContext; + NTE->nte_if = IF; + NTE->nte_flags = NTE_ACTIVE; + + // + // If the new address is in the ATCache, flush it out, otherwise + // TdiOpenAddress may fail. + // + FlushATCache(NewAddr); + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + NTE->nte_flags |= NTE_VALID; + NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY; + NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING; + } + + if (IsDynamic) { + NTE->nte_flags |= NTE_DYNAMIC; + } + + NTE->nte_ralist = NULL; + NTE->nte_echolist = NULL; + NTE->nte_icmpseq = 0; + NTE->nte_igmplist = NULL; + CTEInitLock(&NTE->nte_lock); + CTEInitTimer(&NTE->nte_timer); + + if (IsPrimary) { + // + // This is the first (primary) NTE on the interface. + // + NTE->nte_flags |= NTE_PRIMARY; + + // Pass our information to the underlying code. + if (!(*RegRtn)(&(IF->if_configname), NTE, IPRcv, IPSendComplete, + IPStatus, IPTDComplete, IPRcvComplete, BindInfo, + IF->if_index)) { + + // Couldn't register. + goto failure; + } + } + + // + // Link the NTE onto the global NTE list. + // + CTEGetLock(&RouteTableLock, &Handle); + + NTE->nte_next = NetTableList; + NetTableList = NTE; + NumNTE++; + + CTEFreeLock(&RouteTableLock, Handle); + + if (!InitInterface(NTE)) { + goto failure; + } + + if (!InitNTE(NTE)) { + goto failure; + } + + if (!InitNTERouting(NTE, GConfigInfo->igc_numgws, GConfigInfo->igc_gw)) { + // Couldn't add the routes for this NTE. Mark him as not valid. + // Probably should log an event here. + if (NTE->nte_flags & NTE_VALID) { + NTE->nte_flags &= ~NTE_VALID; + NTE->nte_if->if_ntecount--; + } + } + +#ifdef NT + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(NTE->nte_addr), + net_long(NTE->nte_mask), + NTE->nte_if->if_index + ); + } + +#endif // NT + + return(NTE); + +failure: + + // + // BUGBUG - what should we do with the NTE here???? + // + + return(NULL); +} + + +//* IPAddDynamicNTE - Add a new "dynamic" NTE to an existing interface +// +// Called to dynamically create a new network entry on an existing interface. +// This entry was not configured when the interaface was originally created +// and will not persist if the interface is unbound. +// +// Input: InterfaceContext - The context value which identifies the +// interface on which to create the NTE. +// NewAddr - The address of the new NTE. +// NewMask - The subnet mask for the new NTE. +// +// Output: NTEContext - The context identifying the new NTE. +// NTEInstance - The instance number which (reasonably) uniquely +// identifies this NTE in time. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask, + ushort *NTEContext, ulong *NTEInstance) +{ + IFGeneralConfig GConfigInfo; // General config info structure. + NDIS_HANDLE Handle; // Configuration handle. + NetTableEntry *NTE; + Interface *IF; + ushort MTU; + uint Flags = 0; + + +#ifdef NT + PAGED_CODE(); +#endif + + for (IF = IFList; IF != NULL; IF = IF->if_next) { + if (IF->if_index == InterfaceContext) { + break; + } + } + + //* Try to get the network configuration information. + if (!OpenIFConfig(&(IF->if_configname), &Handle)) + return FALSE; + + // Try to get our general config information. + if (!GetGeneralIFConfig(&GConfigInfo, Handle)) { + goto failure; + } + + NTE = IPAddNTE( + &GConfigInfo, + NULL, // PNPContext - BUGBUG needed? + NULL, // RegRtn - not needed if not primary + NULL, // BindInfo - not needed if not primary + IF, + NewAddr, + NewMask, + FALSE, // not primary + TRUE // is dynamic + ); + + if (NTE == NULL) { + goto failure; + } + + CloseIFConfig(Handle); + + // + // Notify upper layers of the new address. + // +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE); + +#endif // SECFLTR + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + InitIGMPForNTE(NTE); + } + else { +#ifdef CHICAGO + // Call DHCP to get an address for this guy. + + // + // BUGBUG (mikemas 8/28/96) + // we may not always want to do this! + // + RequestDHCPAddr(NTE->nte_context); +#endif + } + + // + // Fill in the out parameter value. + // + *NTEContext = NTE->nte_context; + *NTEInstance = NTE->nte_instance; + + return(TRUE); + +failure: + + CloseIFConfig(Handle); + + return(IP_GENERAL_FAILURE); +} + + +//* IPAddInterface - Add an interface. +// +// Called when someone has an interface they want us to add. We read our +// configuration information, and see if we have it listed. If we do, +// we'll try to allocate memory for the structures we need. Then we'll +// call back to the guy who called us to get things going. Finally, we'll +// see if we have an address that needs to be DHCP'ed. +// +// Input: ConfigName - Name of config info we're to read. +// Context - Context to pass to i/f on calls. +// RegRtn - Routine to call to register. +// BindInfo - Pointer to bind information. +// +// Returns: Status of attempt to add the interface. +// +IP_STATUS +IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context, + LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo) +{ + IFGeneralConfig GConfigInfo; // General config info structure. + IFAddrList *AddrList; // List of addresses for this I/F. + uint NumAddr; // Number of IP addresses on this + // interface + NetTableEntry *NTE; // Current NTE being initialized. + uint i; // Index variable. + uint IndexMask; // Mask for searching IFBitMask. + Interface *IF; // Interface being added. + NDIS_HANDLE Handle; // Configuration handle. + NetTableEntry *PrimaryNTE; // The primary NTE for this I/F. + uint IFIndex; // Index to be assigned to this I/F. + NetTableEntry *LastNTE; // Last NTE created. + + + CTERefillMem(); + + PrimaryNTE = NULL; + AddrList = NULL; + IF = NULL; + LastNTE = NULL; + + //* First, try to get the network configuration information. + if (!OpenIFConfig(ConfigName, &Handle)) + return IP_GENERAL_FAILURE; // Couldn't get IFConfig. + + // Try to get our general config information. + if (!GetGeneralIFConfig(&GConfigInfo, Handle)) { + goto failure; + } + + // We got the general config info. Now allocate an interface. + IF = CTEAllocMem(InterfaceSize + ConfigName->MaximumLength); + + if (IF == NULL) { + goto failure; + } + + CTEMemSet(IF, 0, InterfaceSize); + CTEInitLock(&IF->if_lock); + + // Initialize the broadcast we'll use. + if (GConfigInfo.igc_zerobcast) + IF->if_bcast = IP_ZERO_BCST; + else + IF->if_bcast = IP_LOCAL_BCST; + + if (RouterConfigured) { + RouteInterface *RtIF = (RouteInterface *)IF; + + + RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_running = FALSE; + RtIF->ri_q.rsq_pending = 0; + RtIF->ri_q.rsq_maxpending = GConfigInfo.igc_maxpending; + RtIF->ri_q.rsq_qlength = 0; + CTEInitLock(&RtIF->ri_q.rsq_lock); + } + + IF->if_xmit = BindInfo->lip_transmit; + IF->if_transfer = BindInfo->lip_transfer; + IF->if_close = BindInfo->lip_close; + IF->if_invalidate = BindInfo->lip_invalidate; + IF->if_lcontext = BindInfo->lip_context; + IF->if_addaddr = BindInfo->lip_addaddr; + IF->if_deladdr = BindInfo->lip_deladdr; + IF->if_qinfo = BindInfo->lip_qinfo; + IF->if_setinfo = BindInfo->lip_setinfo; + IF->if_getelist = BindInfo->lip_getelist; + IF->if_tdpacket = NULL; + CTEAssert(BindInfo->lip_mss > sizeof(IPHeader)); + IF->if_mtu = BindInfo->lip_mss - sizeof(IPHeader); + IF->if_speed = BindInfo->lip_speed; + IF->if_flags = BindInfo->lip_flags & LIP_P2P_FLAG ? IF_FLAGS_P2P : 0; + IF->if_addrlen = BindInfo->lip_addrlen; + IF->if_addr = BindInfo->lip_addr; + IF->if_pnpcontext = PNPContext; + IF->if_llipflags = BindInfo->lip_flags; + + // Initialize the reference count to 1, for the open. + IF->if_refcount = 1; + +#ifdef IGMPV2 + IF->IgmpVersion = IGMPV2; +#else + IF->IgmpVersion = IGMPV1; +#endif + + + // + // No need to do the following since IF structure is inited to 0 through + // memset above + // + // IF->IgmpVer1Timeout = 0; + + // + // Copy the config string for use later when DHCP enables an address + // on this interface or when an NTE is added dynamically. + // + IF->if_configname.Buffer = (PVOID) (((uchar *)IF) + InterfaceSize); + IF->if_configname.Length = 0; + IF->if_configname.MaximumLength = ConfigName->MaximumLength; + + CTECopyString( + &(IF->if_configname), + ConfigName + ); + + // Find out how many addresses we have, and get the address list. + AddrList = GetIFAddrList(&NumAddr, Handle); + + if (AddrList == NULL) { + CTEFreeMem(IF); + goto failure; + } + + // + //Link this interface onto the global interface list + // + IF->if_next = IFList; + IFList = IF; + + if (FirstIF == NULL) + FirstIF = IF; + + NumIF++; + IndexMask = 1; + + for (i = 0; i < MAX_TDI_ENTITIES; i++) { + if ((IFBitMask[i/BITS_PER_WORD] & IndexMask) == 0) { + IFIndex = i+ 1; + IFBitMask[i/BITS_PER_WORD] |= IndexMask; + break; + } + if (((i+1) % BITS_PER_WORD) == 0) { + IndexMask = 1; + } else { + IndexMask = IndexMask << 1; + } + } + + if (i == MAX_TDI_ENTITIES) { + // Too many interfaces bound. + goto failure; + } + + IF->if_index = IFIndex; + + // Now loop through, initializing each NTE as we go. We don't hold any + // locks while we do this, since NDIS won't reenter us here and no one + // else manipulates the NetTableList. + + for (i = 0;i < NumAddr;i++) { + NetTableEntry *PrevNTE; + IPAddr NewAddr; + uint isPrimary; + + if (i == 0) { + isPrimary = TRUE; + } + else { + isPrimary = FALSE; + } + + NTE = IPAddNTE( + &GConfigInfo, + PNPContext, + RegRtn, + BindInfo, + IF, + net_long(AddrList[i].ial_addr), + net_long(AddrList[i].ial_mask), + isPrimary, + FALSE // not dynamic + ); + + if (NTE == NULL) { + goto failure; + } + + if (isPrimary) { + PrimaryNTE = NTE; + +#ifdef NT + + // + // Write the context of the first interface to the registry. + // + if (isPrimary) { + NTSTATUS writeStatus; + ulong context = (ulong) NTE->nte_context; + + writeStatus = SetRegDWORDValue( + Handle, + L"IPInterfaceContext", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 2, + 1, + &(ConfigName->Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContext value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ConfigName->Buffer, + writeStatus + )); + } + } + +#endif // NT + + } + + LastNTE = NTE; + } + +#ifdef NT + + if (LastNTE != NULL) { + + NTSTATUS writeStatus; + ulong context = (ulong) LastNTE->nte_context; + + writeStatus = SetRegDWORDValue( + Handle, + L"IPInterfaceContextMax", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 3, + 1, + &(ConfigName->Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContextMax value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ConfigName->Buffer, + writeStatus + )); + } + } + +#endif // NT + + CloseIFConfig(Handle); + + // We've initialized our NTEs. Now get the adapter open, and go through + // again, calling DHCP if we need to. + + (*(BindInfo->lip_open))(BindInfo->lip_context); + + if (PrimaryNTE != NULL) { +#ifdef CHICAGO + NotifyInterfaceChange(PrimaryNTE->nte_context, TRUE); +#endif + } + + // Now walk through the NTEs we've added, and get addresses for them (or + // tell clients about them). This code assumes that no one else has mucked + // with the list while we're here. + for (i = 0; i < NumAddr; i++, NTE = NTE->nte_next) { + +// +// BUGBUG - Doesn't this send up a notification of zero for a DHCP'd +// address on chicago??? (mikemas, 2/5/96) +// +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE); + +#endif // SECFLTR + + if (IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + // Call DHCP to get an address for this guy. +#ifdef CHICAGO + RequestDHCPAddr(NTE->nte_context); +#endif + } else { + InitIGMPForNTE(NTE); + } + } + + + CTEFreeMem(AddrList); + return IP_SUCCESS; + +failure: + CloseIFConfig(Handle); + + if (AddrList != NULL) + CTEFreeMem(AddrList); + + return IP_GENERAL_FAILURE; +} + +extern uint BCastMinMTU; + + +//* IPDelNTE - Delete an active NTE +// +// Called to delete an active NTE from the system. The RouteTableLock +// must be acquired before calling this routine. It will be freed upon +// return. +// +// Input: NTE - A pointer to the network entry to delete. +// RouteTableHandle - A pointer to the lock handle for the +// route table lock, which the caller has +// acquired. +// +// Returns: Nothing +// +void +IPDelNTE(NetTableEntry *NTE, CTELockHandle *RouteTableHandle) +{ + Interface *IF = NTE->nte_if; + ReassemblyHeader *RH, *RHNext; + EchoControl *EC, *ECNext; + EchoRtn Rtn; + CTELockHandle Handle; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; + uchar *TDBuffer; + + + if (NTE->nte_flags & NTE_VALID) { + (void) IPpSetNTEAddr(NTE, NULL_IP_ADDR, NULL_IP_ADDR, RouteTableHandle, NULL, NULL); + + } else { + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + NotifyAddrChange(NULL_IP_ADDR, NULL_IP_ADDR, + NTE->nte_pnpcontext, NTE->nte_context, + &NTE->nte_addrhandle, NULL, FALSE); + } + + CTEGetLock(&RouteTableLock, RouteTableHandle); + + if (DHCPNTE == NTE) + DHCPNTE = NULL; + + NTE->nte_flags = 0; + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + CTEStopTimer(&NTE->nte_timer); + + CTEGetLock(&NTE->nte_lock, &Handle); + + RH = NTE->nte_ralist; + NTE->nte_ralist = NULL; + EC = NTE->nte_echolist; + NTE->nte_echolist = NULL; + + CTEFreeLock(&NTE->nte_lock, Handle); + + // Free any reassembly resources. + while (RH != NULL) { + RHNext = RH->rh_next; + FreeRH(RH); + RH = RHNext; + } + + // Now free any pending echo requests. + while (EC != NULL) { + ECNext= EC->ec_next; + Rtn = (EchoRtn)EC->ec_rtn; + (*Rtn)(EC, IP_ADDR_DELETED, NULL, 0, NULL); + EC = ECNext; + } + + // + // Free the TD resource allocated for this NTE. + // + CTEGetLock(&(IF->if_lock), &Handle); + + Packet = IF->if_tdpacket; + + if (Packet != NULL) { + + IF->if_tdpacket = + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link; + + CTEFreeLock(&(IF->if_lock), Handle); + + Buffer = Packet->Private.Head; + TDBuffer = NdisBufferVirtualAddress(Buffer); + NdisFreePacketPool(Packet->Private.Pool); + +#ifdef CHICAGO + NdisFreeBufferPool(Buffer->Pool); +#endif + CTEFreeMem(TDBuffer); + } + else { + CTEFreeLock(&(IF->if_lock), Handle); + } + + return; +} + + +//* IPDeleteDynamicNTE - Deletes a "dynamic" NTE. +// +// Called to delete a network entry which was dynamically created on an +// existing interface. +// +// Input: NTEContext - The context value identifying the NTE to delete. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPDeleteDynamicNTE(ushort NTEContext) +{ + NetTableEntry *NTE; + Interface *IF; + CTELockHandle Handle; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ( (NTE->nte_context == NTEContext) && + (NTE->nte_flags & NTE_DYNAMIC) && + (NTE->nte_flags & NTE_ACTIVE) + ) + { + CTEAssert(NTE != LoopNTE); + CTEAssert(!(NTE->nte_flags & NTE_PRIMARY)); + + IPDelNTE(NTE, &Handle); + + // + // Route table lock was freed by IPDelNTE + // + + return(TRUE); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + return(FALSE); +} + + +//* IPGetNTEInfo - Retrieve information about a network entry. +// +// Called to retrieve context information about a network entry. +// +// Input: NTEContext - The context value which identifies the NTE to query. +// +// Output: NTEInstance - The instance number associated with the NTE. +// Address - The address assigned to the NTE. +// SubnetMask - The subnet mask assigned to the NTE. +// NTEFlags - The flag values associated with the NTE. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPGetNTEInfo(ushort NTEContext, ulong *NTEInstance, IPAddr *Address, + IPMask *SubnetMask, ushort *NTEFlags) +{ + NetTableEntry *NTE; + CTELockHandle Handle; + uint retval = FALSE; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ((NTE->nte_context == NTEContext) && + (NTE->nte_flags & NTE_ACTIVE) + ) + { + *NTEInstance = NTE->nte_instance; + + if (NTE->nte_flags & NTE_VALID) { + *Address = NTE->nte_addr; + *SubnetMask = NTE->nte_mask; + } + else { + *Address = NULL_IP_ADDR; + *SubnetMask = NULL_IP_ADDR; + } + + *NTEFlags = NTE->nte_flags; + retval = TRUE; + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + return(retval); +} + + +//* IPDelInterface - Delete an interface. +// +// Called when we need to delete an interface that's gone away. We'll walk +// the NTE list, looking for NTEs that are on the interface that's going +// away. For each of those, we'll invalidate the NTE, delete routes on it, +// and notify the upper layers that it's gone. When that's done we'll pull +// the interface out of the list and free the memory. +// +// Note that this code probably isn't MP safe. We'll need to fix that for +// the port to NT. +// +// Input: Context - Pointer to primary NTE on the interface. +// +// Returns: Nothing. +// +void +IPDelInterface(void *Context) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + NetTableEntry *FoundNTE = NULL; + Interface *IF, *PrevIF; + CTELockHandle Handle; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; + uchar *TDBuffer; + ReassemblyHeader *RH; + EchoControl *EC; + EchoRtn Rtn; + CTEBlockStruc Block; + + IF = NTE->nte_if; + + CTEGetLock(&RouteTableLock, &Handle); + + IF->if_flags |= IF_FLAGS_DELETING; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (NTE->nte_if == IF) { + + if (FoundNTE == NULL) { + FoundNTE = NTE; + } + + // This guy is on the interface, and needs to be deleted. + IPDelNTE(NTE, &Handle); + + CTEGetLock(&RouteTableLock, &Handle); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + // Clear this index from the IFBitMask. + CTEAssert(IFBitMask[(IF->if_index-1)/BITS_PER_WORD] & (1 << ((IF->if_index - 1)%BITS_PER_WORD))); + + IFBitMask[(IF->if_index-1)/BITS_PER_WORD] &= ~(1 << ((IF->if_index - 1)%BITS_PER_WORD)); + + if (FoundNTE != NULL) { +#ifdef CHICAGO + NotifyInterfaceChange(FoundNTE->nte_context, FALSE); +#endif + } + + // + // Free the TD resources on the IF. + // + + while ((Packet = IF->if_tdpacket) != NULL) { + + IF->if_tdpacket = + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link; + + Buffer = Packet->Private.Head; + TDBuffer = NdisBufferVirtualAddress(Buffer); + NdisFreePacketPool(Packet->Private.Pool); + +#ifdef CHICAGO + NdisFreeBufferPool(Buffer->Pool); +#endif + CTEFreeMem(TDBuffer); + } + + // If this was the 'first' IF, set that to NULL and delete the broadcast + // route that goes through him. + if (FirstIF == IF) { + DeleteRoute(IP_LOCAL_BCST, HOST_MASK, IPADDR_LOCAL, + FirstIF); + DeleteRoute(IP_ZERO_BCST, HOST_MASK, IPADDR_LOCAL, + FirstIF); + FirstIF = NULL; + BCastMinMTU = 0xffff; + } + + // OK, we've cleaned up all the routes through this guy. + // Get ready to block waiting for all reference to go + // away, then dereference our reference. After this, go + // ahead and try to block. Mostly likely our reference was + // the last one, so we won't block - we'll wake up immediately. + CTEInitBlockStruc(&Block); + IF->if_block = &Block; + + DerefIF(IF); + + (void)CTEBlock(&Block); + + // OK, we've cleaned up all references, so there shouldn't be + // any more transmits pending through this interface. Close the + // adapter to force synchronization with any receives in process. + + + (*(IF->if_close))(IF->if_lcontext); + + // Now walk the IFList, looking for this guy. When we find him, free him. + PrevIF = STRUCT_OF(Interface, &IFList, if_next); + while (PrevIF->if_next != IF && PrevIF->if_next != NULL) + PrevIF = PrevIF->if_next; + + if (PrevIF->if_next != NULL) { + PrevIF->if_next = IF->if_next; + NumIF--; + CTEFreeMem(IF); + } else + CTEAssert(FALSE); + + // If we've deleted the first interface but still have other valid + // interfaces, we need to create a new FirstIF and read broadcast routes + // through it. NumIF is always at least one because of the loopback + // interface. + if (FirstIF == NULL && NumIF != 1) { + + FirstIF = IFList; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ((NTE->nte_flags & NTE_VALID) && NTE != LoopNTE) { + BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss); + AddRoute(NTE->nte_if->if_bcast, HOST_MASK, IPADDR_LOCAL, + FirstIF, BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, + NULL); + } + } + } + +} + + +#else // _PNP_POWER + + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We call TDI to perform this function. +// +// Input: Addr - Addr that has changed. +// Mask - Ignored - Mask that has changed. +// Context - Ignored - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) + +{ + IP_STATUS StatusType; + NDIS_HANDLE ConfigHandle = NULL; + int i; + ULStatusProc StatProc; + + + if (Added) { + StatusType = IP_ADDR_ADDED; + +#ifdef SECFLTR + // + // Open a configuration key + // + if (!OpenIFConfig(ConfigName, &ConfigHandle)) { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } +#endif // SECFLTR + + } + else { + StatusType = IP_ADDR_DELETED; + } + + for ( i = 0; i < NextPI; i++) { + StatProc = IPProtInfo[i].pi_status; + if (StatProc != NULL) + (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR, + NULL_IP_ADDR, 0, ConfigHandle ); + } + +#ifdef SECFLTR + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + } + +#endif // SECFLTR + +} + + +#endif // _PNP_POWER + + +#pragma BEGIN_INIT + +//** ipinit - Initialize ourselves. +// +// This routine is called during initialization from the OS-specific +// init code. We need to check for the presence of the common xport +// environment first. +// +// +// Entry: Nothing. +// +// Returns: 0 if initialization failed, non-zero if it succeeds. +// +int +IPInit() +{ + IPConfigInfo *ci; // Pointer to our IP configuration info. + int numnets; // Number of nets active. + int i; + uint j; // Counter variables. + NetTableEntry *nt; // Pointer to current NTE. + LLIPBindInfo *ARPInfo; // Info. returned from ARP. + NDIS_STATUS Status; + Interface *NetInterface; // Interface for a particular net. + LLIPRegRtn RegPtr; + NetTableEntry *lastNTE; + + + if (!CTEInitialize()) + return IP_INIT_FAILURE; + + CTERefillMem(); + + if ((ci = IPGetConfig()) == NULL) + return IP_INIT_FAILURE; + +#ifndef _PNP_POWER + MaxIPNets = ci->ici_numnets + 1; +#endif // _PNP_POWER + + for (ATCIndex=0; ATCIndex < ATC_SIZE; ATCIndex++) { + ATCache[ATCIndex].atc_flags = 0; + } + ATCIndex = 0; + + // First, initalize our loopback stuff. + NetTableList = InitLoopback(ci); + if (NetTableList == NULL) + return IP_INIT_FAILURE; + + if (!ARPInit()) { + CTEFreeMem(NetTableList); + return IP_INIT_FAILURE; // Couldn't initialize ARP. + } + + CTERefillMem(); + if (!InitRouting(ci)) { + CTEFreeMem(NetTableList); + return IP_INIT_FAILURE; + } + + RATimeout = DEFAULT_RA_TIMEOUT; +#if 0 + CTEInitLock(&PILock); +#endif + LastPI = IPProtInfo; + + + if (!ci->ici_gateway) + InterfaceSize = sizeof(Interface); + else + InterfaceSize = sizeof(RouteInterface); + + DeadGWDetect = ci->ici_deadgwdetect; + PMTUDiscovery = ci->ici_pmtudiscovery; + IGMPLevel = ci->ici_igmplevel; + DefaultTTL = MIN(ci->ici_ttl, 255); + DefaultTOS = ci->ici_tos & 0xfc; + if (IGMPLevel > 2) + IGMPLevel = 0; + + InitTimestamp(); + +#ifndef _PNP_POWER + numnets = ci->ici_numnets; + + lastNTE = NetTableList; // loopback is only one on the list + CTEAssert(lastNTE != NULL); + CTEAssert(lastNTE->nte_next == NULL); + + // Loop through the config. info, copying the addresses and masks. + for (i = 0; i < numnets; i++) { + + CTERefillMem(); + nt = CTEAllocMem(sizeof(NetTableEntry)); + if (nt == NULL) + continue; + + CTEMemSet(nt, 0, sizeof(NetTableEntry)); + + nt->nte_addr = net_long(ci->ici_netinfo[i].nci_addr); + nt->nte_mask = net_long(ci->ici_netinfo[i].nci_mask); + nt->nte_mss = MAX(ci->ici_netinfo[i].nci_mtu, 68); + nt->nte_flags = (IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR) ? 0 : + NTE_VALID); + nt->nte_flags |= NTE_ACTIVE; + + CTEInitLock(&nt->nte_lock); + // If the address is invalid, skip it. + if (CLASSD_ADDR(nt->nte_addr) || CLASSE_ADDR(nt->nte_addr)) { + CTEFreeMem(nt); + continue; + } + + // See if we're already bound to this adapter. If we are, use the same + // interface. Otherwise assign a new one. We assume that the loopback + // interface is IF 1, so there is one less than NumIF in the table. + for (j = 0; j < NumIF - 1; j++) { + if (CTEEqualString(&(AdptNameTable[j].nm_name), + &(ci->ici_netinfo[i].nci_name))) { + + // Names match. Now check driver/types. + if (((ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) && + (AdptNameTable[j].nm_driver.Buffer == NULL)) || + (CTEEqualString(&(AdptNameTable[j].nm_driver), + &(ci->ici_netinfo[i].nci_driver)))) + break; // Found a match + } + } + + if (j < (NumIF - 1)) { + + // Found a match above, so use that interface. + CTERefillMem(); + nt->nte_if = AdptNameTable[j].nm_interface; + ARPInfo = AdptNameTable[j].nm_arpinfo; + // If the Init of the interface or the NTE fails, we don't want to + // close the interface, because another net is using it. + + if (!InitInterface(nt)) { + CTEFreeMem(nt); + continue; + } + if (!InitNTE(nt)) { + CTEFreeMem(nt); + continue; + } + + } else { // No match, create a new interface + + CTEAssert(NumIF <= MaxIPNets); + + if (NumIF == MaxIPNets) { + continue; // too many adapters + } + + CTERefillMem(); + + ARPInfo = CTEAllocMem(sizeof(LLIPBindInfo)); + + if (ARPInfo == NULL) { + CTEFreeMem(nt); + continue; + } + + NetInterface = CTEAllocMem( + InterfaceSize + + ci->ici_netinfo[i].nci_configname.MaximumLength + ); + + if (!NetInterface) { + CTEFreeMem(ARPInfo); + CTEFreeMem(nt); + continue; + } + + CTEMemSet(NetInterface, 0, InterfaceSize); + + nt->nte_if = NetInterface; + nt->nte_flags |= NTE_PRIMARY; // He is the primary NTE. + + CTEInitLock(&NetInterface->if_lock); + + if (ci->ici_gateway) { + // Hack in the max pending value here. Probably should be + // done in iproute.c, but it's easier to do it here. + + RouteInterface *RtIF; + + RtIF = (RouteInterface *)NetInterface; + RtIF->ri_q.rsq_maxpending = ci->ici_netinfo[i].nci_maxpending; + } + + // If this is a LAN, register with ARP. + if (ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) + RegPtr = ARPRegister; + else + RegPtr = FindRegPtr(&ci->ici_netinfo[i].nci_driver); + + if (RegPtr == NULL || !((*RegPtr)(&ci->ici_netinfo[i].nci_name, + nt, IPRcv, IPSendComplete, IPStatus, IPTDComplete, + IPRcvComplete, ARPInfo, NumIF))) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; // We're hosed, skip this net. + } + else { + + if (ci->ici_netinfo[i].nci_zerobcast) + NetInterface->if_bcast = IP_ZERO_BCST; + else + NetInterface->if_bcast = IP_LOCAL_BCST; + + NetInterface->if_xmit = ARPInfo->lip_transmit; + NetInterface->if_transfer = ARPInfo->lip_transfer; + NetInterface->if_close = ARPInfo->lip_close; + NetInterface->if_invalidate = ARPInfo->lip_invalidate; + NetInterface->if_lcontext = ARPInfo->lip_context; + NetInterface->if_addaddr = ARPInfo->lip_addaddr; + NetInterface->if_deladdr = ARPInfo->lip_deladdr; + NetInterface->if_qinfo = ARPInfo->lip_qinfo; + NetInterface->if_setinfo = ARPInfo->lip_setinfo; + NetInterface->if_getelist = ARPInfo->lip_getelist; + NetInterface->if_tdpacket = NULL; + NetInterface->if_index = ARPInfo->lip_index; + NetInterface->if_mtu = ARPInfo->lip_mss - sizeof(IPHeader); + NetInterface->if_speed = ARPInfo->lip_speed; + NetInterface->if_flags = ARPInfo->lip_flags & LIP_P2P_FLAG ? + IF_FLAGS_P2P : 0; + NetInterface->if_addrlen = ARPInfo->lip_addrlen; + NetInterface->if_addr = ARPInfo->lip_addr; + NetInterface->if_pnpcontext = PNPContext; + NetInterface->if_llipflags = ArpInfo->lip_flags + + NetInterface->if_configname.Buffer = + (PVOID) (((uchar *)NetInterface) + InterfaceSize); + + NetInterface->if_configname.Length = 0; + NetInterface->if_configname.MaximumLength = + ci->ici_netinfo[i].nci_configname.MaximumLength; + + CTECopyString( + &(NetInterface->if_configname), + &(ci->ici_netinfo[i].nci_configname) + ); + + CTERefillMem(); + + if (!InitInterface(nt)) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + if (!InitNTE(nt)) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + CTERefillMem(); + if (!CTEAllocateString(&AdptNameTable[j].nm_name, + CTELengthString(&ci->ici_netinfo[i].nci_name))) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + if (ci->ici_netinfo[i].nci_type != NET_TYPE_LAN) { + if (!CTEAllocateString(&AdptNameTable[j].nm_driver, + CTELengthString(&ci->ici_netinfo[i].nci_driver))) { + CTEFreeString(&AdptNameTable[j].nm_name); + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + CTECopyString(&(AdptNameTable[j].nm_driver), + &(ci->ici_netinfo[i].nci_driver)); + } + + CTECopyString(&(AdptNameTable[j].nm_name), + &(ci->ici_netinfo[i].nci_name)); + AdptNameTable[j].nm_interface = NetInterface; + AdptNameTable[j].nm_arpinfo = ARPInfo; + NetInterface->if_next = IFList; + IFList = NetInterface; + if (FirstIF == NULL) + FirstIF = NetInterface; + NumIF++; + +#ifdef NT + // + // Write the interface context to the registry for DHCP et al + // + if (ci->ici_netinfo[i].nci_reghandle != NULL) { + NTSTATUS writeStatus; + ulong context = (ulong) nt->nte_context; + + writeStatus = SetRegDWORDValue( + ci->ici_netinfo[i].nci_reghandle, + L"IPInterfaceContext", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 2, + 1, + &(ci->ici_netinfo[i].nci_name.Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContext value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ci->ici_netinfo[i].nci_name.Buffer, + writeStatus + )); + } + } +#endif // NT + } + } + + nt->nte_next = NULL; + lastNTE->nte_next = nt; + lastNTE = nt; + NumNTE++; + + if (!InitNTERouting(nt, ci->ici_netinfo[i].nci_numgws, + ci->ici_netinfo[i].nci_gw)) { + // Couldn't add the routes for this NTE. Mark has as not valid. + // Probably should log an event here. + if (nt->nte_flags & NTE_VALID) { + nt->nte_flags &= ~NTE_VALID; + nt->nte_if->if_ntecount--; + } + } + +#ifdef NT + if (!IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(nt->nte_addr), + net_long(nt->nte_mask), + nt->nte_if->if_index + ); + } +#endif // NT + + } + +#endif // ndef PNP_POWER + + if (NumNTE != 0) { // We have an NTE, and loopback initialized. + PNDIS_PACKET Packet; + +#ifdef _PNP_POWER + for (i=0; iici_gateway ? IP_FORWARDING : + IP_NOT_FORWARDING); + IPSInfo.ipsi_defaultttl = DefaultTTL; + IPSInfo.ipsi_reasmtimeout = DEFAULT_RA_TIMEOUT; + + // Allocate our packet pools. + CTEInitLock(&HeaderLock); +#ifdef NT + ExInitializeSListHead(&PacketList); + ExInitializeSListHead(&HdrBufList); +#endif + + + Packet = GrowIPPacketList(); + + if (Packet == NULL) { + CloseNets(); + FreeNets(); + IPFreeConfig(ci); + return IP_INIT_FAILURE; + } + + (void)FreeIPPacket(Packet); + + NdisAllocateBufferPool(&Status, &BufferPool, NUM_IP_NONHDR_BUFFERS); + if (Status != NDIS_STATUS_SUCCESS) { +#ifdef DEBUG + DEBUGCHK; +#endif + } + + CTERefillMem(); + + ICMPInit(DEFAULT_ICMP_BUFFERS); + if (!IGMPInit()) + IGMPLevel = 1; + + // Should check error code, and log an event here if this fails. + CTERefillMem(); + InitGateway(ci); + + IPFreeConfig(ci); + CTERefillMem(); + +#ifndef _PNP_POWER + OpenAdapters(); + CleanAdaptTable(); // Clean up the adapter info we don't need. +#endif + + CTERefillMem(); + + // Loop through, initialize IGMP for each NTE. + for (nt = NetTableList; nt != NULL; nt = nt->nte_next) + InitIGMPForNTE(nt); + + return IP_INIT_SUCCESS; + } + else { + FreeNets(); + IPFreeConfig(ci); + return IP_INIT_FAILURE; // Couldn't initialize anything. + } +} + +#pragma END_INIT + + diff --git a/private/ntos/tdi/tcpip/ip/ipdef.h b/private/ntos/tdi/tcpip/ip/ipdef.h new file mode 100644 index 000000000..9d93d0616 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipdef.h @@ -0,0 +1,371 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ +#include "ipfilter.h" + +//** IPDEF.H - IP private definitions. +// +// This file contains all of the definitions for IP that +// are private to IP, i.e. not visible to outside layers. + +// Internal error codes, not seen by IP uses. +#define IP_OPTION_STRICT (MAX_IP_STATUS+1) + +#define CLASSA_ADDR(a) (( (*((uchar *)&(a))) & 0x80) == 0) +#define CLASSB_ADDR(a) (( (*((uchar *)&(a))) & 0xc0) == 0x80) +#define CLASSC_ADDR(a) (( (*((uchar *)&(a))) & 0xe0) == 0xc0) +#define CLASSE_ADDR(a) ((( (*((uchar *)&(a))) & 0xf0) == 0xf0) && \ + ((a) != 0xffffffff)) + +#define CLASSA_MASK 0x000000ff +#define CLASSB_MASK 0x0000ffff +#define CLASSC_MASK 0x00ffffff +#define CLASSD_MASK 0x000000e0 +#define CLASSE_MASK 0xffffffff + +#define IP_OPT_COPIED 0x80 // Bit indicating options is to be copied. +#define IP_OPT_TYPE 0 +#define IP_OPT_LENGTH 1 +#define IP_OPT_DATA 2 +#define IP_OPT_PTR 2 // Pointer offset, for those options that have it. +#define IP_TS_OVFLAGS 3 // Offset for overflow and flags. +#define IP_TS_FLMASK 0xf // Mask for flags +#define IP_TS_OVMASK 0xf0 // Mask for overflow field. +#define IP_TS_MAXOV 0xf0 // Maximum value for the overflow field. +#define IP_TS_INC 0x10 // Increment used on overflow field. + +#define MIN_RT_PTR 4 +#define MIN_TS_PTR 5 + +#define TS_REC_TS 0 // Record TS option. +#define TS_REC_ADDR 1 // Record TS and address. +#define TS_REC_SPEC 3 // Only specified addresses record. + +#define OPT_SSRR 1 // We've seen a SSRR in this option buffer +#define OPT_LSRR 2 // We've seen a LSRR in this option buffer +#define OPT_RR 4 // We've seen a RR +#define OPT_TS 8 // We've seen a TS. + +#define MAX_OPT_SIZE 40 + +#define ALL_ROUTER_MCAST 0x020000E0 + +// Received option index structure. +struct OptIndex { + uchar oi_srindex; + uchar oi_rrindex; + uchar oi_tsindex; + uchar oi_srtype; +}; /* OptIndex */ + +typedef struct OptIndex OptIndex; + +#define MAX_HDR_SIZE (sizeof(IPHeader) + MAX_OPT_SIZE) + +#define DEFAULT_VERLEN 0x45 // Default version and length. + +#define IP_VERSION 0x40 +#define IP_VER_FLAG 0xF0 + +#define IP_RSVD_FLAG 0x0080 // Reserved. +#define IP_DF_FLAG 0x0040 // 'Don't fragment' flag +#define IP_MF_FLAG 0x0020 // 'More fragments flag' + + +#define IP_OFFSET_MASK ~0x00E0 // Mask for extracting offset field. + +typedef IP_STATUS (*ULRcvProc)(void *, IPAddr, IPAddr, IPAddr, IPAddr, + IPHeader UNALIGNED *, uint, IPRcvBuf *, uint, + uchar, uchar, IPOptInfo *); + +typedef uint (*ULStatusProc)(uchar, IP_STATUS, IPAddr, IPAddr, IPAddr, ulong, void *); + +//* Protocol information structure. These is one of there for each protocol bound +// to an NTE. +struct ProtInfo { + void (*pi_xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine. + ULRcvProc pi_rcv; // Pointer to receive routine. + ULStatusProc pi_status; // Pointer to status handler. + void (*pi_rcvcmplt)(void); // Pointer to recv. cmplt handler. + uchar pi_protocol; // Protocol type. + uchar pi_pad[3]; // Pad to dword +}; /* ProtInfo */ + +typedef struct ProtInfo ProtInfo; + +//* Per-net information. We keep a variety of information for +// each net, including the IP address, subnet mask, and reassembly +// information. + +#define MAX_IP_PROT 5 // ICMP, IGMP, TCP, UDP, & Raw + +struct IPRtrEntry { + struct IPRtrEntry *ire_next; + IPAddr ire_addr; + long ire_preference; + ushort ire_lifetime; + ushort ire_pad; +}; /* IPRtrEntry */ + +typedef struct IPRtrEntry IPRtrEntry; + +struct NetTableEntry { + struct NetTableEntry *nte_next; // Next NTE of I/F. + IPAddr nte_addr; // IP address for this net. + IPMask nte_mask; // Subnet mask for this net. + struct Interface *nte_if; // Pointer to interface for this net. + struct NetTableEntry *nte_ifnext; // Linkage on if chain. + ushort nte_flags; // Flags for NTE. + ushort nte_context; // Context passed to upper layers. + ulong nte_instance; // Unique instance ID for this net + void *nte_pnpcontext; // PNP context. + DEFINE_LOCK_STRUCTURE(nte_lock) + struct ReassemblyHeader *nte_ralist; // Reassembly list. + struct EchoControl *nte_echolist; // List of pending echo control blocks + CTETimer nte_timer; // Timer for this net. + ushort nte_mss; + ushort nte_icmpseq; // ICMP seq. # + struct IGMPAddr *nte_igmplist; // List of mcast addresses. +#ifdef _PNP_POWER + void *nte_addrhandle; // Handle for address registration. +#endif + IPAddr nte_rtrdiscaddr; // Address used for Router Discovery + uchar nte_rtrdiscstate; // state of router solicitations + uchar nte_rtrdisccount; // router solicitation count + uchar nte_rtrdiscovery; + IPRtrEntry *nte_rtrlist; + +}; /* NetTableEntry */ + +typedef struct NetTableEntry NetTableEntry; + +#define NTE_VALID 0x0001 // NTE is valid. +#define NTE_COPY 0x0002 // For NDIS copy lookahead stuff. +#define NTE_PRIMARY 0x0004 // This is the 'primary' NTE on the I/F. +#define NTE_ACTIVE 0x0008 // NTE is active, i.e. interface is valid. +#define NTE_DYNAMIC 0x0010 // NTE is was created dynamically + +#define IP_TIMEOUT 500 + +#define NTE_RTRDISC_UNINIT 0 +#define NTE_RTRDISC_DELAYING 1 +#define NTE_RTRDISC_SOLICITING 2 + +#define MAX_SOLICITATION_DELAY 2 // ticks to delay +#define SOLICITATION_INTERVAL 6 // ticks between solicitations +#define MAX_SOLICITATIONS 3 // number of solicitations + +struct AddrTypeCache { + IPAddr atc_addr; // IP Addr of cache entry + uchar atc_flags; // Valid flag + uchar atc_type; // Addr Type +}; + +typedef struct AddrTypeCache AddrTypeCache; + +#define ATC_SIZE 8 +#define ATC_MASK 7 // mask used to make sure indexes are less than ATC_SIZE + +//* Buffer reference structure. Used by broadcast and fragmentation code to +// track multiple references to a single user buffer. +struct BufferReference { + PNDIS_BUFFER br_buffer; // Pointer to uses buffer. + DEFINE_LOCK_STRUCTURE(br_lock) + int br_refcount; // Count of references to user's buffer. +}; /* BufferReference */ + +typedef struct BufferReference BufferReference; + +// Definitions of flags in pc_flags field +#define PACKET_FLAG_OPTIONS 1 // Set if packet has an options buffer. +#define PACKET_FLAG_IPBUF 2 // Set if packet is composed of IP buffers. +#define PACKET_FLAG_RA 4 // Set if packet is being used for reassembly. +#define PACKET_FLAG_FW 8 // Set if packet is a forwarding packet. +#define PACKET_FLAG_IPHDR 0x10 // Packet uses an IP hdr buffer. + +//* Transfer data packet context. Used when TD'ing a packet - we store information for the +// callback here. +struct TDContext { + struct PCCommon tdc_common; + void *tdc_buffer; // Pointer to buffer containing data. + NetTableEntry *tdc_nte; // NTE to receive this on. + struct RABufDesc *tdc_rbd; // Pointer to RBD, if any. + uchar tdc_dtype; // Destination type of original address. + uchar tdc_hlength; // Length in bytes of header. + uchar tdc_pad[2]; + uchar tdc_header[MAX_HDR_SIZE + 8]; +}; /* TDContext */ + +typedef struct TDContext TDContext; + +//* Information about net interfaces. There can be multiple nets for each interface, +// but there is exactly one interface per net. + +struct Interface { + struct Interface *if_next; // Next interface in chain. + void *if_lcontext; // Link layer context. + NDIS_STATUS (*if_xmit)(void *, PNDIS_PACKET, IPAddr, RouteCacheEntry *); + NDIS_STATUS (*if_transfer)(void *, NDIS_HANDLE, uint, uint, uint, PNDIS_PACKET, + uint *); + void (*if_close)(void *); + void (*if_invalidate)(void *, RouteCacheEntry *); + uint (*if_addaddr)(void *, uint, IPAddr, IPMask, void *); + void (*if_deladdr)(void *, uint, IPAddr, IPMask); + int (*if_qinfo)(void *, struct TDIObjectID *, + PNDIS_BUFFER, uint *, void *); + int (*if_setinfo)(void *, struct TDIObjectID *, void *, + uint); + int (*if_getelist)(void *, void *, uint *); + PNDIS_PACKET if_tdpacket; // Packet used for transferring data. + uint if_index; // Index of this interface. + uint if_ntecount; // Valid NTEs on this interface. + NetTableEntry *if_nte; // Pointer to list of NTE on interface. + IPAddr if_bcast; // Broadcast address for this interface. + uint if_mtu; // True maximum MTU for the interface. + uint if_speed; // Speed in bits/sec of this interface. + uint if_flags; // Flags for this interface. + INTERFACE_CONTEXT if_filtercontext; // Filter context for this i/f. + uint if_addrlen; // Length of i/f addr. + uchar *if_addr; // Pointer to addr. + + uint IgmpVersion; //igmp version active on this interface + uint IgmpVer1Timeout; //Version 1 router present timeout +#ifdef _PNP_POWER + uint if_refcount; // Reference count for this i/f. + CTEBlockStruc *if_block; // Block structure for PnP. + void *if_pnpcontext; // Context to pass to upper layers. +#endif // _PNP_POWER + + uint if_llipflags; // Lower layer flags +#ifdef SECFLTR + NDIS_STRING if_configname; // Name of the i/f config section +#endif //SECFLTR + DEFINE_LOCK_STRUCTURE(if_lock) +}; /* Interface */ + +typedef struct Interface Interface; + +/*NOINC*/ +extern void DerefIF(Interface *IF); +/*INC*/ + +#define IF_FLAGS_P2P 1 // Point to point interface +#define IF_FLAGS_DELETING 2 // Interface is in the process of going + // away. + +// Structure of a reassembly buffer descriptor. Each RBD describes a fragment of the total +// datagram +struct RABufDesc { + IPRcvBuf rbd_buf; // IP receive buffer for this fragment. + ushort rbd_start; // Offset of first byte of this fragment. + ushort rbd_end; // Offset of last byte of this fragment. +}; /* RABufDesc */ + +typedef struct RABufDesc RABufDesc; + +// Reassembly header. The includes the information needed for the lookup, as well as space +// for the received header and a chain of reassembly buffer descriptors. +struct ReassemblyHeader { + struct ReassemblyHeader *rh_next; // Next header in chain. + IPAddr rh_dest; // Destination address of fragment. + IPAddr rh_src; // Source address of fragment. + ushort rh_id; // ID of datagram. + uchar rh_protocol; // Protocol of datagram. + uchar rh_ttl; // Remaining time of datagram. + RABufDesc *rh_rbd; // Chain of RBDs for this datagram. + ushort rh_datasize; // Total size of data. + ushort rh_datarcvd; // Amount of data received so far. + uint rh_headersize; // Size in bytes of header. + uchar rh_header[MAX_HDR_SIZE+8]; // Saved IP header of first fragment. +}; /* ReassemblyHeader */ + +typedef struct ReassemblyHeader ReassemblyHeader; + +// ICMP type and code definitions +#define IP_DEST_UNREACH_BASE IP_DEST_NET_UNREACHABLE + +#define ICMP_REDIRECT 5 // Redirect +#define ADDR_MASK_REQUEST 17 // Address mask request +#define ADDR_MASK_REPLY 18 +#define ICMP_DEST_UNREACH 3 // Destination unreachable +#define ICMP_TIME_EXCEED 11 // Time exceeded during reassembly +#define ICMP_PARAM_PROBLEM 12 // Parameter problem +#define ICMP_SOURCE_QUENCH 4 // Source quench +#define ICMP_ROUTER_ADVERTISEMENT 9 // Router Advertisement +#define ICMP_ROUTER_SOLICITATION 10 // Router Solicitation + +#define NET_UNREACH 0 +#define HOST_UNREACH 1 +#define PROT_UNREACH 2 +#define PORT_UNREACH 3 +#define FRAG_NEEDED 4 +#define SR_FAILED 5 +#define DEST_NET_UNKNOWN 6 +#define DEST_HOST_UNKNOWN 7 +#define SRC_ISOLATED 8 +#define DEST_NET_ADMIN 9 +#define DEST_HOST_ADMIN 10 +#define NET_UNREACH_TOS 11 +#define HOST_UNREACH_TOS 12 + +#define TTL_IN_TRANSIT 0 // TTL expired in transit +#define TTL_IN_REASSEM 1 // Time exceeded in reassembly + + +#define PTR_VALID 0 +#define REQ_OPTION_MISSING 1 + +#define REDIRECT_NET 0 +#define REDIRECT_HOST 1 +#define REDIRECT_NET_TOS 2 +#define REDIRECT_HOST_TOS 3 + +extern uint DHCPActivityCount; + +extern IP_STATUS SetIFContext(uint Index, INTERFACE_CONTEXT *Context); + +extern IP_STATUS SetFilterPtr(IPPacketFilterPtr FilterPtr); + +extern IP_STATUS SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr); + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'iPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'iPCT') + +#endif // POOL_TAGGING + +// +// Use the TCP core checksum routine. +// + +ULONG +tcpxsum ( + IN ULONG Checksum, + IN PUCHAR Source, + IN ULONG Length + ); + +#define xsum(Buffer, Length) ((ushort) tcpxsum(0, (PUCHAR) (Buffer), (Length))) + +#else // NT + +extern ushort xsum(void *, int); + +#endif // NT + diff --git a/private/ntos/tdi/tcpip/ip/ipinit.h b/private/ntos/tdi/tcpip/ip/ipinit.h new file mode 100644 index 000000000..d1d730915 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipinit.h @@ -0,0 +1,155 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPINIT.H - IP initialization definitions. +// +// This file contains all of the definitions for IP that are +// init. time specific. + +#define IP_INIT_FAILURE 0 // If we fail. +#define IP_INIT_SUCCESS 1 +#define CFG_REQUIRED 1 +#define CFG_OPTIONAL 0 + + +#define NET_TYPE_LAN 0 // The local net interface is a LAN. +#define NET_TYPE_WAN 1 // Point to point or other non-LAN network. +#define DEFAULT_TTL 128 +#define DEFAULT_TOS 0 + +#define MAX_DEFAULT_GWS 5 // Maximum number of default gateways per net. +#define MAX_NAME_SIZE 32 // Maximum length of an adapter name. + +#define DEFAULT_FW_PACKETS 50 // Default number of packets for forwarding. +#define DEFAULT_FW_BUFSIZE 74240 // Enough for 50 1480-byte Ethernet packets, + // rounded up to a multiple of 256. + +#define DEFAULT_MAX_FW_PACKETS 0xffffffff +#define DEFAULT_MAX_FW_BUFSIZE 0xffffffff + +#define DEFAULT_MAX_PENDING 5000 + +#define TR_RII_ALL 0x80 +#define TR_RII_SINGLE 0xC0 + +#define DEFAULT_ARP_CACHE_LIFE (2L*60L) // 2 minutes + +#ifndef _PNP_POWER + +//* Per net config. information. +struct NetConfigInfo { + IPAddr nci_addr; // IPAddr for this net. + IPMask nci_mask; // Net mask for this net. + uint nci_type; // Type of this net - Enet, TR, SLIP., etc. + NDIS_STRING nci_driver; // Device name for lower layer driver. + // Unused for NET_TYPE_LAN. + NDIS_STRING nci_name; // Name of adapter for this net. + +#ifdef SECFLTR + NDIS_STRING nci_configname; // Name of config section in registry. +#endif // SECFLTR + + uint nci_zerobcast; // Type of broadcast to be used on this net. + +#ifdef NT + HANDLE nci_reghandle; // Open handle to the registry key for + // this adapter. +#endif // NT + + uint nci_mtu; // Max MSS for this net. + uint nci_maxpending; // Max routing packets pending. + uint nci_numgws; // Number of default gateways for this interface. + IPAddr nci_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways + uint nci_rtrdiscovery; // Router discovery enabled + IPAddr nci_rtrdiscaddr; // Multicast or BCast? +}; /* NetConfigInfo */ + +typedef struct NetConfigInfo NetConfigInfo; + + +#else // _PNP_POWER + +/*NOINC*/ + + +// Per-net config structures for Chicago. +typedef struct IFGeneralConfig { + uint igc_zerobcast; // Type of broadcast to be used on this net. + uint igc_mtu; // Max MSS for this net. + uint igc_maxpending; // Max FW pending on this IF. + uint igc_numgws; // Number of default gateways for this + // interface. + IPAddr igc_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways + uint igc_rtrdiscovery; // Router discovery enabled + IPAddr igc_rtrdiscaddr; // Multicast or BCast? +} IFGeneralConfig; + +typedef struct IFAddrList { + IPAddr ial_addr; // Address for this interface. + IPMask ial_mask; // Mask to go with this. +} IFAddrList; + + +/*INC*/ + +#endif // _PNP_POWER + +//* Structure of configuration information. A pointer to this information +// is returned from a system-specific config. information routine. +struct IPConfigInfo { + uint ici_gateway; // 1 if we are a gateway, 0 otherwise + uint ici_fwbcast; // 1 if bcasts should be forwarded. Else 0. + uint ici_fwbufsize; // Total size of FW buf size. + uint ici_fwpackets; // Total number of FW packets to have. + uint ici_maxfwbufsize; // Maximum size of FW buffer. + uint ici_maxfwpackets; // Maximum number of FW packets. + uint ici_deadgwdetect; // True if we're doing dead GW detection. + uint ici_pmtudiscovery; // True if we're doing Path MTU discovery. + uint ici_igmplevel; // Level of IGMP we're doing. + uint ici_ttl; // Default TTL. + uint ici_tos; // Default TOS; + +#ifndef _PNP_POWER + int ici_numnets; // Number of nets present. + struct NetConfigInfo *ici_netinfo; // Per net config. info +#endif // _PNP_POWER + +}; /* IPConfigInfo */ + +typedef struct IPConfigInfo IPConfigInfo; + + +#ifndef _PNP_POWER + +struct NameMapping { + NDIS_STRING nm_driver; + NDIS_STRING nm_name; + void *nm_interface; + void *nm_arpinfo; +}; /* NameMapping */ + +typedef struct NameMapping NameMapping; + +struct DriverRegMapping { + NDIS_STRING drm_driver; + void *drm_regptr; +}; /* DriverRegMapping */ + +typedef struct DriverRegMapping DriverRegMapping; + +#endif // _PNP_POWER +extern uchar TrRii; + + +struct SetAddrControl { + void *sac_rtn; // Pointer to routine to call when completing request. +}; /* SetAddrControl */ + +/*NOINC*/ +typedef struct SetAddrControl SetAddrControl; +typedef void (*SetAddrRtn)(void *, IP_STATUS); +/*INC*/ + diff --git a/private/ntos/tdi/tcpip/ip/iploop.c b/private/ntos/tdi/tcpip/ip/iploop.c new file mode 100644 index 000000000..b47676876 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iploop.c @@ -0,0 +1,665 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iploop.c - IP loopback routines. +// +// This file contains all the routines related to loopback + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "tdistat.h" +#include "ipdef.h" +#include "ipinit.h" +#include "llipif.h" +#include "iprtdef.h" +#include "iproute.h" +#include "tdiinfo.h" +#include "llinfo.h" + +#define LOOP_LOOKAHEAD MAX_HDR_SIZE + 8 + +extern int NumNTE; + +extern Interface *IFList; +extern uint NumIF; + +extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint); +extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS); +extern void IPRcvComplete(void); +extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset); + +DEFINE_LOCK_STRUCTURE(LoopLock) +PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL; +PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL; +CTEEvent LoopXmitEvent; +RouteInterface LoopInterface; // Loopback interface. +uint LoopXmitRtnRunning = 0; + + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +NetTableEntry *InitLoopback(IPConfigInfo *ConfigInfo); + +#pragma alloc_text(INIT, InitLoopback) + +#endif // ALLOC_PRAGMA +#endif // NT + + +uint LoopIndex; // Index of loop I/F. +uint LoopInstance; // I/F instance of loopback I/F. +NetTableEntry *LoopNTE; // Pointer to loopback NTE. + +IFEntry LoopIFE; // Loopback IF Entry. + +uchar LoopName[] = "MS TCP Loopback interface"; + +uint LoopEntityType = IF_MIB; + +//* LoopXmitRtn - Loopback xmit event routine. +// +// This is the delayed event routine called for a loopback transmit. +// +// Entry: Event - Pointer to event structure. +// Context - Pointer to loopback NTE +// +// Returns: Nothing. +// +void +LoopXmitRtn(CTEEvent *Event, void *Context) +{ + PNDIS_PACKET Packet; // Pointer to packet being transmitted + CTELockHandle Handle; + PNDIS_BUFFER Buffer; // Current NDIS buffer being processed. + uint TotalLength; // Total length of send. + uint LookaheadLength; // Bytes in lookahead. + uint Copied; // Bytes copied so far. + uchar *CopyPtr; // Pointer to buffer being copied into. + uchar *SrcPtr; // Pointer to buffer being copied from. + uint SrcLength; // Length of src buffer. + uchar LookaheadBuffer[LOOP_LOOKAHEAD]; + uchar Rcvd = FALSE; +#ifdef NT + KIRQL OldIrql; + + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + // + // Raise IRQL so we can acquire locks at DPC level in the receive code. + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); +#endif // NT + + CTEGetLock(&LoopLock, &Handle); + + if (LoopXmitRtnRunning) { + CTEFreeLock(&LoopLock, Handle); +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + return; + } + + LoopXmitRtnRunning = 1; + + for (;;) { + + Packet = LoopXmitHead; // Get the next packet from the list. + if (Packet != (PNDIS_PACKET)NULL) { + LoopXmitHead = *(PNDIS_PACKET *)Packet->MacReserved; + LoopIFE.if_outqlen--; + CTEFreeLock(&LoopLock, Handle); + } else { // Nothing left to do. + LoopXmitRtnRunning = 0; + CTEFreeLock(&LoopLock, Handle); + break; + } + + NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength); + LoopIFE.if_outoctets += TotalLength; + LoopIFE.if_inoctets += TotalLength; + + // See if the interface is up. If it's not, we can't deliver it. + if (LoopIFE.if_adminstatus == IF_STATUS_UP) { + LookaheadLength = MIN(LOOP_LOOKAHEAD, TotalLength); + Copied = 0; + CopyPtr = LookaheadBuffer; + while (Copied < LookaheadLength) { + uint ThisCopy; // Bytes to copy this time. + +#ifdef DEBUG + if (!Buffer) { + DEBUGCHK; + CTEGetLock(&LoopLock, &Handle); + LoopXmitRtnRunning = 0; + CTEFreeLock(&LoopLock, Handle); +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + return; + } +#endif + + NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength); + ThisCopy = MIN(SrcLength, LookaheadLength - Copied); + CTEMemCopy(CopyPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + CopyPtr += ThisCopy; + NdisGetNextBuffer(Buffer, &Buffer); + } + + Rcvd = TRUE; + LoopIFE.if_inucastpkts++; + + IPRcv(Context, LookaheadBuffer, LookaheadLength, TotalLength, + (NDIS_HANDLE) Packet, 0, FALSE); + + } else { + LoopIFE.if_indiscards++; + } + + IPSendComplete(Context, Packet, NDIS_STATUS_SUCCESS); + +#ifdef NT + // + // Give other threads a chance to run. + // + KeLowerIrql(OldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); +#endif // NT + + CTEGetLock(&LoopLock, &Handle); + } + + if (Rcvd) { + IPRcvComplete(); + } + +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + +} + +//** LoopXmit - Transmit a loopback packet. +// +// This is the routine called when we need to transmit a packet to ourselves. We put +// the packet on our loopback list, and schedule an event to deal with it. +// +// Entry: Context - Pointer to the loopback NTE. +// Packet - Pointer to packet to be transmitted. +// Dest - Destination addres of packet. +// RCE - Pointer to RCE (should be NULL). +// +// Returns: NDIS_STATUS_PENDING +// +NDIS_STATUS +LoopXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE) +{ + PNDIS_PACKET *PacketPtr; + CTELockHandle Handle; + + LoopIFE.if_outucastpkts++; + + if (LoopIFE.if_adminstatus == IF_STATUS_UP) { + PacketPtr = (PNDIS_PACKET *)Packet->MacReserved; + *PacketPtr = (PNDIS_PACKET)NULL; + + + CTEGetLock(&LoopLock, &Handle); + if (LoopXmitHead == (PNDIS_PACKET)NULL) { // Xmit. Q is empty + LoopXmitHead = Packet; + } else { // Xmit. Q is not empty + PacketPtr = (PNDIS_PACKET *)LoopXmitTail->MacReserved; + *PacketPtr = Packet; + } + LoopXmitTail = Packet; + LoopIFE.if_outqlen++; + if (!LoopXmitRtnRunning) { + CTEScheduleEvent(&LoopXmitEvent, Context); + } + CTEFreeLock(&LoopLock, Handle); + return NDIS_STATUS_PENDING; + } else { + LoopIFE.if_outdiscards++; + return NDIS_STATUS_SUCCESS; + } +} + +//* LoopXfer - Loopback transfer data routine. +// +// Called when we need to transfer data for the loopback net. The input TDContext is +// the original packet. +// +// Entry: Context - Pointer to loopback NTE. +// TDContext - Original packet that was sent. +// Dummy - Unused +// Offset - Offset in frame from which to start copying. +// BytesToCopy - Number of bytes to copy. +// DestPacket - Packet describing buffer to copy into. +// BytesCopied - Place to return bytes copied. +// +// Returns: NDIS_STATUS_SUCCESS +// +NDIS_STATUS +LoopXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy, + PNDIS_PACKET DestPacket, uint *BytesCopied) +{ + PNDIS_BUFFER SrcBuffer; // Current buffer we're copying from. + PNDIS_PACKET SrcPacket = (PNDIS_PACKET)TDContext; + uchar *SrcPtr; // Where we're copying from. + uint SrcLength; // Length of current src buffer. + PNDIS_BUFFER DestBuffer; // Buffer we're copying to. + uchar *DestPtr; // Where we're copying to. + uint DestLength; // Length of current dest. buffer. + uint Copied; // Length we've copied so far. + + // First, skip over Offset bytes in the packet. + NdisQueryPacket(SrcPacket, NULL, NULL, &SrcBuffer, NULL); +#ifdef DEBUG + if (!SrcBuffer) + DEBUGCHK; +#endif + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + while (Offset >= SrcLength) { + Offset -= SrcLength; + NdisGetNextBuffer(SrcBuffer, &SrcBuffer); +#ifdef DEBUG + if (!SrcBuffer) + DEBUGCHK; +#endif + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + } + // Update Src pointer and length. + SrcPtr += Offset; + SrcLength -= Offset; + + // Set up the destination pointers and lengths. + NdisQueryPacket(DestPacket, NULL, NULL, &DestBuffer, NULL); + NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength); + Copied = 0; + + while (BytesToCopy) { + uint ThisCopy; // What we're copying this time. + + ThisCopy = MIN(SrcLength, DestLength); + CTEMemCopy(DestPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + DestPtr += ThisCopy; + SrcPtr += ThisCopy; + BytesToCopy -= ThisCopy; + SrcLength -= ThisCopy; + DestLength -= ThisCopy; + if (!SrcLength) { // We've exhausted the source buffer. + NdisGetNextBuffer(SrcBuffer, &SrcBuffer); + if (!SrcBuffer) { +#ifdef DEBUG + if (BytesToCopy) + DEBUGCHK; +#endif + break; // Copy is done. + } + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + } + if (!DestLength) { // We've exhausted the destination buffer. + NdisGetNextBuffer(DestBuffer, &DestBuffer); + if (!DestBuffer) { +#ifdef DEBUG + if (BytesToCopy) + DEBUGCHK; +#endif + break; // Copy is done. + } + NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength); + } + } + + *BytesCopied = Copied; + return NDIS_STATUS_SUCCESS; + + +} + +//* LoopClose - Loopback close routine. +// +// This is the loopback close routine. It does nothing but return. +// +// Entry: Context - Unused. +// +// Returns: Nothing. +// +void +LoopClose(void *Context) +{ + +} + +//* LoopInvalidate - Invalidate an RCE. +// +// The loopback invalidate RCE routine. It also does nothing. +// +// Entry: Context - Unused. +// RCE - Pointer to RCE to be invalidated. +// +// Returns: Nothing. +// +void +LoopInvalidate(void *Context, RouteCacheEntry *RCE) +{ + +} + +//* LoopQInfo - Loopback query information handler. +// +// Called when the upper layer wants to query information about the loopback +// interface. +// +// Input: IFContext - Interface context (unused). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// Context - Pointer to context block. +// +// Returns: Status of attempt to query information. +// +int +LoopQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + uint Offset = 0; + uint BufferSize = *Size; + uint Entity; + uint Instance; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if (Entity != IF_ENTITY || Instance != LoopInstance) { + return TDI_INVALID_REQUEST; + } + + *Size = 0; // In case of an error. + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_id == ENTITY_TYPE_ID) { + // He's trying to see what type we are. + if (BufferSize >= sizeof(uint)) { + (void)CopyToNdis(Buffer, (uchar *)&LoopEntityType, sizeof(uint), + &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } else + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + // If he's asking for MIB statistics, then return them, otherwise fail + // the request. + + if (ID->toi_id == IF_MIB_STATS_ID) { + + + // He's asking for statistics. Make sure his buffer is at least big + // enough to hold the fixed part. + + if (BufferSize < IFE_FIXED_SIZE) { + return TDI_BUFFER_TOO_SMALL; + } + + // He's got enough to hold the fixed part. Copy our IFE structure + // into his buffer. + Buffer = CopyToNdis(Buffer, (uchar *)&LoopIFE, IFE_FIXED_SIZE, &Offset); + + // See if he has room for the descriptor string. + if (BufferSize >= (IFE_FIXED_SIZE + sizeof(LoopName))) { + // He has room. Copy it. + (void)CopyToNdis(Buffer, LoopName, sizeof(LoopName), &Offset); + *Size = IFE_FIXED_SIZE + sizeof(LoopName); + return TDI_SUCCESS; + } else { + // Not enough room to copy the desc. string. + *Size = IFE_FIXED_SIZE; + return TDI_BUFFER_OVERFLOW; + } + + } + + return TDI_INVALID_PARAMETER; + +} + +//* LoopSetInfo - Loopback set information handler. +// +// The loopback set information handler. We support setting of an I/F admin +// status. +// +// Input: Context - Pointer to I/F to set on. +// ID - The object ID +// Buffer - Pointer to buffer containing value to set. +// Size - Size in bytes of Buffer. +// +// Returns: Status of attempt to set information. +// +int +LoopSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size) +{ + IFEntry *IFE = (IFEntry *)Buffer; + uint Entity, Instance, Status; + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if (Entity != IF_ENTITY || Instance != LoopInstance) { + return TDI_INVALID_REQUEST; + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL || + ID->toi_type != INFO_TYPE_PROVIDER) { + return TDI_INVALID_PARAMETER; + } + + // It's for the I/F level, see if it's for the statistics. + if (ID->toi_id == IF_MIB_STATS_ID) { + // It's for the stats. Make sure it's a valid size. + if (Size >= IFE_FIXED_SIZE) { + // It's a valid size. See what he wants to do. + Status = IFE->if_adminstatus; + if (Status == IF_STATUS_UP || Status == IF_STATUS_DOWN) + LoopIFE.if_adminstatus = Status; + else + if (Status != IF_STATUS_TESTING) + return TDI_INVALID_PARAMETER; + + return TDI_SUCCESS; + + } else + return TDI_INVALID_PARAMETER; + } + + return TDI_INVALID_PARAMETER; +} + +//* LoopAddAddr - Dummy loopback add address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +LoopAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + + return TRUE; +} + +//* LoopDelAddr - Dummy loopback del address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +LoopDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + + return TRUE; +} + +#pragma BEGIN_INIT + +extern int InitNTE(NetTableEntry *); +extern int InitInterface(NetTableEntry *); + +#ifdef CHICAGO +#pragma END_INIT +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//* LoopGetEList - Get the entity list. +// +// Called at init time to get an entity list. We fill our stuff in and return. +// +// Input: Context - Unused. +// EntityList - Pointer to entity list to be filled in. +// Count - Pointer to number of entries in the list. +// +// Returns Status of attempt to get the info. +// +int +LoopGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + uint ECount; + uint MyIFBase; + uint i; + + ECount = *Count; + + // Walk down the list, looking for existing IF entities, and + // adjust our base instance accordingly. + + MyIFBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == IF_ENTITY) + MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1); + } + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. + + if ((ECount + 1) > MAX_TDI_ENTITIES) + return FALSE; + + // At this point we've figure out our base instance. Save for later use. + LoopInstance = MyIFBase; + + // Now fill it in. + EntityList->tei_entity = IF_ENTITY; + EntityList->tei_instance = MyIFBase; + (*Count)++; + + return TRUE; +} + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + + +//** InitLoopback - Initialize the loopback NTE. +// +// This function initialized the loopback NTE. We set up the the MSS and pointer to +// the various pseudo-link routines, then call InitNTE and return. +// +// Entry: ConfigInfo - Pointer to config. info structure. +// +// Returns: TRUE if we initialized, FALSE if we didn't. +// +NetTableEntry * +InitLoopback(IPConfigInfo *ConfigInfo) +{ + LLIPBindInfo ARPInfo; + + CTERefillMem(); + LoopNTE = CTEAllocMem(sizeof(NetTableEntry)); + if (LoopNTE == NULL) + return LoopNTE; + + CTEMemSet(LoopNTE, 0, sizeof(NetTableEntry)); + + LoopNTE->nte_addr = LOOPBACK_ADDR; + LoopNTE->nte_mask = CLASSA_MASK; + LoopNTE->nte_icmpseq = 1; + LoopNTE->nte_flags = NTE_VALID | NTE_ACTIVE | NTE_PRIMARY; + + CTEInitLock(&LoopNTE->nte_lock); + CTEInitLock(&LoopInterface.ri_if.if_lock); + LoopNTE->nte_mss = LOOPBACK_MSS; + LoopNTE->nte_if = (Interface *)&LoopInterface; + LoopInterface.ri_if.if_lcontext = LoopNTE; + LoopInterface.ri_if.if_xmit = LoopXmit; + LoopInterface.ri_if.if_transfer = LoopXfer; + LoopInterface.ri_if.if_close = LoopClose; + LoopInterface.ri_if.if_invalidate = LoopInvalidate; + LoopInterface.ri_if.if_qinfo = LoopQInfo; + LoopInterface.ri_if.if_setinfo = LoopSetInfo; + LoopInterface.ri_if.if_getelist = LoopGetEList; + LoopInterface.ri_if.if_addaddr = LoopAddAddr; + LoopInterface.ri_if.if_deladdr = LoopDelAddr; + LoopInterface.ri_if.if_bcast = IP_LOCAL_BCST; + LoopInterface.ri_if.if_speed = 10000000; + LoopInterface.ri_if.if_mtu = LOOPBACK_MSS; + LoopInterface.ri_if.if_llipflags = LIP_COPY_FLAG; +#ifdef _PNP_POWER + LoopInterface.ri_if.if_refcount = 1; + LoopInterface.ri_if.if_pnpcontext = 0; +#endif + + ARPInfo.lip_mss = LOOPBACK_MSS + sizeof(IPHeader); + ARPInfo.lip_index = LoopIndex; + ARPInfo.lip_close = LoopClose; + ARPInfo.lip_addaddr = LoopAddAddr; + ARPInfo.lip_deladdr = LoopDelAddr; + ARPInfo.lip_flags = LIP_COPY_FLAG; + LoopIndex = NumIF + 1; + LoopInterface.ri_if.if_index = LoopIndex; + CTEInitEvent(&LoopXmitEvent, LoopXmitRtn); + CTEInitLock(&LoopLock); + LoopIFE.if_index = LoopIndex; + LoopIFE.if_type = IF_TYPE_LOOPBACK; + LoopIFE.if_mtu = ARPInfo.lip_mss; + LoopIFE.if_speed = 10000000; + LoopIFE.if_adminstatus = IF_STATUS_UP; + LoopIFE.if_operstatus = IF_STATUS_UP; + LoopIFE.if_descrlen = sizeof(LoopName); + + IFList = (Interface *)&LoopInterface; + NumIF++; + + NumNTE++; + + if (!InitInterface(LoopNTE)) + return NULL; + + if (!InitNTE(LoopNTE)) + return NULL; + + return LoopNTE; +} + +#pragma END_INIT + diff --git a/private/ntos/tdi/tcpip/ip/iprcv.c b/private/ntos/tdi/tcpip/ip/iprcv.c new file mode 100644 index 000000000..791bc58f6 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iprcv.c @@ -0,0 +1,1145 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iprcv.c - IP receive routines. +// +// This module contains all receive related IP routines. +// + + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "info.h" +#include "iproute.h" +#include "ipfilter.h" + +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); + +extern uchar RATimeout; +extern NDIS_HANDLE BufferPool; +#if 0 +EXTERNAL_LOCK(PILock) +#endif +extern ProtInfo IPProtInfo[]; // Protocol information table. +extern ProtInfo *LastPI; // Last protinfo structure looked at. +extern int NextPI; // Next PI field to be used. +extern ProtInfo *RawPI; // Raw IP protinfo +extern NetTableEntry *NetTableList; // Pointer to the net table. + +DEBUGSTRING(RcvFile, "iprcv.c"); + +#ifdef CHICAGO +extern void RefillReasmMem(void); +#endif + +//* FindUserRcv - Find the receive handler to be called for a particular protocol. +// +// This functions takes as input a protocol value, and returns a pointer to +// the receive routine for that protocol. +// +// Input: NTE - Pointer to NetTableEntry to be searched +// Protocol - Protocol to be searched for. +// UContext - Place to returns UL Context value. +// +// Returns: Pointer to the receive routine. +// +ULRcvProc +FindUserRcv(uchar Protocol) +{ + ULRcvProc RcvProc; + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + + if (LastPI->pi_protocol == Protocol) { + RcvProc = LastPI->pi_rcv; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + } + + RcvProc = (ULRcvProc)NULL; + for ( i = 0; i < NextPI; i++) { + if (IPProtInfo[i].pi_protocol == Protocol) { + LastPI = &IPProtInfo[i]; + RcvProc = IPProtInfo[i].pi_rcv; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + } + } + + // + // Didn't find a match. Use the raw protocol if it is registered. + // + if (RawPI != NULL) { + RcvProc = RawPI->pi_rcv; + } + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + +} + +//* IPRcvComplete - Handle a receive complete. +// +// Called by the lower layer when receives are temporarily done. +// +// Entry: Nothing. +// +// Returns: Nothing. +// +void +IPRcvComplete(void) +{ + void (*ULRcvCmpltProc)(void); + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + for (i = 0; i < NextPI; i++) { + if ((ULRcvCmpltProc = IPProtInfo[i].pi_rcvcmplt) != NULL) { +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + (*ULRcvCmpltProc)(); +#if 0 + CTEGetLock(&PILock, &Handle); +#endif + } + } +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + +} +//* FindRH - Look up a reassembly header on an NTE. +// +// A utility function to look up a reassembly header. We assume the lock on the NTE +// is taken when we are called. If we find a matching RH we'll take the lock on it. +// We also return the predeccessor of the RH, for use in insertion or deletion. +// +// Input: PrevRH - Place to return pointer to previous RH +// NTE - NTE to be searched. +// Dest - Destination IP address +// Src - Src IP address +// ID - ID of RH +// Protocol - Protocol of RH +// +// Returns: Pointer to RH, or NULL if none. +// +ReassemblyHeader * +FindRH(ReassemblyHeader **PrevRH, NetTableEntry *NTE, IPAddr Dest, IPAddr Src, ushort Id, + uchar Protocol) +{ + ReassemblyHeader *TempPrev, *Current; + + TempPrev = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next); + Current = NTE->nte_ralist; + while (Current != (ReassemblyHeader *)NULL) { + if (Current->rh_dest == Dest && Current->rh_src == Src && Current->rh_id == Id && + Current->rh_protocol == Protocol) + break; + TempPrev = Current; + Current = Current->rh_next; + } + + *PrevRH = TempPrev; + return Current; + +} + +//* ParseRcvdOptions - Validate incoming options. +// +// Called during reception handling to validate incoming options. We make sure that everything +// is OK as best we can, and find indices for any source route option. +// +// Input: OptInfo - Pointer to option info. structure. +// Index - Pointer to optindex struct to be filled in. +// +// +// Returns: Index of error if any, MAX_OPT_SIZE if no errors. +// +uchar +ParseRcvdOptions(IPOptInfo *OptInfo, OptIndex *Index) +{ + uint i= 0; // Index variable. + uchar *Options = OptInfo->ioi_options; + uint OptLength = (uint)OptInfo->ioi_optlength; + uchar Length; // Length of option. + uchar Pointer; // Pointer field, for options that use it. + + while(i < OptLength && *Options != IP_OPT_EOL) { + if (*Options == IP_OPT_NOP) { + i++; + Options++; + continue; + } + if (((Length = Options[IP_OPT_LENGTH]) + i) > OptLength) { + return (uchar)i + (uchar)IP_OPT_LENGTH; // Length exceeds options length. + } + + Pointer = Options[IP_OPT_DATA] - 1; + + if (*Options == IP_OPT_TS) { + if (Length < (MIN_TS_PTR - 1)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + Index->oi_tsindex = (uchar)i; + } else { + if (Length < (MIN_RT_PTR - 1)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + + if (*Options == IP_OPT_LSRR || *Options == IP_OPT_SSRR) { + // A source route option + if (Pointer < Length) { // Route not complete + + if ((Length - Pointer) < sizeof(IPAddr)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + + Index->oi_srtype = *Options; + Index->oi_srindex = (uchar)i; + } + } else { + if (*Options == IP_OPT_RR) { + if (Pointer < Length) + Index->oi_rrindex = (uchar)i; + } + } + } + + i += Length; + Options += Length; + } + + return MAX_OPT_SIZE; +} + +//* BCastRcv - Receive a broadcast or multicast packet. +// +// Called when we have to receive a broadcast packet. We loop through the NTE table, +// calling the upper layer receive protocol for each net which matches the receive I/F +// and for which the destination address is a broadcast. +// +// Input: RcvProc - The receive procedure to be called. +// SrcNTE - NTE on which the packet was originally received. +// DestAddr - Destination address. +// SrcAddr - Source address of packet. +// Data - Pointer to received data. +// DataLength - Size in bytes of data +// Protocol - Upper layer protocol being called. +// OptInfo - Pointer to received IP option info. +// +// Returns: Nothing. +// +void +BCastRcv(ULRcvProc RcvProc, NetTableEntry *SrcNTE, IPAddr DestAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *Header, uint HeaderLength, + IPRcvBuf *Data, uint DataLength, uchar Protocol, IPOptInfo *OptInfo) +{ + NetTableEntry *CurrentNTE; + const Interface *SrcIF = SrcNTE->nte_if; + ulong Delivered = 0; + + + for (CurrentNTE = NetTableList; + CurrentNTE != NULL; + CurrentNTE = CurrentNTE->nte_next) + { + if ((CurrentNTE->nte_flags & NTE_ACTIVE) && + (CurrentNTE->nte_if == SrcIF) && + IS_BCAST_DEST(IsBCastOnNTE(DestAddr, CurrentNTE))) + { + Delivered = 1; + + (*RcvProc)(CurrentNTE, DestAddr, SrcAddr, CurrentNTE->nte_addr, + SrcNTE->nte_addr, Header, HeaderLength, Data, DataLength, + TRUE, Protocol, OptInfo); + } + } + + if (Delivered) { + IPSInfo.ipsi_indelivers++; + } +} + +//* DeliverToUser - Deliver data to a user protocol. +// +// This procedure is called when we have determined that an incoming packet belongs +// here, and any options have been processed. We accept it for upper layer processing, +// which means looking up the receive procedure and calling it, or passing it to BCastRcv +// if neccessary. +// +// Input: SrcNTE - Pointer to NTE on which packet arrived. +// DestNTE - Pointer to NTE that is accepting packet. +// Header - Pointer to IP header of packet. +// HeaderLength - Length of Header in bytes. +// Data - Pointer to IPRcvBuf chain. +// DataLength - Length in bytes of upper layer data. +// OptInfo - Pointer to Option information for this receive. +// DestType - Type of destination - LOCAL, BCAST. +// +// Returns: Nothing. +void +DeliverToUser(NetTableEntry *SrcNTE, NetTableEntry *DestNTE, + IPHeader UNALIGNED *Header, uint HeaderLength, IPRcvBuf *Data, + uint DataLength, IPOptInfo *OptInfo, uchar DestType) +{ + ULRcvProc rcv; + +#ifdef DEBUG + if (DestType >= DEST_REMOTE) + DEBUGCHK; +#endif + + // Process this request right now. Look up the protocol. If we + // find it, copy the data if we need to, and call the protocol's + // receive handler. If we don't find it, send an ICMP + // 'protocol unreachable' message. + rcv = FindUserRcv(Header->iph_protocol); + if (rcv != NULL) { + IP_STATUS Status; + + if (DestType == DEST_LOCAL) { + Status = (*rcv)(SrcNTE,Header->iph_dest, Header->iph_src, + DestNTE->nte_addr, SrcNTE->nte_addr, Header, + HeaderLength, Data, DataLength, FALSE, + Header->iph_protocol, OptInfo); + + if (Status == IP_SUCCESS) { + IPSInfo.ipsi_indelivers++; + return; + } + + if (Status == IP_DEST_PROT_UNREACHABLE) { + IPSInfo.ipsi_inunknownprotos++; + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, + PROT_UNREACH, 0); + } + else { + IPSInfo.ipsi_indelivers++; + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, + PORT_UNREACH, 0); + } + + return; // Just return out of here now. + } + else { + BCastRcv(rcv, SrcNTE, Header->iph_dest, Header->iph_src, + Header, HeaderLength, Data, DataLength, + Header->iph_protocol, OptInfo); + } + + } else { + IPSInfo.ipsi_inunknownprotos++; + // If we get here, we didn't find a matching protocol. Send an ICMP message. + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, PROT_UNREACH, 0); + } + +} + +//* FreeRH - Free a reassembly header. +// +// Called when we need to free a reassembly header, either because of a timeout or because +// we're done with it. +// +// Input: RH - RH to be freed. +// +// Returns: Nothing. +// +void +FreeRH(ReassemblyHeader *RH) +{ + RABufDesc *RBD, *TempRBD; + + RBD = RH->rh_rbd; + while (RBD != NULL) { + TempRBD = RBD; + RBD = (RABufDesc *)RBD->rbd_buf.ipr_next; + CTEFreeMem(TempRBD); + } + CTEFreeMem(RH); + +} + +//* ReassembleFragment - Put a fragment into the reassembly list. +// +// This routine is called once we've put a fragment into the proper buffer. We look for +// a reassembly header for the fragment. If we don't find one, we create one. Otherwise +// we search the reassembly list, and insert the datagram in it's proper place. +// +// Input: NTE - NTE to reassemble on. +// SrcNTE - NTE datagram arrived on. +// NewRBD - New RBD to be inserted. +// IPH - Pointer to header of datagram. +// HeaderSize - Size in bytes of header. +// DestType - Type of destination address. +// +// Returns: Nothing. +// +void +ReassembleFragment(NetTableEntry *NTE, NetTableEntry *SrcNTE, RABufDesc *NewRBD, + IPHeader UNALIGNED *IPH, uint HeaderSize, uchar DestType) +{ + CTELockHandle NTEHandle; // Lock handle used for NTE + ReassemblyHeader *RH, *PrevRH; // Current and previous reassembly headers. + RABufDesc *PrevRBD; // Previous RBD in reassembly header list. + RABufDesc *CurrentRBD; + ushort DataLength = (ushort)NewRBD->rbd_buf.ipr_size, DataOffset; + ushort Offset; // Offset of this fragment. + ushort NewOffset; // Offset we'll copy from after checking RBD list. + ushort NewEnd; // End offset of fragment, after trimming (if any). + + // If this is a broadcast, go ahead and forward it now. + if (IS_BCAST_DEST(DestType)) + IPForward(SrcNTE, IPH, HeaderSize, NewRBD->rbd_buf.ipr_buffer, + NewRBD->rbd_buf.ipr_size, NULL, 0, DestType); + + + // We've got the buffer we need. Now get the reassembly header, if there is one. If + // there isn't, create one. + CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle); + RH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol); + if (RH == (ReassemblyHeader *)NULL) { // Didn't find one, so create one. + ReassemblyHeader *NewRH; + + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + RH = CTEAllocMem(sizeof(ReassemblyHeader)); + if (RH == (ReassemblyHeader *)NULL) { // Couldn't get a buffer. +#ifdef CHICAGO + RefillReasmMem(); +#endif + IPSInfo.ipsi_reasmfails++; + CTEFreeMem(NewRBD); + return; + } + + CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle); + // Need to look it up again - it could have changed during above call. + NewRH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol); + if (NewRH != (ReassemblyHeader *)NULL) { + CTEFreeMem(RH); + RH = NewRH; + } else { + + RH->rh_next = PrevRH->rh_next; + PrevRH->rh_next = RH; + + + // Initialize our new reassembly header. + RH->rh_dest = IPH->iph_dest; + RH->rh_src = IPH->iph_src; + RH->rh_id = IPH->iph_id; + RH->rh_protocol = IPH->iph_protocol; + RH->rh_ttl = RATimeout; + RH->rh_datasize = 0xffff; // Default datasize to maximum. + RH->rh_rbd = (RABufDesc *)NULL; // And nothing on chain. + RH->rh_datarcvd = 0; // Haven't received any data yet. + RH->rh_headersize = 0; + + } + } + + // When we reach here RH points to the reassembly header we want to use. + // and we hold locks on the NTE and the RH. If this is the first fragment we'll save + // the options and header information here. + + Offset = IPH->iph_offset & IP_OFFSET_MASK; + Offset = net_short(Offset) * 8; + + if (Offset == 0) { // First fragment. + RH->rh_headersize = HeaderSize; + CTEMemCopy(RH->rh_header, IPH, HeaderSize + 8); + } + + // If this is the last fragment, update the amount of data we expect to received. + if (!(IPH->iph_offset & IP_MF_FLAG)) + RH->rh_datasize = Offset + DataLength; + + // Update the TTL value with the maximum of the current TTL and the incoming + // TTL (+1, to deal with rounding errors). + RH->rh_ttl = MAX(RH->rh_ttl, MIN(254, IPH->iph_ttl) + 1); + + // Now we need to see where in the RBD list to put this. + // + // The idea is to go through the list of RBDs one at a time. The RBD currently + // being examined is CurrentRBD. If the start offset of the new fragment is less + // than (i.e. in front of) the offset of CurrentRBD, we need to insert the NewRBD + // in front of the CurrentRBD. If this is the case we need to check and see if the + // end of the new fragment overlaps some or all of the fragment described by + // CurrentRBD, and possibly subsequent fragment. If it overlaps part of a fragment + // we'll adjust our end down to be in front of the existing fragment. If it overlaps + // all of the fragment we'll free the old fragment. + // + // If the new fragment does not start in front of the current fragment we'll check + // to see if it starts somewhere in the middle of the current fragment. If this + // isn't the case, we move on the the next fragment. If this is the case, we check + // to see if the current fragment completely covers the new fragment. If not we + // move our start up and continue with the next fragment. + + NewOffset = Offset; + NewEnd = Offset + DataLength - 1; + PrevRBD = STRUCT_OF(RABufDesc, STRUCT_OF(IPRcvBuf, &RH->rh_rbd, ipr_next), rbd_buf); + CurrentRBD = RH->rh_rbd; + for (; CurrentRBD != NULL; PrevRBD = CurrentRBD, CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next) { + + // See if it starts in front of this fragment. + if (NewOffset < CurrentRBD->rbd_start) { + // It does start in front. Check to see if there's any overlap. + + if (NewEnd < CurrentRBD->rbd_start) + break; // No overlap, so get out. + else { + // It does overlap. While we have overlap, walk down the list + // looking for RBDs we overlap completely. If we find one, put it + // on our deletion list. If we have overlap but not complete overlap, + // move our end down if front of the fragment we overlap. + do { + if (NewEnd > CurrentRBD->rbd_end) { // This overlaps completely. + RABufDesc *TempRBD; + + RH->rh_datarcvd -= CurrentRBD->rbd_buf.ipr_size; + TempRBD = CurrentRBD; + CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next; + CTEFreeMem(TempRBD); + } else // Only partial ovelap. + NewEnd = CurrentRBD->rbd_start - 1; + // Update of NewEnd will force us out of loop. + + } while (CurrentRBD != NULL && NewEnd >= CurrentRBD->rbd_start); + break; + } + } else { + // This fragment doesn't go in front of the current RBD. See if it is + // entirely beyond the end of the current fragment. If it is, just + // continue. Otherwise see if the current fragment completely subsumes + // us. If it does, get out, otherwise update our start offset and + // continue. + + if (NewOffset > CurrentRBD->rbd_end) + continue; // No overlap at all. + else { + if (NewEnd <= CurrentRBD->rbd_end) { + // The current fragment overlaps the new fragment totally. Set + // our offsets so that we'll skip the copy below. + NewEnd = NewOffset - 1; + break; + } else // Only partial overlap. + NewOffset = CurrentRBD->rbd_end + 1; + } + } + } // End of for loop. + + // Adjust the length and offset fields in the new RBD. + DataLength = NewEnd - NewOffset + 1; + DataOffset = NewOffset - Offset; + // Link him in chain. + NewRBD->rbd_buf.ipr_size = (uint)DataLength; + NewRBD->rbd_end = NewEnd; + NewRBD->rbd_start = NewOffset; + RH->rh_datarcvd += DataLength; + NewRBD->rbd_buf.ipr_buffer += DataOffset; + NewRBD->rbd_buf.ipr_next = (IPRcvBuf *)CurrentRBD; + PrevRBD->rbd_buf.ipr_next = &NewRBD->rbd_buf; + + // If we've received all the data, deliver it to the user. + if (RH->rh_datarcvd == RH->rh_datasize) { // We have it all. + IPOptInfo OptInfo; + IPHeader *Header; + IPRcvBuf *FirstBuf; + + PrevRH->rh_next = RH->rh_next; + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + Header = (IPHeader *)RH->rh_header; + OptInfo.ioi_ttl = Header->iph_ttl; + OptInfo.ioi_tos = Header->iph_tos; + OptInfo.ioi_flags = 0; // Flags must be 0 - DF can't be set, this was reassembled. + + if (RH->rh_headersize != sizeof(IPHeader)) { // We had options. + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = RH->rh_headersize - sizeof(IPHeader); + } else { + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + } + + // Make sure that the first buffer contains enough data. + FirstBuf = (IPRcvBuf *)RH->rh_rbd; + while (FirstBuf->ipr_size < MIN_FIRST_SIZE) { + IPRcvBuf *NextBuf = FirstBuf->ipr_next; + uint CopyLength; + + if (NextBuf == NULL) + break; + + CopyLength = MIN(MIN_FIRST_SIZE - FirstBuf->ipr_size, + NextBuf->ipr_size); + CTEMemCopy(FirstBuf->ipr_buffer + FirstBuf->ipr_size, + NextBuf->ipr_buffer, CopyLength); + FirstBuf->ipr_size += CopyLength; + NextBuf->ipr_buffer += CopyLength; + NextBuf->ipr_size -= CopyLength; + if (NextBuf->ipr_size == 0) { + FirstBuf->ipr_next = NextBuf->ipr_next; + CTEFreeMem(NextBuf); + } + } + + IPSInfo.ipsi_reasmoks++; + DeliverToUser(SrcNTE, NTE, Header, RH->rh_headersize, FirstBuf, + RH->rh_datasize, &OptInfo, DestType); + FreeRH(RH); + } else + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + + +} + +//* RATDComplete - Completion routing for a reassembly transfer data. +// +// This is the completion handle for TDs invoked because we are reassembling a fragment. +// +// Input: NetContext - Pointer to the net table entry on which we received this. +// Packet - Packet we received into. +// Status - Final status of copy. +// DataSize - Size in bytes of data transferred. +// +// Returns: Nothing +// +void +RATDComplete(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataSize) +{ + NetTableEntry *NTE = (NetTableEntry *)NetContext; + Interface *SrcIF; + TDContext *Context = (TDContext *)Packet->ProtocolReserved; + CTELockHandle Handle; + PNDIS_BUFFER Buffer; + + if (Status == NDIS_STATUS_SUCCESS) { + Context->tdc_rbd->rbd_buf.ipr_size = DataSize; + ReassembleFragment(Context->tdc_nte, NTE, Context->tdc_rbd, + (IPHeader *)Context->tdc_header, Context->tdc_hlength, Context->tdc_dtype); + } + + NdisUnchainBufferAtFront(Packet, &Buffer); + NdisFreeBuffer(Buffer); + Context->tdc_common.pc_flags &= ~PACKET_FLAG_RA; + SrcIF = NTE->nte_if; + CTEGetLockAtDPC(&SrcIF->if_lock, &Handle); + + Context->tdc_common.pc_link = SrcIF->if_tdpacket; + SrcIF->if_tdpacket = Packet; + CTEFreeLockFromDPC(&SrcIF->if_lock, Handle); + + return; + +} + +//* IPReassemble - Reassemble an incoming datagram. +// +// Called when we receive an incoming fragment. The first thing we do is get a buffer +// to put the fragment in. If we can't we'll exit. Then we copy the data, either via +// transfer data or directly if it all fits. +// +// Input: SrcNTE - Pointer to NTE that received the datagram. +// NTE - Pointer to NTE on which to reassemble. +// IPH - Pointer to header of packet. +// HeaderSize - Size in bytes of header. +// Data - Pointer to data part of fragment. +// BufferLength - Length in bytes of user data available in the buffer. +// DataLength - Length in bytes of the (upper-layer) data. +// DestType - Type of destination +// LContext1, LContext2 - Link layer context values. +// +// Returns: Nothing. +// +void +IPReassemble(NetTableEntry *SrcNTE, NetTableEntry *NTE, IPHeader UNALIGNED *IPH, + uint HeaderSize, + uchar *Data, uint BufferLength, uint DataLength, uchar DestType, NDIS_HANDLE LContext1, + uint LContext2) +{ + Interface *RcvIF; + PNDIS_PACKET TDPacket; // NDIS packet used for TD. + TDContext *TDC = (TDContext *)NULL; // Transfer data context. + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + RABufDesc *NewRBD; // Pointer to new RBD to hold arriving fragment. + CTELockHandle Handle; + uint AllocSize; + + IPSInfo.ipsi_reasmreqds++; + + // First, get a new RBD to hold the arriving fragment. If we can't, then just skip + // the rest. The RBD has the buffer implicitly at the end of it. The buffer for the + // first fragment must be at least MIN_FIRST_SIZE bytes. + if ((IPH->iph_offset & IP_OFFSET_MASK) == 0) + AllocSize = MAX(MIN_FIRST_SIZE, DataLength); + else + AllocSize = DataLength; + + NewRBD = CTEAllocMem(sizeof(RABufDesc) + AllocSize); + + if (NewRBD != (RABufDesc *)NULL) { + + NewRBD->rbd_buf.ipr_buffer = (uchar *)(NewRBD + 1); + NewRBD->rbd_buf.ipr_size = DataLength; + NewRBD->rbd_buf.ipr_owner = IPR_OWNER_IP; + + // Copy the data into the buffer. If we need to call transfer data do so now. + if (DataLength > BufferLength) { // Need to call transfer data. + NdisAllocateBuffer(&Status, &Buffer, BufferPool, NewRBD + 1, DataLength); + if (Status != NDIS_STATUS_SUCCESS) { + IPSInfo.ipsi_reasmfails++; + CTEFreeMem(NewRBD); + return; + } + + // Now get a packet for transferring the frame. + RcvIF = SrcNTE->nte_if; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDPacket = RcvIF->if_tdpacket; + + if (TDPacket != (PNDIS_PACKET)NULL) { + + TDC = (TDContext *)TDPacket->ProtocolReserved; + RcvIF->if_tdpacket = TDC->tdc_common.pc_link; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + + TDC->tdc_common.pc_flags |= PACKET_FLAG_RA; + TDC->tdc_nte = NTE; + TDC->tdc_dtype = DestType; + TDC->tdc_hlength = (uchar)HeaderSize; + TDC->tdc_rbd = NewRBD; + CTEMemCopy(TDC->tdc_header, IPH, HeaderSize + 8); + NdisChainBufferAtFront(TDPacket, Buffer); + Status = (*(RcvIF->if_transfer))(RcvIF->if_lcontext, LContext1, LContext2, + HeaderSize, DataLength, TDPacket, &DataLength); + if (Status != NDIS_STATUS_PENDING) + RATDComplete(SrcNTE, TDPacket, Status, DataLength); + else + return; + } else { // Couldn't get a TD packet. + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + CTEFreeMem(NewRBD); + IPSInfo.ipsi_reasmfails++; + return; + } + } else { // It all fits, copy it. + CTEMemCopy(NewRBD + 1, Data, DataLength); + ReassembleFragment(NTE, SrcNTE, NewRBD, IPH, HeaderSize, DestType); + } + } else { +#ifdef CHICAGO + RefillReasmMem(); +#endif + + IPSInfo.ipsi_reasmfails++; + } + + return; +} + + + +//* CheckLocalOptions - Check the options received with a packet. +// +// A routine called when we've received a packet for this host and want to examine +// it for options. We process the options, and return TRUE or FALSE depending on whether +// or not it's for us. +// +// Input: SrcNTE - Pointer to NTE this came in on. +// Header - Pointer to incoming header. +// OptInfo - Place to put opt info. +// DestType - Type of incoming packet. +// +// Returns: DestType - Local or remote. +// +uchar +CheckLocalOptions(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, + IPOptInfo *OptInfo, uchar DestType) +{ + uint HeaderLength; // Length in bytes of header. + OptIndex Index; + uchar ErrIndex; + +#ifdef DEBUG + if (DestType >= DEST_REMOTE) + DEBUGCHK; +#endif + + + HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + +#ifdef DEBUG + if (HeaderLength <= sizeof(IPHeader)) + DEBUGCHK; +#endif + + OptInfo->ioi_options = (uchar *)(Header + 1); + OptInfo->ioi_optlength = HeaderLength - sizeof(IPHeader); + + + // We have options of some sort. The packet may or may not be bound for us. + Index.oi_srindex = MAX_OPT_SIZE; + if ((ErrIndex = ParseRcvdOptions(OptInfo, &Index)) < MAX_OPT_SIZE) { + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, PTR_VALID, + ((ulong)ErrIndex + sizeof(IPHeader))); + return DEST_INVALID; // Parameter error. + } + + // If there's no source route, or if the destination is a broadcast, we'll take + // it. If it is a broadcast DeliverToUser will forward it when it's done, and + // the forwarding code will reprocess the options. + if (Index.oi_srindex == MAX_OPT_SIZE || IS_BCAST_DEST(DestType)) + return DEST_LOCAL; + else + return DEST_REMOTE; + +} + +//* TDUserRcv - Completion routing for a user transfer data. +// +// This is the completion handle for TDs invoked because we need to give data to a +// upper layer client. All we really do is call the upper layer handler with +// the data. +// +// Input: NetContext - Pointer to the net table entry on which we received this. +// Packet - Packet we received into. +// Status - Final status of copy. +// DataSize - Size in bytes of data transferred. +// +// Returns: Nothing +// +void +TDUserRcv(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, + uint DataSize) +{ + NetTableEntry *NTE = (NetTableEntry *)NetContext; + Interface *SrcIF; + TDContext *Context = (TDContext *)Packet->ProtocolReserved; + CTELockHandle Handle; + uchar DestType; + IPRcvBuf RcvBuf; + IPOptInfo OptInfo; + IPHeader *Header; + + if (Status == NDIS_STATUS_SUCCESS) { + Header = (IPHeader *)Context->tdc_header; + OptInfo.ioi_ttl = Header->iph_ttl; + OptInfo.ioi_tos = Header->iph_tos; + OptInfo.ioi_flags = (net_short(Header->iph_offset) >> 13) & IP_FLAG_DF; + if (Context->tdc_hlength != sizeof(IPHeader)) { + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = Context->tdc_hlength - sizeof(IPHeader); + } else { + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + } + + DestType = Context->tdc_dtype; + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_owner = IPR_OWNER_IP; + RcvBuf.ipr_buffer = (uchar *)Context->tdc_buffer; + RcvBuf.ipr_size = DataSize; + + DeliverToUser(NTE, Context->tdc_nte, Header, Context->tdc_hlength, + &RcvBuf, DataSize, &OptInfo, DestType); + // If it's a broadcast packet forward it on. + if (IS_BCAST_DEST(DestType)) + IPForward(NTE, Header, Context->tdc_hlength, RcvBuf.ipr_buffer, DataSize, + NULL, 0, DestType); + } + + SrcIF = NTE->nte_if; + CTEGetLockAtDPC(&SrcIF->if_lock, &Handle); + + Context->tdc_common.pc_link = SrcIF->if_tdpacket; + SrcIF->if_tdpacket = Packet; + CTEFreeLockFromDPC(&SrcIF->if_lock, Handle); + + return; + +} + + +//* IPRcv - Receive an incoming IP datagram. +// +// This is the routine called by the link layer module when an incoming IP +// datagram is to be processed. We validate the datagram (including doing +// the xsum), copy and process incoming options, and decide what to do with it. +// +// Entry: MyContext - The context valued we gave to the link layer. +// Data - Pointer to the data buffer. +// DataSize - Size in bytes of the data buffer. +// TotalSize - Total size in bytes available. +// LContext1 - 1st link context. +// LContext2 - 2nd link context. +// BCast - Indicates whether or not packet was received on bcast address. +// +// Returns: Nothing. +// +void +IPRcv(void *MyContext, void *Data, uint DataSize, uint TotalSize, NDIS_HANDLE LContext1, + uint LContext2, uint BCast) +{ + IPHeader UNALIGNED *IPH = (IPHeader UNALIGNED *)Data; + NetTableEntry *NTE = (NetTableEntry *)MyContext; // Local NTE received on + NetTableEntry *DestNTE; // NTE to receive on. + Interface *RcvIF; // Interface corresponding to NTE. + CTELockHandle Handle; + PNDIS_PACKET TDPacket; // NDIS packet used for TD. + TDContext *TDC = (TDContext *)NULL; // Transfer data context. + NDIS_STATUS Status; + IPAddr DAddr; // Dest. IP addr. of received packet. + uint HeaderLength; // Size in bytes of received header. + uint IPDataLength; // Length in bytes of IP (including UL) data in packet. + IPOptInfo OptInfo; // Incoming header information. + uchar DestType; // Type (LOCAL, REMOTE, SR) of Daddr. + IPRcvBuf RcvBuf; + +#if 0 + CTECheckMem(RcvFile); // Check heap status. +#endif + + IPSInfo.ipsi_inreceives++; + + // Make sure we actually have data. + if (DataSize) { + + // Check the header length, the xsum and the version. If any of these + // checks fail silently discard the packet. + HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2); + if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= DataSize && + xsum(Data, HeaderLength) == (ushort)0xffff) { + + // Check the version, and sanity check the total length. + IPDataLength = (uint)net_short(IPH->iph_length); + if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION && + IPDataLength > sizeof(IPHeader) && IPDataLength <= TotalSize) { + + IPDataLength -= HeaderLength; + Data = (uchar *)Data + HeaderLength; + DataSize -= HeaderLength; + + DAddr = IPH->iph_dest; + DestNTE = NTE; + + // Find local NTE, if any. + DestType = GetLocalNTE(DAddr, &DestNTE); + + // Check to see if this is a non-broadcast IP address that + // came in as a link layer broadcast. If it is, throw it out. + // This is an important check for DHCP, since if we're + // DHCPing an interface all otherwise unknown addresses will + // come in as DEST_LOCAL. This check here will throw them out + // if they didn't come in as unicast. + + if (BCast && !IS_BCAST_DEST(DestType)) { + IPSInfo.ipsi_inhdrerrors++; + return; // Non bcast packet on bcast address. + } + + OptInfo.ioi_ttl = IPH->iph_ttl; + OptInfo.ioi_tos = IPH->iph_tos; + OptInfo.ioi_flags = (net_short(IPH->iph_offset) >> 13) & + IP_FLAG_DF; + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + + if (DestType < DEST_REMOTE) { + // It's either local or some sort of broadcast. + + // The data probably belongs at this station. If there + // aren't any options, it definetly belongs here, and we'll + // dispatch it either to our reasssmbly code or to the + // deliver to user code. If there are options, we'll check + // them and then either handle the packet locally or pass it + // to our forwarding code. + + if (HeaderLength != sizeof(IPHeader)) { + // We have options. + + uchar NewDType; + NewDType = CheckLocalOptions(NTE, IPH, &OptInfo, + DestType); + if (NewDType != DEST_LOCAL) { + if (NewDType == DEST_REMOTE) + goto forward; + else { + IPSInfo.ipsi_inhdrerrors++; + return; // Bad Options. + } + } + } + + // Before we go further, if we have a filter installed + // call it to see if we should take this. + + if (ForwardFilterPtr != NULL) { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(IPH, + Data, + DataSize, + NTE->nte_if->if_filtercontext, + NULL); + + if (Action != FORWARD) { + IPSInfo.ipsi_indiscards++; + return; + } + } + + // No options. See if it's a fragment. If it is, call our + // reassembly handler. + if ((IPH->iph_offset & ~(IP_DF_FLAG | IP_RSVD_FLAG)) == 0) { + + // We don't have a fragment. If the data all fits, + // handle it here. Otherwise transfer data it. + +#ifdef VXD + if (IPDataLength > DataSize) { + // Data isn't all in the buffer. +#else + // Make sure data is all in buffer, and directly + // accesible. + if ((IPDataLength > DataSize) || + !(NTE->nte_flags & NTE_COPY)) { +#endif + // The data isn't all here. Transfer data it. + RcvIF = NTE->nte_if; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDPacket = RcvIF->if_tdpacket; + + if (TDPacket != (PNDIS_PACKET)NULL) { + + TDC = (TDContext *)TDPacket->ProtocolReserved; + RcvIF->if_tdpacket = TDC->tdc_common.pc_link; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + + TDC->tdc_nte = DestNTE; + TDC->tdc_dtype = DestType; + TDC->tdc_hlength = (uchar)HeaderLength; + CTEMemCopy(TDC->tdc_header, IPH, + HeaderLength + 8); + Status = (*(RcvIF->if_transfer))( + RcvIF->if_lcontext, LContext1, + LContext2, HeaderLength, IPDataLength, + TDPacket, &IPDataLength); + + // Check the status. If it's success, call the + // receive procedure. Otherwise, if it's pending + // wait for the callback. + Data = TDC->tdc_buffer; + if (Status != NDIS_STATUS_PENDING) { + if (Status != NDIS_STATUS_SUCCESS) { + IPSInfo.ipsi_indiscards++; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDC->tdc_common.pc_link = + RcvIF->if_tdpacket; + RcvIF->if_tdpacket = TDPacket; + CTEFreeLockFromDPC(&RcvIF->if_lock, + Handle); + return; + } + } else + return; // Status is pending. + } else { // Couldn't get a packet. + IPSInfo.ipsi_indiscards++; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + return; + } + } + + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_owner = IPR_OWNER_IP; + RcvBuf.ipr_buffer = (uchar *)Data; + RcvBuf.ipr_size = IPDataLength; + // When we get here, we have the whole packet. Deliver + // it. + DeliverToUser(NTE, DestNTE, IPH, HeaderLength, &RcvBuf, + IPDataLength, &OptInfo, DestType); + // When we're here, we're through with the packet + // locally. If it's a broadcast packet forward it on. + if (IS_BCAST_DEST(DestType)) { + IPForward(NTE, IPH, HeaderLength, Data, IPDataLength, + NULL, 0, DestType); + } + if (TDC != NULL) { + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDC->tdc_common.pc_link = RcvIF->if_tdpacket; + RcvIF->if_tdpacket = TDPacket; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + } + return; + } else { + // This is a fragment. Reassemble it. + IPReassemble(NTE, DestNTE, IPH, HeaderLength, Data, + DataSize, IPDataLength, DestType, LContext1, + LContext2); + return; + } + + } + + // Not for us, may need to be forwarded. It might be an outgoing + // broadcast that came in through a source route, so we need to + // check that. +forward: + if (DestType != DEST_INVALID) + IPForward(NTE, IPH, HeaderLength, Data, DataSize, + LContext1, LContext2, DestType); + else + IPSInfo.ipsi_inaddrerrors++; + return; + } // Bad version + } // Bad checksum + + } // No data + + IPSInfo.ipsi_inhdrerrors++; +} + +//* IPTDComplete - IP Transfer data complete handler. +// +// This is the routine called by the link layer when a transfer data completes. +// +// Entry: MyContext - Context value we gave to the link layer. +// Packet - Packet we originally gave to transfer data. +// Status - Final status of command. +// BytesCopied - Number of bytes copied. +// +// Exit: Nothing +// +void +IPTDComplete(void *MyContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint BytesCopied) +{ + TDContext *TDC = (TDContext *)Packet->ProtocolReserved; + + if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_FW)) + if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_RA)) + TDUserRcv(MyContext, Packet, Status, BytesCopied); + else + RATDComplete(MyContext, Packet, Status, BytesCopied); + else + SendFWPacket(Packet, Status, BytesCopied); + + +} diff --git a/private/ntos/tdi/tcpip/ip/iproute.c b/private/ntos/tdi/tcpip/ip/iproute.c new file mode 100644 index 000000000..3a0bb109d --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iproute.c @@ -0,0 +1,4703 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1995 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iproute.c - IP routing routines. +// +// This file contains all the routines related to IP routing, including +// routing table lookup and management routines. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "info.h" +#include "tdistat.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipxmit.h" +#include "igmp.h" +#include "tdiinfo.h" +#include "ipfilter.h" + +extern NetTableEntry *NetTableList; // Pointer to the net table. +extern NetTableEntry *DHCPNTE; // Pointer to NTE being DHCP'd. + +extern NetTableEntry *LoopNTE; // Pointer to loopback NTE. +extern Interface LoopInterface; // Pointer to loopback interface. + +extern AddrTypeCache ATCache[]; +extern int ATCIndex; + +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); +extern uchar ParseRcvdOptions(IPOptInfo *, OptIndex *); +extern void ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr, + uint NewMTU); + +extern Interface *IFList; +extern Interface *FirstIF; + +#define ROUTE_TABLE_SIZE 32 // Size of the route table. +DEFINE_LOCK_STRUCTURE(RouteTableLock) + +#define FWPACKET_GROW_AMOUNT 20 + +#define FW_BUF_SIZE 256 // Size of a forwarding buffer. + +#define FW_BUF_GROW_AMOUNT 30720 // Enough for 20 Ethernet packets. + +#define NO_SR 0 + +RouteTableEntry *RouteTable[ROUTE_TABLE_SIZE]; + +DEFINE_LOCK_STRUCTURE(FWPacketFreeLock) +DEFINE_LOCK_STRUCTURE(FWBufFreeLock) + +PNDIS_PACKET FWPacketFree; // Free list of forwarding packets. +PNDIS_BUFFER FWBufFree; // Free list of forwarding buffers. + +uint MaxFWPackets; // Maximum number of forward packets allowed. +uint CurrentFWPackets; // Number of forwarding packets currently + // allocated. +uint MaxFWBufferSize; // Maximum number of forwarding buffers allowed. +uint CurrentFWBufferSize; // Number of forwarding buffers allocated. + +uchar ForwardPackets; // Flag indicating whether we should forward. +uchar RouterConfigured; // TRUE if we were initially + // configured as a router. +uchar ForwardBCast; // Flag indicating if we should forward bcasts. +RouteSendQ *BCastRSQ; + +uint DefGWConfigured; // Number of default gateways configed. +uint DefGWActive; // Number of def. gateways active. + +uint DeadGWDetect; +uint PMTUDiscovery; + +ProtInfo *RtPI = NULL; + +IPMask IPMaskTable[] = { + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSC_MASK, + CLASSC_MASK, + CLASSD_MASK, + CLASSE_MASK }; + +extern void TransmitFWPacket(PNDIS_PACKET, uint); + +uint MTUTable[] = { + + 65535 - sizeof(IPHeader), + 32000 - sizeof(IPHeader), + 17914 - sizeof(IPHeader), + 8166 - sizeof(IPHeader), + 4352 - sizeof(IPHeader), + 2002 - sizeof(IPHeader), + 1492 - sizeof(IPHeader), + 1006 - sizeof(IPHeader), + 508 - sizeof(IPHeader), + 296 - sizeof(IPHeader), + MIN_VALID_MTU - sizeof(IPHeader) +}; + +CTETimer IPRouteTimer; + +// Pointer to callout routine for dial on demand. +IPMapRouteToInterfacePtr DODCallout; + +// Pointer to packet filter handler. +IPPacketFilterPtr ForwardFilterPtr; + +RouteInterface DummyInterface; // Dummy interface. + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +int InitRouting(IPConfigInfo *ci); + +#pragma alloc_text(INIT, InitRouting) + +#endif // ALLOC_PRAGMA +#endif // NT + + +#define InsertAfterRTE(P, R) (R)->rte_next = (P)->rte_next;\ + (P)->rte_next = (R) + +#define InsertRTE(R) {\ + RouteTableEntry *__P__; \ + __P__ = FindInsertPoint((R)); \ + InsertAfterRTE(__P__, (R)); \ + } + +#define RemoveRTE(P, R) (P)->rte_next = (R)->rte_next; + +//** DuumyXmit - Dummy interface transmit handler. +// +// A dummy routine that should never be called. +// +// Entry: Context - NULL. +// Packet - Pointer to packet to be transmitted. +// Dest - Destination addres of packet. +// RCE - Pointer to RCE (should be NULL). +// +// Returns: NDIS_STATUS_PENDING +// +NDIS_STATUS +DummyXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE) +{ + DbgPrint("TCPIP: Dummy Xmit called - NOT GOOD\n"); + CTEAssert(FALSE); + return NDIS_STATUS_SUCCESS; +} + +//* DummyXfer - Dummy interface transfer data routine. +// +// A dummy routine that should never be called. +// +// Entry: Context - NULL. +// TDContext - Original packet that was sent. +// Dummy - Unused +// Offset - Offset in frame from which to start copying. +// BytesToCopy - Number of bytes to copy. +// DestPacket - Packet describing buffer to copy into. +// BytesCopied - Place to return bytes copied. +// +// Returns: NDIS_STATUS_SUCCESS +// +NDIS_STATUS +DummyXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy, + PNDIS_PACKET DestPacket, uint *BytesCopied) +{ + DbgPrint("TCPIP: DummyXfer called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return NDIS_STATUS_FAILURE; +} + +//* DummyClose - Dummy close routine. +// +// A dummy routine that should never be called. +// +// Entry: Context - Unused. +// +// Returns: Nothing. +// +void +DummyClose(void *Context) +{ + DbgPrint("TCPIP: Dummy Close called - NOT GOOD\n"); + + CTEAssert(FALSE); +} + +//* DummyInvalidate - . +// +// A dummy routine that should never be called. +// +// Entry: Context - Unused. +// RCE - Pointer to RCE to be invalidated. +// +// Returns: Nothing. +// +void +DummyInvalidate(void *Context, RouteCacheEntry *RCE) +{ + DbgPrint("TCPIP: Dummy Invalidate called - NOT GOOD\n"); + + CTEAssert(FALSE); + +} + +//* DummyQInfo - Dummy query information handler. +// +// A dummy routine that should never be called. +// +// Input: IFContext - Interface context (unused). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// Context - Pointer to context block. +// +// Returns: Status of attempt to query information. +// +int +DummyQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + DbgPrint("TCPIP: DummyQInfo called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TDI_INVALID_REQUEST; +} + +//* DummySetInfo - Dummy query information handler. +// +// A dummy routine that should never be called. +// +// Input: IFContext - Interface context (unused). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// +// Returns: Status of attempt to query information. +// +int +DummySetInfo(void *IFContext, TDIObjectID *ID, void *Buffer, uint Size) +{ + DbgPrint("TCPIP: DummySetInfo called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TDI_INVALID_REQUEST; +} + +//* DummyAddAddr - Dummy add address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +DummyAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + CTEAssert(FALSE); + + return TRUE; +} + +//* DummyDelAddr - Dummy del address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +DummyDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + DbgPrint("TCPIP: DummyAddAddr called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TRUE; +} + +//* DummyGetEList - Dummy get entity list. +// +// A dummy routine that should never be called. +// +// Input: Context - Unused. +// EntityList - Pointer to entity list to be filled in. +// Count - Pointer to number of entries in the list. +// +// Returns Status of attempt to get the info. +// +int +DummyGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + DbgPrint("TCPIP: DummyGetEList called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return FALSE; +} + +#ifdef _PNP_POWER +//* DerefIF - Dereference an interface. +// +// Called when we need to dereference an interface. We decrement the +// refcount, and if it goes to zero we signal whoever is blocked on +// it. +// +// Input: IF - Interfaec to be dereferenced. +// +// Returns: Nothing. +// +void +DerefIF(Interface *IF) +{ + uint Original; + + Original = CTEInterlockedAddUlong( + &IF->if_refcount, + (ULONG)-1, + &RouteTableLock + ); + if (Original != 1) { + return; + } else { + // We just decremented the last reference. Wake whoever is + // blocked on it. + CTEAssert(IF->if_block != NULL); + CTESignal(IF->if_block, NDIS_STATUS_SUCCESS); + } +} + +//* LockedDerefIF - Dereference an interface w/RouteTableLock held. +// +// Called when we need to dereference an interface. We decrement the +// refcount, and if it goes to zero we signal whoever is blocked on +// it. The difference here is that we assume the caller already holds +// the RouteTableLock. +// +// Input: IF - Interfaec to be dereferenced. +// +// Returns: Nothing. +// +void +LockedDerefIF(Interface *IF) +{ + uint Original; + + IF->if_refcount--; + + if (IF->if_refcount != 0) { + return; + } else { + // We just decremented the last reference. Wake whoever is + // blocked on it. + CTEAssert(IF->if_block != NULL); + CTESignal(IF->if_block, NDIS_STATUS_SUCCESS); + } +} +#endif + +//* GetHashMask - Get mask to use with address when hashing. +// +// Called when we need to decide on the mask to use when hashing. If the +// supplied mask is the host mask or the default mask, we'll use that. Else +// if the supplied mask is at least as specific as the net mask, we'll use the +// net mask. Otherwise we drop back to the default mask. +// +// Input: Destination - Destination we'll be hashing on. +// Mask - Caller supplied mask. +// +// Returns: Mask to use. +// +IPMask +GetHashMask(IPAddr Destination, IPMask Mask) +{ + IPMask NetMask; + + if (Mask == HOST_MASK || Mask == DEFAULT_MASK) + return Mask; + + NetMask = IPNetMask(Destination); + + if ((NetMask & Mask) == NetMask) + return NetMask; + + return DEFAULT_MASK; + +} + +//** AddrOnIF - Check to see if a given address is local to an IF +// +// Called when we want to see if a given address is a valid local address +// for an interface. We walk down the chain of NTEs in the interface, and +// see if we get a match. We assume the caller holds the RouteTableLock +// at this point. +// +// Input: IF - Interface to check. +// Addr - Address to check. +// +// Returns: TRUE if Addr is an address for IF, FALSE otherwise. +// +uint +AddrOnIF(Interface *IF, IPAddr Addr) +{ + NetTableEntry *NTE; + + NTE = IF->if_nte; + while (NTE != NULL) { + if ((NTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(NTE->nte_addr, Addr)) + return TRUE; + else + NTE = NTE->nte_ifnext; + } + + return FALSE; +} + +//** BestNTEForIF - Find the 'best match' NTE on a given interface. +// +// This is a utility function that takes an address and tries to find the +// 'best match' NTE on a given interface. This is really only useful when we +// have multiple IP addresses on a single interface. +// +// Input: Address - Source address of packet. +// IF - Pointer to IF to be searched. +// +// Returns: The 'best match' NTE. +// +NetTableEntry * +BestNTEForIF(IPAddr Address, Interface *IF) +{ + NetTableEntry *CurrentNTE, *FoundNTE; + + if (IF->if_nte != NULL) { + // Walk the list of NTEs, looking for a valid one. + CurrentNTE = IF->if_nte; + FoundNTE = NULL; + do { + if (CurrentNTE->nte_flags & NTE_VALID) { + if (IP_ADDR_EQUAL(Address & CurrentNTE->nte_mask, + CurrentNTE->nte_addr & CurrentNTE->nte_mask)) + return CurrentNTE; + else + if (FoundNTE == NULL) + FoundNTE = CurrentNTE; + + } + + CurrentNTE = CurrentNTE->nte_ifnext; + } while (CurrentNTE != NULL); + + // If we found a match, or we didn't and the destination is not + // a broadcast, return the result. We have special case code to + // handle broadcasts, since the interface doesn't really matter there. + if (FoundNTE != NULL || (!IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) && + !IP_ADDR_EQUAL(Address, IP_ZERO_BCST))) + return FoundNTE; + + } + + // An 'anonymous' I/F, or the address we're reaching is a broadcast and the + // first interface has no address. Find a valid (non-loopback) address. + for (CurrentNTE = NetTableList; CurrentNTE != NULL; + CurrentNTE = CurrentNTE->nte_next) { + if (CurrentNTE != LoopNTE && (CurrentNTE->nte_flags & NTE_VALID)) + return CurrentNTE; + + } + + return NULL; + +} + +//** IsBCastonNTE - Determine if the specified addr. is a bcast on a spec. NTE. +// +// This routine is called when we need to know if an address is a broadcast +// on a particular net. We check in the order we expect to be most common - a +// subnet bcast, an all ones broadcast, and then an all subnets broadcast. We +// return the type of broadcast it is, or return DEST_LOCAL if it's not a +// broadcast. +// +// Entry: Address - Address in question. +// NTE - NetTableEntry to check Address against. +// +// Returns: Type of broadcast. +// +uchar +IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE) +{ + IPMask Mask; + IPAddr BCastAddr; + + BCastAddr = NTE->nte_if->if_bcast; + + if (NTE->nte_flags & NTE_VALID) { + + Mask = NTE->nte_mask; + + if(Mask != 0xFFFFFFFF) + { + if (IP_ADDR_EQUAL(Address, (NTE->nte_addr & Mask) | (BCastAddr & ~Mask))) + return DEST_SN_BCAST; + } + + // See if it's an all subnet's broadcast. + if (!CLASSD_ADDR(Address)) { + Mask = IPNetMask(Address); + + if (IP_ADDR_EQUAL(Address, + (NTE->nte_addr & Mask) | (BCastAddr & ~Mask))) + return DEST_BCAST; + } else { + // This is a class D address. If we're allowed to receive + // mcast datagrams, check our list. + + if (IGMPLevel == 2) { + IGMPAddr *AddrPtr; + CTELockHandle Handle; + + CTEGetLock(&NTE->nte_lock, &Handle); + AddrPtr = NTE->nte_igmplist; + while (AddrPtr != NULL) { + if (IP_ADDR_EQUAL(Address, AddrPtr->iga_addr)) + break; + else + AddrPtr = AddrPtr->iga_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + if (AddrPtr != NULL) + return DEST_MCAST; + } + } + } + + // A global bcast is certainly a bcast on this net. + if (IP_ADDR_EQUAL(Address, BCastAddr)) + return DEST_BCAST; + + return DEST_LOCAL; + +} + +//** InvalidSourceAddress - Check to see if a source address is invalid. +// +// This function takes an input address and checks to see if it is valid +// if used as the source address of an incoming packet. An address is invalid +// if it's 0, -1, a Class D or Class E address, is a net or subnet broadcast, +// or has a 0 subnet or host part. +// +// Input: Address - Address to be check. +// +// Returns: FALSE if the address is not invalid, TRUE if it is invalid. +// +uint +InvalidSourceAddress(IPAddr Address) +{ + NetTableEntry *NTE; // Pointer to current NTE. + IPMask Mask; // Mask for address. + uchar Result; // Result of broadcast check. + IPMask SNMask; + IPAddr MaskedAddress; + IPAddr LocalAddress; + + + if ( !CLASSD_ADDR(Address) && + !CLASSE_ADDR(Address) && + !IP_ADDR_EQUAL(Address, IP_ZERO_BCST) && + !IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) + ) { + // It's not an obvious broadcast. See if it's an all subnets + // broadcast, or has a zero host part. + Mask = IPNetMask(Address); + MaskedAddress = Address & Mask; + + if (!IP_ADDR_EQUAL(Address, MaskedAddress) && + !IP_ADDR_EQUAL(Address, (MaskedAddress | ~Mask)) + ) { + // It's not an all subnet's broadcast, and it has a non-zero + // host/subnet part. Walk our local IP addresses, and see if it's + // a subnet broadcast. + NTE = NetTableList; + do { + + LocalAddress = NTE->nte_addr; + + if ((NTE->nte_flags & NTE_VALID) && + !IP_LOOPBACK(LocalAddress)) { + + Mask = NTE->nte_mask; + MaskedAddress = LocalAddress & Mask; + + if (IP_ADDR_EQUAL(Address, MaskedAddress) || + IP_ADDR_EQUAL(Address, + (MaskedAddress | + (NTE->nte_if->if_bcast & ~Mask)))) { + return TRUE; + } + } + + + NTE = NTE->nte_next; + } while (NTE != NULL); + + return FALSE; + } + } + + return TRUE; +} + +//** FlushATCache - Flush an address from the ATCache +// +// This function takes an input address, and removes it from the ATCache, +// if it is present. +// +// Input: Address - Address to be check. +// +// Returns: Destination type. +// +void +FlushATCache(IPAddr Address) +{ + uint i; + + + for (i=0; inte_addr, Address) && + (NTE->nte_flags & NTE_VALID)) { + Result = DEST_LOCAL; + goto gat_exit; + } + + if ((Result = IsBCastOnNTE(Address, NTE)) != DEST_LOCAL) { + goto gat_exit; + } + + // See if the destination has a valid host part. + if (NTE->nte_flags & NTE_VALID) { + SNMask = NTE->nte_mask; + if (IP_ADDR_EQUAL(Address & SNMask, NTE->nte_addr & SNMask)) { + // On this subnet. See if the host part is invalid. + + if (IP_ADDR_EQUAL(Address & SNMask, Address)) { + Result = DEST_INVALID; // Invalid 0 host part. + goto gat_exit; + } + } + } + NTE = NTE->nte_next; + } while (NTE != NULL); + + // It's not a local address, see if it's loopback. + if (IP_LOOPBACK(Address)) { + Result = DEST_LOCAL; + goto gat_exit; + } + + // If we're doing IGMP, see if it's a Class D address. If it it, + // return that. + if (CLASSD_ADDR(Address)) { + if (IGMPLevel != 0) { + Result = DEST_REM_MCAST; + goto gat_exit; + } + else { + Result = DEST_INVALID; + goto gat_exit; + } + } + + Mask = IPNetMask(Address); + + // Now check remote broadcast. When we get here we know that the + // address is not a global broadcast, a subnet broadcast for a subnet + // of which we're a member, or an all-subnets broadcast for a net of + // which we're a member. Since we're avoiding making assumptions about + // all subnet of a net having the same mask, we can't really check for + // a remote subnet broadcast. We'll use the net mask and see if it's + // a remote all-subnet's broadcast. + if (IP_ADDR_EQUAL(Address, (Address & Mask) | (IP_LOCAL_BCST & ~Mask))) { + Result = DEST_REM_BCAST; + goto gat_exit; + } + + // Check for invalid 0 parts. All we can do from here is see if he's + // sending to a remote net with all zero subnet and host parts. We + // can't check to see if he's sending to a remote subnet with an all + // zero host part. + if (IP_ADDR_EQUAL(Address, Address & Mask) || + IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) { + Result = DEST_INVALID; + goto gat_exit; + } + + // Must be remote. + Result = DEST_REMOTE; + goto gat_exit; + } + + Result = DEST_INVALID; + +gat_exit: + + ++ATCIndex; + + i = ATCIndex & ATC_MASK; + + ATCache[i].atc_addr = Address; + ATCache[i].atc_type = Result; + ATCache[i].atc_flags = 1; + return(Result); + +} + +//** IPHash - IP hash function. +// +// This is the function to compute the hash index from a masked address. +// +// Input: Address - Masked address to be hashed. +// +// Returns: Hashed value. +// +uint +IPHash(IPAddr Address) +{ + uchar *i = (uchar *)&Address; + return (i[0] + i[1] + i[2] + i[3]) & (ROUTE_TABLE_SIZE-1); +} + +//** GetLocalNTE - Get the local NTE for an incoming packet. +// +// Called during receive processing to find a matching NTE for a packet. +// First we check against the NTE we received it on, then against any NTE. +// +// Input: Address - The dest. address of the packet. +// NTE - Pointer to NTE packet was received on - filled in on +// exit w/correct NTE. +// +// Returns: DEST_LOCAL if the packet is destined for this host, DEST_REMOTE if it needs to +// be routed, DEST_SN_BCAST or DEST_BCAST if it's some sort of a broadcast. +uchar +GetLocalNTE(IPAddr Address, NetTableEntry **NTE) +{ + NetTableEntry *LocalNTE = *NTE; + IPMask Mask; + uchar Result; + int i; + Interface *LocalIF; + NetTableEntry *OriginalNTE; + + // Quick check to see if it is for the NTE it came in on (the common case). + if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) && + (LocalNTE->nte_flags & NTE_VALID)) + return DEST_LOCAL; // For us, just return. + + // Now check to see if it's a broadcast of some sort on the interface it + // came in on. + if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) + return Result; + + // The common cases failed us. Loop through the NetTable and see if + // it is either a valid local address or is a broadcast on one of the NTEs + // on the incoming interface. We won't check the NTE we've already looked + // at. We look at all NTEs, including the loopback NTE, because a loopback + // frame could come through here. Also, frames from ourselves to ourselves + // will come in on the loopback NTE. + + i = 0; + LocalIF = LocalNTE->nte_if; + OriginalNTE = LocalNTE; + LocalNTE = NetTableList; + do { + if (LocalNTE != OriginalNTE) { + if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) && + (LocalNTE->nte_flags & NTE_VALID)) { + *NTE = LocalNTE; + return DEST_LOCAL; // For us, just return. + } + + // If this NTE is on the same interface as the NTE it arrived on, + // see if it's a broadcast. + if (LocalIF == LocalNTE->nte_if) + if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) { + *NTE = LocalNTE; + return Result; + } + + } + + LocalNTE = LocalNTE->nte_next; + + } while (LocalNTE != NULL); + + // It's not a local address, see if it's loopback. + if (IP_LOOPBACK(Address)) { + *NTE = LoopNTE; + return DEST_LOCAL; + } + + // If it's a class D address and we're receiveing multicasts, handle it + // here. + if (CLASSD_ADDR(Address)) { + if (IGMPLevel != 0) + return DEST_REM_MCAST; + else + return DEST_INVALID; + } + + // It's not local. Check to see if maybe it's a net broadcast for a net + // of which we're not a member. If so, return remote bcast. We can't check + // for subnet broadcast of subnets for which we're not a member, since we're + // not making assumptions about all subnets of a single net having the + // same mask. If we're here it's not a subnet broadcast for a net of which + // we're a member, so we don't know a subnet mask for it. We'll just use + // the net mask. + Mask = IPNetMask(Address); + if (IP_ADDR_EQUAL(Address, (Address & Mask) | + ((*NTE)->nte_if->if_bcast & ~Mask))) + return DEST_REM_BCAST; + + // If it's to the 0 address, or a Class E address, or has an all-zero + // subnet and net part, it's invalid. + + + if (IP_ADDR_EQUAL(Address, IP_ZERO_BCST) || + IP_ADDR_EQUAL(Address, (Address & Mask)) || + CLASSE_ADDR(Address)) + return DEST_INVALID; + + // If we're DHCPing the interface on which this came in we'll accept this. + // If it came in as a broadcast a check in IPRcv() will reject it. If it's + // a unicast to us we'll pass it up. + if (DHCPNTE != NULL && DHCPNTE == *NTE) { + return DEST_LOCAL; + } + + return DEST_REMOTE; +} + + +//** FindSpecificRTE - Look for a particular RTE. +// +// Called when we're adding a route and want to find a particular RTE. +// We take in the destination, mask, first hop, and src addr, and search +// the appropriate routeing table chain. We assume the caller has the +// RouteTableLock held. If we find the match, we'll return a pointer to the +// RTE with the RTE lock held, as well as a pointer to the previous RTE in +// the chain. +// +// Input: Dest - Destination to search for. +// Mask - Mask for destination. +// FirstHop - FirstHop to Dest. +// OutIF - Pointer to outgoing interface structure. +// PrevRTE - Place to put PrevRTE, if found. +// +// Returns: Pointer to matching RTE if found, or NULL if not. +// +RouteTableEntry * +FindSpecificRTE(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF, + RouteTableEntry **PrevRTE) +{ + uint Index; + IPMask HashMask; + RouteTableEntry *TempRTE, *CurrentRTE; + + HashMask = GetHashMask(Dest, Mask); + + Index = IPHash(Dest & HashMask); + TempRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next); + CurrentRTE = TempRTE->rte_next; + + // + // If this has been called because user mode was trying to set the route + // to INVALID, then the OUTIF will be DummyInterface, but we want to match + // any interface, since the IF will have already been plumbed by DODCallOut + // + + if(OutIF == (Interface *)&DummyInterface) + { + // + // Match everything but the interface + // + + while (CurrentRTE != NULL) + { + if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) && + CurrentRTE->rte_mask == Mask && + IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop)) + { + break; + } + + TempRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + } + else + { + // Walk the table, looking for a match. + while (CurrentRTE != NULL) { + // See if everything matches. + if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) && + CurrentRTE->rte_mask == Mask && + IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop) && + CurrentRTE->rte_if == OutIF) + break; + + TempRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + } + + *PrevRTE = TempRTE; + return CurrentRTE; + +} + +//** IsRouteICMP - This function is used by Router Discovery to determine +// how we learned about the route. We are not allowed to update or timeout +// routes that were not learned about via icmp. If the route is new then +// we treat it as icmp and add a new entry. +// Input: Dest - Destination to search for. +// Mask - Mask for destination. +// FirstHop - FirstHop to Dest. +// OutIF - Pointer to outgoing interface structure. +// +// Returns: TRUE if learned via ICMP, FALSE otherwise. +// +uint +IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF) +{ + RouteTableEntry *RTE; + RouteTableEntry *TempRTE; + + RTE = FindSpecificRTE(Dest, Mask, FirstHop, OutIF, &TempRTE); + + if (RTE == NULL) + return(TRUE); + + if (RTE->rte_proto == IRE_PROTO_ICMP) { + return(TRUE); + } else { + return(FALSE); + } +} + + +//** FindRTE - Find a matching RTE in a hash table chain. +// +// Called when we want to find a matching RTE. We take in a destination, +// a source, a hash index, and a maximum priority, and walk down the +// chain specified by the index looking for a matching RTE. If we can find +// one, we'll keep looking hoping for a match on the source address. +// +// The caller must hold the RouteTableLock before calling this function. +// +// Input: Dest - Destination we're trying to reach. +// Source - Source address to match. +// Index - Index of chain to search. +// MaxPri - Maximum acceptable priority. +// MinPri - Minimum acceptable priority. +// +// Returns: Pointer to RTE if found, or NULL if not. +// +RouteTableEntry * +FindRTE(IPAddr Dest, IPAddr Source, uint Index, uint MaxPri, uint MinPri) +{ + RouteTableEntry *CurrentRTE; + uint RTEPri; + uint Metric; + RouteTableEntry *FoundRTE; + + // First walk down the chain, skipping those RTEs that have a + // a priority greater than what we want. + + CurrentRTE = RouteTable[Index]; + + for (;;) { + if (CurrentRTE == NULL) + return NULL; // Hit end of chain, bounce out. + + if (CurrentRTE->rte_priority <= MaxPri) + break; // He's a possible match. + + // Priority is too big. Try the next one. + CurrentRTE = CurrentRTE->rte_next; + } + + FoundRTE = NULL; + + // When we get here, we have a locked RTE with a priority less than or + // equal to what was specifed. + // Examine it, and if it doesn't match try the next one. If it does match + // we'll stash it temporarily and keep looking for one that matches the + // specifed source. + for (;;) { + + // The invariant at the top of this loop is that CurrentRTE points to + // a candidate RTE, locked with the handle in CurrentHandle. + + if (CurrentRTE->rte_flags & RTE_VALID) { + // He's valid. Make sure he's at least the priority we need. If + // he is, see if he matches. Otherwise we're done. + + if (CurrentRTE->rte_priority < MinPri) { + // His priority is too small. Since the list is in sorted order, + // all following routes must have too low a priority, so we're + // done. + return NULL; + } + + if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, CurrentRTE->rte_dest)) { + // He's valid for this route. Save the current information, + // and look for a matching source. + FoundRTE = CurrentRTE; + + if (!IP_ADDR_EQUAL(Source, NULL_IP_ADDR) && + !AddrOnIF(CurrentRTE->rte_if, Source)) { + RTEPri = CurrentRTE->rte_priority; + Metric = CurrentRTE->rte_metric; + + CurrentRTE = CurrentRTE->rte_next; + + // We've save the info. Starting at the next RTE, look for + // an RTE that matches both the mask criteria and the source + // address. The search will terminate when we hit the end + // of the list, or the RTE we're examing has a different + // (presumably lesser) priority (or greater metric), or we + // find a match. + while (CurrentRTE != NULL && + CurrentRTE->rte_priority == RTEPri && + CurrentRTE->rte_metric == Metric) { + + + // Skip invalid route types. + if (CurrentRTE->rte_flags & RTE_VALID) { + if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, + CurrentRTE->rte_dest)) { + if (AddrOnIF(CurrentRTE->rte_if, Source)) { + // He matches the source. Free the old lock, + // and break out. + FoundRTE = CurrentRTE; + break; + } + } + + } + CurrentRTE = CurrentRTE->rte_next; + } + } + + // At this point, FoundRTE points to the RTE we want to return, + // and *Handle has the lock handle for the RTE. Break out. + break; + } + + } + + CurrentRTE = CurrentRTE->rte_next; + + if (CurrentRTE != NULL) { + continue; + } else + break; + } + + return FoundRTE; +} + +//* ValidateDefaultGWs - Mark all default gateways as valid. +// +// Called to one or all of our default gateways as up. The caller specifies +// the IP address of the one to mark as up, or NULL_IP_ADDR if they're all +// supposed to be marked up. We return a count of how many we marked as +// valid. +// +// Input: IP address of G/W to mark as up. +// +// Returns: Count of gateways marked as up. +// +uint +ValidateDefaultGWs(IPAddr Addr) +{ + RouteTableEntry *RTE; + uint Count = 0; + uint Now = CTESystemUpTime() / 1000L; + + RTE = RouteTable[IPHash(0)]; + + while (RTE != NULL) { + if (RTE->rte_mask == DEFAULT_MASK && !(RTE->rte_flags & RTE_VALID) && + (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(Addr, RTE->rte_addr))) { + RTE->rte_flags |= RTE_VALID; + RTE->rte_valid = Now; + Count++; + } + RTE = RTE->rte_next; + } + + DefGWActive += Count; + return Count; +} + +//* InvalidateRCEChain - Invalidate the RCEs on an RCE. +// +// Called to invalidate the RCE chain on an RTE. We assume the caller holds +// the route table lock. +// +// Input: RTE - RTE on which to invalidate RCEs. +// +// Returns: Nothing. +// +void +InvalidateRCEChain(RouteTableEntry *RTE) +{ + CTELockHandle RCEHandle; // Lock handle for RCE being updated. + RouteCacheEntry *TempRCE, *CurrentRCE; + Interface *OutIF; + + OutIF = RTE->rte_if; + + // If there is an RCE chain on this RCE, invalidate the RCEs on it. We still + // hold the RouteTableLock, so RCE closes can't happen. + + + CurrentRCE = RTE->rte_rcelist; + RTE->rte_rcelist = NULL; + + // Walk down the list, nuking each RCE. + while (CurrentRCE != NULL) { + + CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle); + + if (CurrentRCE->rce_flags & RCE_VALID) { + CTEAssert(CurrentRCE->rce_rte == RTE); + CurrentRCE->rce_flags &= ~RCE_VALID; + CurrentRCE->rce_rte = (RouteTableEntry *)OutIF; + if ((CurrentRCE->rce_flags & RCE_CONNECTED) && + CurrentRCE->rce_usecnt == 0) { + + (*(OutIF->if_invalidate))(OutIF->if_lcontext, CurrentRCE); +#ifdef _PNP_POWER + if (CurrentRCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(OutIF); + CurrentRCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } + } else + CTEAssert(FALSE); + + TempRCE = CurrentRCE->rce_next; + CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle); + CurrentRCE = TempRCE; + } + +} + +//** FindValidIFForRTE - Find a valid inteface for an RTE. +// +// Called when we're going to send a packet out a route that currently marked +// as disconnected. If we have a valid callout routine we'll call it to find +// the outgoing interface index, and set up the RTE to point at that interface. +// This routine is called with the RouteTableLock held. +// +// Input: RTE - A pointer to the RTE for the route being used. +// Destination - Destination IP address we're trying to reach. +// Source - Source IP address we're sending from. +// Protocol - Protocol type of packet that caused send. +// Buffer - Pointer to first part of packet that caused send. +// Length - Length of buffer. +// +// Returns: A pointer to the RTE, or NULL if that RTE couldn't be connected. +// +RouteTableEntry * +FindValidIFForRTE(RouteTableEntry *RTE, IPAddr Destination, IPAddr Source, + uchar Protocol, uchar *Buffer, uint Length) +{ + uint NewIFIndex; + Interface *NewIF; + NetTableEntry *NewNTE; + + if (DODCallout != NULL) { + // There is a callout. See if it can help us. + NewIFIndex = (*DODCallout)(RTE->rte_context, Destination, Source, + Protocol, Buffer, Length); + if (NewIFIndex != INVALID_IF_INDEX) { + // We got what should be a valid index. Walk our interface table list + // and see if we can find a matching interface structure. + for (NewIF = IFList; NewIF != NULL; NewIF = NewIF->if_next) { + if (NewIF->if_index == NewIFIndex) { + // Found one. + break; + } + } + if (NewIF != NULL) { + // We found a matching structure. Set the RTE interface to point + // to this, and mark as connected. + if (RTE->rte_addr != IPADDR_LOCAL) { + // See if the first hop of the route is a local address on this + // new interface. If it is, mark it as local. + for (NewNTE = NewIF->if_nte; NewNTE != NULL; + NewNTE = NewNTE->nte_ifnext) { + + // Don't look at him if he's not valid. + if (!(NewNTE->nte_flags & NTE_VALID)) { + continue; + } + + // See if the first hop in the RTE is equal to this IP + // address. + if (IP_ADDR_EQUAL(NewNTE->nte_addr, RTE->rte_addr)) { + // It is, so mark as local and quit looking. + RTE->rte_addr = IPADDR_LOCAL; + RTE->rte_type = IRE_TYPE_DIRECT; + break; + } + } + } + + // Set the RTE to the new interface, and mark him as valid. + RTE->rte_if = NewIF; + RTE->rte_flags |= RTE_IF_VALID; + RTE->rte_mtu = NewIF->if_mtu - sizeof(IPHeader); + return RTE; + } else + CTEAssert(FALSE); + } + } + + // Either the callout is NULL, or the callout couldn't map a inteface index. + return NULL; +} + +//** LookupRTE - Lookup a routing table entry. +// +// This routine looks up a routing table entry, and returns with the entry +// locked if it finds one. If it doesn't find one, it returns NULL. This +// routine assumes that the routing table is locked when it is called. +// +// The routeing table is organized as an open hash table. The table contains +// routes to hosts, subnets, and nets. Host routes are hashed on the host +// address, other non-default routes on the destination anded with the net +// mask, and default routes wind up in bucket 0. Within each bucket chain +// the routes are sorted with the greatest priority (i.e. number of bits in the +// route mask) first, and within each priority class the routes are sorted +// with the lowest metric first. The caller may specify a maximum priority +// for the route to be found. We look for routes in order of most specific to +// least specifc, i.e. first host routes, then other non-default routes, and +// finally default routes. We give preference to routes that are going out +// on an interface with an address that matches the input source address. +// +// It might be worthile in the future to split this up into multiple tables, +// so that we have a table for host routes, a table for non-default routes, +// and a list of default routes. +// +// Entry: Address - Address for which a route is to be found. +// Src - IPAddr of source (may be 0). +// MaxPri - Maximum priority of route to find. +// +// Returns: A pointer to the locked RTE if we find one, or NULL if we don't +// +RouteTableEntry * +LookupRTE(IPAddr Address, IPAddr Src, uint MaxPri) +{ + RouteTableEntry *RTE; + + // First try to find a host route, if we're allowed to. + if (MaxPri == HOST_ROUTE_PRI) { + RTE = FindRTE(Address, Src, IPHash(Address), MaxPri, MaxPri); + if (RTE != NULL) { + return RTE; + } + } + + // Don't have or weren't allowed to find a host route. See if we can + // find a non-default route. + if (MaxPri > DEFAULT_ROUTE_PRI) { + RTE = FindRTE(Address, Src, IPHash(Address & IPNetMask(Address)), + MaxPri, DEFAULT_ROUTE_PRI + 1); + if (RTE != NULL) { + return RTE; + } + } + + // No non-default route. Try a default route. + RTE = FindRTE(Address, Src, IPHash(0), MaxPri, DEFAULT_ROUTE_PRI); + + return RTE; + +} + +//** GetRouteContext - Routine to get the route context for a specific route. +// +// Called when we need to get the route context for a path, usually when we're adding +// a route derived from an existing route. We return the route context for the +// existing route, or NULL if we can't find one. +// +// Input: Destination - Destination address of path. +// Source - Source address of path. +// +// Returns: A ROUTE_CONTEXT, or NULL. +// +void * +GetRouteContext(IPAddr Destination, IPAddr Source) +{ + CTELockHandle Handle; + RouteTableEntry *RTE; + ROUTE_CONTEXT Context; + + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Destination, Source, HOST_ROUTE_PRI); + if (RTE != NULL) { + Context = RTE->rte_context; + } else + Context = NULL; + + CTEFreeLock(&RouteTableLock, Handle); + + return(Context); +} + +//* FindInsertPoint - Find out where to insert an RTE in the table. +// +// Called to find out where to insert an RTE. We hash into the table, +// and walk the chain until we hit the end or we find an RTE with a +// lesser priority or a greater metric. Once we find the appropriate spot +// we return a pointer to the RTE immediately prior to the one we want to +// insert. We assume the caller holds the lock on the route table when calling +// this function. +// +// Input: InsertRTE - RTE we're going to (eventually) insert. +// +// Returns: Pointer to RTE in insert after. +// +RouteTableEntry * +FindInsertPoint(RouteTableEntry *InsertRTE) +{ + RouteTableEntry *PrevRTE, *CurrentRTE; + IPMask HashMask, Mask; + uint Priority, Metric; + uint Index; + + Priority = InsertRTE->rte_priority; + Metric = InsertRTE->rte_metric; + + // First figure out where he should go. We'll hash on the whole address + // if the mask allows us to, or on the net portion if this is a non-default + // route. + + Mask = InsertRTE->rte_mask; + HashMask = GetHashMask(InsertRTE->rte_dest, Mask); + + Index = IPHash(InsertRTE->rte_dest & HashMask); + + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next); + CurrentRTE = PrevRTE->rte_next; + + // Walk the table, looking for a place to insert it. + while (CurrentRTE != NULL) { + if (CurrentRTE->rte_priority < Priority) { + break; + } + + if (CurrentRTE->rte_priority == Priority) { + // Priorities match. Check the metrics. + if (CurrentRTE->rte_metric > Metric) { + // Our metric is smaller than his, so we're done. + break; + } + } + + // Either his priority is greater than ours or his metric is less + // than or equal to ours. Check the next one. + PrevRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + + // At this point, we've either found the correct spot or hit the end + // of the list. + return PrevRTE; + +} + + +//** LookupNextHop - Look up the next hop +// +// Called when we need to find the next hop on our way to a destination. We +// call LookupRTE to find it, and return the appropriate information. +// +// In a PnP build, the interface is referenced here. +// +// Entry: Destination - IP address we're trying to reach. +// Src - Source address of datagram being routed. +// NextHop - Pointer to IP address of next hop (returned). +// MTU - Pointer to where to return max MTU used on the +// route. +// +// Returns: Pointer to outgoing interface if we found one, NULL otherwise. +// +Interface * +LookupNextHop(IPAddr Destination, IPAddr Src, IPAddr *NextHop, uint *MTU) +{ + CTELockHandle TableLock; // Lock handle for routing table. + RouteTableEntry *Route; // Pointer to route table entry for route. + Interface *IF; + + CTEGetLock(&RouteTableLock, &TableLock); + Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI); + + if (Route != (RouteTableEntry *)NULL) { + IF = Route->rte_if; + + // If this is a direct route, send straight to the destination. + *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination : + Route->rte_addr; + + *MTU = Route->rte_mtu; +#ifdef _PNP_POWER + IF->if_refcount++; +#endif + CTEFreeLock(&RouteTableLock, TableLock); + return IF; + } else { // Couldn't find a route. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } +} + +//** LookupNextHopWithBuffer - Look up the next hop, with packet information. +// +// Called when we need to find the next hop on our way to a destination and we +// have packet information that we may use for dial on demand support. We call +// LookupRTE to find it, and return the appropriate information. We may bring up +// the link if neccessary. +// +// In a PnP build, the interface is referenced here. +// +// Entry: Destination - IP address we're trying to reach. +// Src - Source address of datagram being routed. +// NextHop - Pointer to IP address of next hop (returned). +// MTU - Pointer to where to return max MTU used on the +// route. +// Protocol - Protocol type for packet that's causing this lookup. +// Buffer - Pointer to first part of packet causing lookup. +// Length - Length of Buffer. +// +// Returns: Pointer to outgoing interface if we found one, NULL otherwise. +// +Interface * +LookupNextHopWithBuffer(IPAddr Destination, IPAddr Src, IPAddr *NextHop, + uint *MTU, uchar Protocol, uchar *Buffer, uint Length) +{ + CTELockHandle TableLock; // Lock handle for routing table. + RouteTableEntry *Route; // Pointer to route table entry for route. + Interface *IF; + + CTEGetLock(&RouteTableLock, &TableLock); + Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI); + + if (Route != (RouteTableEntry *)NULL) { + + // If this is a direct route, send straight to the destination. + *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination : + Route->rte_addr; + + + // See if the route we found is connected. If not, try to connect it. + if (!(Route->rte_flags & RTE_IF_VALID)) { + Route = FindValidIFForRTE(Route, Destination, Src, Protocol, Buffer, + Length); + if (Route == NULL) { + // Couldn't bring it up. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } else + IF = Route->rte_if; + } else + IF = Route->rte_if; + + *MTU = Route->rte_mtu; +#ifdef _PNP_POWER + IF->if_refcount++; +#endif + CTEFreeLock(&RouteTableLock, TableLock); + return IF; + } else { // Couldn't find a route. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } +} + +//* RTReadNext - Read the next route in the table. +// +// Called by the GetInfo code to read the next route in the table. We assume +// the context passed in is valid, and the caller has the RouteTableLock. +// +// Input: Context - Pointer to a RouteEntryContext. +// Buffer - Pointer to an IPRouteEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +RTReadNext(void *Context, void *Buffer) +{ + RouteEntryContext *REContext = (RouteEntryContext *)Context; + IPRouteEntry *IPREntry = (IPRouteEntry *)Buffer; + RouteTableEntry *CurrentRTE; + uint i; + uint Now = CTESystemUpTime() / 1000L; + Interface *IF; + NetTableEntry *SrcNTE; + + CurrentRTE = REContext->rec_rte; + + // Fill in the buffer. + IF = CurrentRTE->rte_if; + + IPREntry->ire_dest = CurrentRTE->rte_dest; + IPREntry->ire_index = IF->if_index; + IPREntry->ire_metric1 = CurrentRTE->rte_metric; + IPREntry->ire_metric2 = IRE_METRIC_UNUSED; + IPREntry->ire_metric3 = IRE_METRIC_UNUSED; + IPREntry->ire_metric4 = IRE_METRIC_UNUSED; + IPREntry->ire_metric5 = IRE_METRIC_UNUSED; + if (IP_ADDR_EQUAL(CurrentRTE->rte_addr, IPADDR_LOCAL)) { + SrcNTE = BestNTEForIF(CurrentRTE->rte_dest, IF); + if (IF->if_nte != NULL && SrcNTE != NULL) + IPREntry->ire_nexthop = SrcNTE->nte_addr; + else + IPREntry->ire_nexthop = IPREntry->ire_dest; + } else { + IPREntry->ire_nexthop = CurrentRTE->rte_addr; + } + IPREntry->ire_type = (CurrentRTE->rte_flags & RTE_VALID ? + CurrentRTE->rte_type : IRE_TYPE_INVALID); + IPREntry->ire_proto = CurrentRTE->rte_proto; + IPREntry->ire_age = Now - CurrentRTE->rte_valid; + IPREntry->ire_mask = CurrentRTE->rte_mask; + IPREntry->ire_context = CurrentRTE->rte_context; + + // We've filled it in. Now update the context. + if (CurrentRTE->rte_next != NULL) { + REContext->rec_rte = CurrentRTE->rte_next; + return TRUE; + } else { + // The next RTE is NULL. Loop through the RouteTable looking for a new + // one. + i = REContext->rec_index + 1; + while (i < ROUTE_TABLE_SIZE) { + if (RouteTable[i] != NULL) { + REContext->rec_rte = RouteTable[i]; + REContext->rec_index = i; + return TRUE; + break; + } else + i++; + } + + REContext->rec_index = 0; + REContext->rec_rte = NULL; + return FALSE; + } + +} + +//* RTValidateContext - Validate the context for reading the route table. +// +// Called to start reading the route table sequentially. We take in +// a context, and if the values are 0 we return information about the +// first route in the table. Otherwise we make sure that the context value +// is valid, and if it is we return TRUE. +// We assume the caller holds the route table lock. +// +// Input: Context - Pointer to a RouteEntryContext. +// Valid - Where to return information about context being +// valid. +// +// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set +// to TRUE if input context is valid +// +uint +RTValidateContext(void *Context, uint *Valid) +{ + RouteEntryContext *REContext = (RouteEntryContext *)Context; + uint i; + RouteTableEntry *TargetRTE; + RouteTableEntry *CurrentRTE; + + i = REContext->rec_index; + TargetRTE = REContext->rec_rte; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetRTE == NULL) { + *Valid = TRUE; + do { + if ((CurrentRTE = RouteTable[i]) != NULL) { + break; + } + i++; + } while (i < ROUTE_TABLE_SIZE); + + if (CurrentRTE != NULL) { + REContext->rec_index = i; + REContext->rec_rte = CurrentRTE; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < ROUTE_TABLE_SIZE) { + CurrentRTE = RouteTable[i]; + while (CurrentRTE != NULL) { + if (CurrentRTE == TargetRTE) { + *Valid = TRUE; + return TRUE; + break; + } else { + CurrentRTE = CurrentRTE->rte_next; + } + } + + } + + // If we get here, we didn't find the matching RTE. + *Valid = FALSE; + return FALSE; + + } + +} + + +//* DeleteRTE - Delete an RTE. +// +// Called when we need to delete an RTE. We assume the caller has the +// RouteTableLock. We'll splice out the RTE, invalidate his RCEs, and +// free the memory. +// +// Input: PrevRTE - RTE in 'front' of one being deleted. +// RTE - RTE to be deleted. +// +// Returns: Nothing. +// +void +DeleteRTE(RouteTableEntry *PrevRTE, RouteTableEntry *RTE) +{ + PrevRTE->rte_next = RTE->rte_next; // Splice him from the table. + IPSInfo.ipsi_numroutes--; + + if (RTE->rte_mask == DEFAULT_MASK) { + // We're deleting a default route. + DefGWConfigured--; + if (RTE->rte_flags & RTE_VALID) + DefGWActive--; + if (DefGWActive == 0) + ValidateDefaultGWs(NULL_IP_ADDR); + } + + InvalidateRCEChain(RTE); + // Free the old route. + CTEFreeMem(RTE); + +} + +//* DeleteRTEOnIF - Delete all RTEs on a particular IF. +// +// A function called by RTWalk when we want to delete all RTEs on a particular +// inteface. We just check the I/F of each RTE, and if it matches we return +// FALSE. +// +// Input: RTE - RTE to check. +// Context - Interface on which we're deleting. +// +// Returns: FALSE if we want to delete it, TRUE otherwise. +// +uint +DeleteRTEOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF && !IP_ADDR_EQUAL(RTE->rte_dest, IF->if_bcast)) + return FALSE; + else + return TRUE; + +} + +//* InvalidateRCEOnIF - Invalidate all RCEs on a particular IF. +// +// A function called by RTWalk when we want to invalidate all RCEs on a +// particular inteface. We just check the I/F of each RTE, and if it +// matches we call InvalidateRCEChain to invalidate the RCEs. +// +// Input: RTE - RTE to check. +// Context - Interface on which we're invalidating. +// +// Returns: TRUE. +// +uint +InvalidateRCEOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF) + InvalidateRCEChain(RTE); + + return TRUE; + +} + +//* SetMTUOnIF - Set the MTU on an interface. +// +// Called when we need to set the MTU on an interface. +// +// Input: RTE - RTE to check. +// Context - Pointer to a context. +// Context1 - Pointer to the new MTU. +// +// Returns: TRUE. +// +uint +SetMTUOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + uint NewMTU = *(uint *)Context1; + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF) + RTE->rte_mtu = NewMTU; + + return TRUE; +} + +//* SetMTUToAddr - Set the MTU to a specific address. +// +// Called when we need to set the MTU to a specific address. We set the MTU +// for all routes that use the specified address as a first hop to the new +// MTU. +// +// Input: RTE - RTE to check. +// Context - Pointer to a context. +// Context1 - Pointer to the new MTU. +// +// Returns: TRUE. +// +uint +SetMTUToAddr(RouteTableEntry *RTE, void *Context, void *Context1) +{ + uint NewMTU = *(uint *)Context1; + IPAddr Addr = *(IPAddr *)Context; + + if (IP_ADDR_EQUAL(RTE->rte_addr, Addr)) + RTE->rte_mtu = NewMTU; + + return TRUE; +} + +//* RTWalk - Routine to walk the route table. +// +// This routine walks the route table, calling the specified function +// for each entry. If the called function returns FALSE, the RTE is +// deleted. +// +// Input: CallFunc - Function to call for each entry. +// Context - Context value to pass to each call. +// +// Returns: Nothing. +// +void +RTWalk(uint (*CallFunc)(struct RouteTableEntry *, void *, void *), + void *Context, void *Context1) +{ + uint i; + CTELockHandle Handle; + RouteTableEntry *RTE, *PrevRTE; + + CTEGetLock(&RouteTableLock, &Handle); + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) { + + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next); + RTE = RouteTable[i]; + while (RTE != NULL) { + if (!(*CallFunc)(RTE, Context, Context1)) { + DeleteRTE(PrevRTE, RTE); + } else { + PrevRTE = RTE; + } + RTE = PrevRTE->rte_next; + } + } + + CTEFreeLock(&RouteTableLock, Handle); +} + +//** AttachRCEToRTE - Attach an RCE to an RTE. +// +// This procedure takes an RCE, finds the appropriate RTE, and attaches it. +// We check to make sure that the source address is still valid. +// +// Entry: RCE - RCE to be attached. +// Protocol - Protocol type for packet causing this call. +// Buffer - Pointer to buffer for packet causing this +// call. +// Length - Length of buffer. +// +// Returns: TRUE if we attach it, false if we don't. +// +uint +AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, uchar *Buffer, uint Length) +{ + CTELockHandle TableHandle, RCEHandle; + RouteTableEntry *RTE; + NetTableEntry *NTE; + uint Status; + + + CTEGetLock(&RouteTableLock, &TableHandle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if ((NTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(RCE->rce_src, NTE->nte_addr)) + break; + + if (NTE == NULL) { + // Didn't find a match. + CTEFreeLock(&RouteTableLock, TableHandle); + return FALSE; + } + + RTE = LookupRTE(RCE->rce_dest, RCE->rce_src, HOST_ROUTE_PRI); + + // See if we found an RTE. + if (RTE != NULL) { + + Status = TRUE; + + // Yep, we found one. Get the lock on the RCE, and make sure he's + // not pointing at an RTE already. We also need to make sure that the usecnt + // is 0, so that we can invalidate the RCE at the low level. If we set valid + // to TRUE without doing this we may get into a wierd situation where we + // link the RCE onto an RTE but the lower layer information is wrong, so we + // send to IP address X at mac address Y. So to be safe we don't set valid + // to TRUE until both usecnt is 0 and valid is FALSE. We'll keep coming + // through this routine on every send until that happens. + + CTEGetLock(&RCE->rce_lock, &RCEHandle); + if (RCE->rce_usecnt == 0) { + // Nobody is using him, so we can link him up. + if (!(RCE->rce_flags & RCE_VALID)) { + Interface *IF; + // He's not valid. Invalidate the lower layer info, just in + // case. Make sure he's connected before we try to do this. If + // he's not marked as connected, don't bother to try and invalidate + // him as there is no interface. + if (RCE->rce_flags & RCE_CONNECTED) { + IF = (Interface *)RCE->rce_rte; + (*(IF->if_invalidate))(IF->if_lcontext, RCE); +#ifdef _PNP_POWER + if (RCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + RCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } else { + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); + } + + // Link the RCE on the RTE, and set up the back pointer. + RCE->rce_rte = RTE; + RCE->rce_flags |= RCE_VALID; + RCE->rce_next = RTE->rte_rcelist; + RTE->rte_rcelist = RCE; + + // Make sure the RTE is connected. If not, try to connect him. + if (!(RTE->rte_flags & RTE_IF_VALID)) { + // Not connected. Try to connect him. + RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src, + Protocol, Buffer, Length); + if (RTE != NULL) { + // Got one, so mark as connected. + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); +#ifdef _PNP_POWER + RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED); + RTE->rte_if->if_refcount++; +#else + RCE->rce_flags |= RCE_CONNECTED; + +#endif + } else { + + // Couldn't get a valid i/f. Mark the RCE as not connected, + // and set up to fail this call. + CTEAssert(FALSE); + RCE->rce_flags &= ~RCE_CONNECTED; + Status = FALSE; + } + } else { + // The RTE is connected, mark the RCE as connected. + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); +#ifdef _PNP_POWER + RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED); + RTE->rte_if->if_refcount++; +#else + RCE->rce_flags |= RCE_CONNECTED; +#endif + } + } else { + // The RCE is valid. See if it's connected. + if (!(RCE->rce_flags & RCE_CONNECTED)) { + + // Not connected, try to get a valid i/f. + if (!(RTE->rte_flags & RTE_IF_VALID)) { + RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src, + Protocol, Buffer, Length); + if (RTE != NULL) { + RCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); + RCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; +#endif + } else { + + // Couldn't connect, so fail. + CTEAssert(FALSE); + Status = FALSE; + } + } else { // Already connected, just mark as valid. + RCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + if (!(RCE->rce_flags & RCE_REFERENCED)) { + RCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; + } +#endif + } + } + } + } + + // Free the locks and we're done. + CTEFreeLock(&RCE->rce_lock, RCEHandle); + CTEFreeLock(&RouteTableLock, TableHandle); + return Status; + } else { + // No route! Fail the call. + CTEFreeLock(&RouteTableLock, TableHandle); + return FALSE; + } + +} +//** IPGetPInfo - Get information.. +// +// Called by an upper layer to get information about a path. We return the +// MTU of the path and the maximum link speed to be expected on the path. +// +// Input: Dest - Destination address. +// Src - Src address. +// NewMTU - Where to store path MTU (may be NULL). +// MaxPathSpeed - Where to store maximum path speed (may be NULL). +// +// Returns: Status of attempt to get new MTU. +// +IP_STATUS +IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, uint *MaxPathSpeed) +{ + CTELockHandle Handle; + RouteTableEntry *RTE; + IP_STATUS Status; + + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + if (RTE != NULL) { + if (NewMTU != NULL) + *NewMTU = RTE->rte_mtu; + if (MaxPathSpeed != NULL) + *MaxPathSpeed = RTE->rte_if->if_speed; + Status = IP_SUCCESS; + } else + Status = IP_DEST_HOST_UNREACHABLE; + + CTEFreeLock(&RouteTableLock, Handle); + return Status; + +} + +//** IPCheckRoute - Check that a route is valid. +// +// Called by an upper layer when it believes a route might be invalid. +// We'll check if we can. If the upper layer is getting there through a +// route derived via ICMP (presumably a redirect) we'll check to see +// if it's been learned within the last minute. If it has, it's assumed +// to still be valid. Otherwise, we'll mark it as down and try to find +// another route there. If we can, we'll delete the old route. Otherwise +// we'll leave it. If the route is through a default gateway we'll switch +// to another one if we can. Otherwise, we'll just leave - we don't mess +// with manually configured routes. +// +// Input: Dest - Destination to be reached. +// Src - Src we're sending from. +// +// Returns: Nothing. +// +void +IPCheckRoute(IPAddr Dest, IPAddr Src) +{ + RouteTableEntry *RTE; + RouteTableEntry *NewRTE; + RouteTableEntry *TempRTE; + CTELockHandle Handle; + uint Now = CTESystemUpTime() / 1000L; + + if (DeadGWDetect) { + // We are doing dead G/W detection. Get the lock, and try and + // find the route. + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + if (RTE != NULL && ((Now - RTE->rte_valid) > MIN_RT_VALID)) { + + // Found a route, and it's older than the minimum valid time. If it + // goes through a G/W, and is a route we learned via ICMP or is a + // default route, do something with it. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) { + // It's not through a G/W. + + if (RTE->rte_proto == IRE_PROTO_ICMP) { + + // Came from ICMP. Mark as invalid, and then make sure + // we have another route there. + RTE->rte_flags &= ~RTE_VALID; + NewRTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + + if (NewRTE == NULL) { + // Can't get there any other way so leave this + // one alone. + RTE->rte_flags |= RTE_VALID; + } else { + // There is another route, so destroy this one. Use + // FindSpecificRTE to find the previous RTE. + TempRTE = FindSpecificRTE(RTE->rte_dest, RTE->rte_mask, + RTE->rte_addr, RTE->rte_if, &NewRTE); + CTEAssert(TempRTE == RTE); + DeleteRTE(NewRTE, TempRTE); + } + } else { + if (RTE->rte_mask == DEFAULT_MASK) { + + // This is a default gateway. If we have more than one + // configured move to the next one. + + if (DefGWConfigured > 1) { + // Have more than one. Try the next one. First + // invalidate any RCEs on this G/W. + + InvalidateRCEChain(RTE); + if (DefGWActive == 1) { + // No more active. Revalidate all of them, + // and try again. + ValidateDefaultGWs(NULL_IP_ADDR); + CTEAssert(DefGWActive == DefGWConfigured); + } else { + // More than one active, so invalidate this + // one, and move to the next one. Stamp the + // next one with a valid time of Now, so we + // don't move from him too easily. + --DefGWActive; + RTE->rte_flags &= ~RTE_VALID; + RTE = FindRTE(Dest, Src, IPHash(0), + DEFAULT_ROUTE_PRI, DEFAULT_ROUTE_PRI); + if (RTE == NULL) { + // No more default gateways! This is bad. + CTEAssert(FALSE); + ValidateDefaultGWs(NULL_IP_ADDR); + CTEAssert(DefGWActive == DefGWConfigured); + } else { + CTEAssert(RTE->rte_mask == DEFAULT_MASK); + RTE->rte_valid = Now; + } + } + } + } + } + } + } + CTEFreeLock(&RouteTableLock, Handle); + } +} + + +//** FindRCE - Find an RCE on an RTE. +// +// A routine to find an RCE that's chained on an RTE. We assume the lock +// is held on the RTE. +// +// Entry: RTE - RTE to search. +// Dest - Destination address of RTE to find. +// Src - Source address of RTE to find. +// +// Returns: Pointer to RTE found, or NULL. +// +RouteCacheEntry * +FindRCE(RouteTableEntry *RTE, IPAddr Dest, IPAddr Src) +{ + RouteCacheEntry *CurrentRCE; + + CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)); + for (CurrentRCE = RTE->rte_rcelist; CurrentRCE != NULL; + CurrentRCE = CurrentRCE->rce_next) { + if ( IP_ADDR_EQUAL(CurrentRCE->rce_dest, Dest) && + IP_ADDR_EQUAL(CurrentRCE->rce_src, Src)) { + break; + } + } + return CurrentRCE; + +} + +//** OpenRCE - Open an RCE for a specific route. +// +// Called by the upper layer to open an RCE. We look up the type of the address +// - if it's invalid, we return 'Destination invalid'. If not, we look up the +// route, fill in the RCE, and link it on the correct RTE. +// +// As an added bonus, this routine will return the local address to use +// to reach the destination. +// +// Entry: Address - Address for which we are to open an RCE. +// Src - Source address we'll be using. +// RCE - Pointer to where to return pointer to RCE. +// Type - Pointer to where to return destination type. +// MSS - Pointer to where to return MSS for route. +// OptInfo - Pointer to option information, such as TOS and +// any source routing info. +// +// Returns: Source IP address to use. This will be NULL_IP_ADDR if the +// specified destination is unreachable for any reason. +// +IPAddr +OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, uchar *Type, + ushort *MSS, IPOptInfo *OptInfo) +{ + RouteTableEntry *RTE; // Pointer to RTE to put RCE on. + CTELockHandle TableLock; + uchar LocalType; + + + if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) + Address = OptInfo->ioi_addr; + + CTEGetLock(&RouteTableLock, &TableLock); + + // Make sure we're not in DHCP update. + if (DHCPActivityCount != 0) { + // We are updating DHCP. Just fail this now, since we're in an + // indeterminate state. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + LocalType = GetAddrType(Address); + + *Type = LocalType; + + // If the specified address isn't invalid, continue. + if (LocalType != DEST_INVALID) { + RouteCacheEntry *NewRCE; + + // If he's specified a source address, loop through the NTE table + // now and make sure it's valid. + if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if ((NTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(Src, NTE->nte_addr)) + break; + + if (NTE == NULL) { + // Didn't find a match. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + } + + // Find the route for this guy. If we can't find one, return NULL. + RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI); + + if (RTE != (RouteTableEntry *)NULL) { + CTELockHandle RCEHandle; + RouteCacheEntry *OldRCE; + + // We found one. + *MSS = (ushort)RTE->rte_mtu; // Return the route MTU. + + if (IP_LOOPBACK_ADDR(Src) && (RTE->rte_if != &LoopInterface)) { + // The upper layer is sending from a loopback address, but the + // destination isn't reachable through the loopback interface. + // Fail the request. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + // We have the RTE. Fill in the RCE, and link it on the RTE. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) + *Type |= DEST_OFFNET_BIT; // Tell upper layer it's off + // net. + + // + // If no source address was specified, then use the best address + // for the interface. This will generally prevent dynamic NTE's from + // being chosen as the source for wildcard binds. + // + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + + if (LocalType == DEST_LOCAL) + Src = Address; + else { + NetTableEntry *SrcNTE; + + SrcNTE = BestNTEForIF( + ADDR_FROM_RTE(RTE, Address), + RTE->rte_if + ); + + if (SrcNTE == NULL) { + // Can't find an address! Fail the request. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + Src = SrcNTE->nte_addr; + } + } + + // Now, see if an RCE already exists for this. + if ((OldRCE = FindRCE(RTE, Address, Src)) == NULL) { + + // Don't have an existing RCE. See if we can get a new one, + // and fill it in. + + NewRCE = CTEAllocMem(sizeof(RouteCacheEntry)); + *RCE = NewRCE; + + if (NewRCE != NULL) { + CTEMemSet(NewRCE, 0, sizeof(RouteCacheEntry)); + + NewRCE->rce_src = Src; + NewRCE->rce_dtype = LocalType; + NewRCE->rce_cnt = 1; + CTEInitLock(&NewRCE->rce_lock); + NewRCE->rce_dest = Address; + NewRCE->rce_rte = RTE; + NewRCE->rce_flags = RCE_VALID; + if (RTE->rte_flags & RTE_IF_VALID) { + NewRCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + //* Update the ref. count for this interface. + NewRCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; +#endif + } + NewRCE->rce_next = RTE->rte_rcelist; + RTE->rte_rcelist = NewRCE; + } + + CTEFreeLock(&RouteTableLock, TableLock); + return Src; + } else { + // We have an existing RCE. We'll return his source as the + // valid source, bump the reference count, free the locks + // and return. + CTEGetLock(&OldRCE->rce_lock, &RCEHandle); + OldRCE->rce_cnt++; + *RCE = OldRCE; + CTEFreeLock(&OldRCE->rce_lock, RCEHandle); + CTEFreeLock(&RouteTableLock, TableLock); + return Src; + } + } else { + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + } + + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; +} + +//* CloseRCE - Close an RCE. +// +// Called by the upper layer when it wants to close the RCE. We unlink it from +// the RTE. +// +// Entry: RCE - Pointer to the RCE to be closed. +// +// Exit: Nothing. +// +void +CloseRCE(RouteCacheEntry *RCE) +{ + RouteTableEntry *RTE; // Route on which RCE is linked. + RouteCacheEntry *PrevRCE; + CTELockHandle TableLock; // Lock handles used. + CTELockHandle RCEHandle; + Interface *IF; + + if (RCE != NULL) { + CTEGetLock(&RouteTableLock, &TableLock); + CTEGetLock(&RCE->rce_lock, &RCEHandle); + + if (--RCE->rce_cnt == 0) { + CTEAssert(RCE->rce_usecnt == 0); + if (RCE->rce_flags & RCE_VALID) { + // The RCE is valid, so we have a valid RTE in the pointer + // field. Walk down the RTE rcelist, looking for this guy. + + RTE = RCE->rce_rte; + IF = RTE->rte_if; + + PrevRCE = STRUCT_OF(RouteCacheEntry, &RTE->rte_rcelist, + rce_next); + + // Walk down the list until we find him. + while (PrevRCE != NULL) { + if (PrevRCE->rce_next == RCE) + break; + PrevRCE = PrevRCE->rce_next; + } + + CTEAssert(PrevRCE != NULL); + PrevRCE->rce_next = RCE->rce_next; + } else + IF = (Interface *)RCE->rce_rte; + + if (RCE->rce_flags & RCE_CONNECTED) { + (*(IF->if_invalidate))(IF->if_lcontext, RCE); + } + +#ifdef _PNP_POWER + if (RCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + } +#endif + CTEFreeLock(&RCE->rce_lock, RCEHandle); + CTEFreeMem(RCE); + } + else { + CTEFreeLock(&RCE->rce_lock, RCEHandle); + } + + CTEFreeLock(&RouteTableLock, TableLock); + + } + +} + + +//* LockedAddRoute - Add a route to the routing table. +// +// Called by AddRoute to add a route to the routing table. We assume the +// route table lock is already held. If the route to be added already exists +// we update it. Routes are identified by a (Destination, Mask, FirstHop, +// Interface) tuple. If an exact match exists we'll update the metric, which +// may cause us to promote RCEs from other RTEs, or we may be demoted in which +// case we'll invalidate our RCEs and let them be reassigned at transmission +// time. +// +// If we have to create a new RTE we'll do so, and find the best previous +// RTE, and promote RCEs from that one to the new one. +// +// The route table is an open hash structure. Within each hash chain the +// RTEs with the longest masks (the 'priority') come first, and within +// each priority the RTEs with the smallest metric come first. +// +// +// Entry: Destination - Destination address for which route is being +// added. +// Mask - Mask for destination. +// FirstHop - First hop for address. Could be IPADDR_LOCAL. +// OutIF - Pointer to outgoing I/F. +// MTU - Maximum MTU for this route. +// Metric - Metric for this route. +// Proto - Protocol type to store in route. +// AType - Administrative type of route. +// +// Returns: Status of attempt to add route. +// +IP_STATUS +LockedAddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, + Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType, + ROUTE_CONTEXT Context) +{ + uint RouteType; // SNMP route type. + RouteTableEntry *NewRTE, *OldRTE; // Entries for new and previous + // RTEs. + RouteTableEntry *PrevRTE; // Pointer to previous RTE. + CTELockHandle RCEHandle; // Lock handle for RCEs. + uint OldMetric; // Previous metric in use. + uint OldPriority; // Priority of previous route to + // destination. + RouteCacheEntry *CurrentRCE; // Current RCE being examined. + RouteCacheEntry *PrevRCE; // Previous RCE examined. + Interface *IF; // Interface being added on. + uint Priority; // Priority of the route. + uint TempMask; // Temporary copy of the mask. + uint Now = CTESystemUpTime() / 1000L; // System up time, + // in seconds. + uint MoveAny; // TRUE if we'll move any RCE. + ushort OldFlags; + + // First do some consistency checks. Make sure that the Mask and + // Destination agree. + if (!IP_ADDR_EQUAL(Destination & Mask, Destination)) + return IP_BAD_DESTINATION; + + if (AType != ATYPE_PERM && AType != ATYPE_OVERRIDE && AType != ATYPE_TEMP) + return IP_BAD_REQ; + +#ifdef _PNP_POWER + // If the interface is marked as going away, fail this. + if (OutIF->if_flags & IF_FLAGS_DELETING) { + return IP_BAD_REQ; + } +#endif + + RouteType = IP_ADDR_EQUAL(FirstHop, IPADDR_LOCAL) ? IRE_TYPE_DIRECT : + IRE_TYPE_INDIRECT; + + + MTU = MAX(MTU, MIN_VALID_MTU); + + // If the outgoing interface has NTEs attached but none are valid, fail + // this request unless it's a request to add the broadcast route. + if (OutIF != (Interface *)&DummyInterface) { + if (OutIF->if_ntecount == 0 && OutIF->if_nte != NULL && + !IP_ADDR_EQUAL(Destination, OutIF->if_bcast) ) { + // This interface has NTEs attached, but none are valid. Fail the + // request. + return IP_BAD_REQ; + } + } + + + // First look and see if the RTE already exists. + NewRTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE); + + if (NewRTE != NULL) { + + // The RTE already exists, and we have a lock on it. See if we're + // updating the metric. If we're not, just return. Otherwise + // we'll remove the RTE and update the metric. If the metric is + // increasing, walk down the RCE chain on the RTE and invalidate + // the RCE back pointers so that they'll be revalidated upon + // transmits, and then reinsert the RTE. If the metric is + // decreasing we'll just fall through to the case below where we'll + // promote RCEs and insert the RTE. + + + // Update the things that won't cause routing table motion. + NewRTE->rte_mtu = MTU; + NewRTE->rte_proto = Proto; + NewRTE->rte_valid = Now; + NewRTE->rte_mtuchange = Now; + NewRTE->rte_context = Context; + OldFlags = NewRTE->rte_flags; + + // We always turn off the increase flag when a route is updated, so + // that we take the long timeout to try to increase it. We also + // always turn on the valid flag. + NewRTE->rte_flags = (OldFlags & ~RTE_INCREASE) | RTE_VALID; + if (OutIF != (Interface *)&DummyInterface) { + NewRTE->rte_flags |= RTE_IF_VALID; + } else { + + // + // Invalidating a previously valid route + // + + NewRTE->rte_flags &= ~RTE_IF_VALID; + } + + // If the RTE is for a default gateway and the old flags indicate + // he wasn't valid then we're essentially creating a new active + // default gateway. In the case bump the active default gateway count. + if (NewRTE->rte_mask == DEFAULT_MASK && !(OldFlags & RTE_VALID)) + DefGWActive++; + + // Need to update the metric, which will cause this RTE to move + // in the table. + OldMetric = NewRTE->rte_metric; // Save the old one. + RemoveRTE(PrevRTE, NewRTE); // Pull him from the chain. + NewRTE->rte_metric = Metric; + + // Now see if we're increasing or decreasing the metric, or + // re-validating a previously invalid route. By definition, if the + // route wasn't valid before this there can't have been any RCEs + // attached to it, so we'll fall through to the promotion code + // and see if we can move any onto it. Otherwise, if we're + // demoting this route invalidate any RCEs on it. + + if (Metric >= OldMetric && (OldFlags & RTE_VALID)) { + // We're increasing it, or leaving it the same. His valid state + // may have changed, so we'll we'll invalidate any RCEs on him + // now in any case. + InsertRTE(NewRTE); + + InvalidateRCEChain(NewRTE); + + // + // Whether the IF has changed or not, we can always overwrite it + // We wait till here to overwrite because if old rte_if was + // not dummy if, then we will want to invalidate RCEChain + // Since the invalidate function is stored in the interface + // structure we want to keep it around + // + + NewRTE->rte_if = OutIF; + + return IP_SUCCESS; + } + + + NewRTE->rte_if = OutIF; + + // The metric is less than the old metric, so we're promoting this + // RTE. Fall through to the code below that deals with this, after + // saving the priority of the RTE. + Priority = NewRTE->rte_priority; + + } else { + + // Didn't find a matching RTE. Allocate a new one, and fill it in. + NewRTE = CTEAllocMem(sizeof(RouteTableEntry)); + + if (NewRTE == NULL) { + // Couldn't get the memory. + return IP_NO_RESOURCES; + } + + IPSInfo.ipsi_numroutes++; + + // Fill him in, and take the lock on him. To do this we'll need to + // calculate the priority. + Priority = 0; + TempMask = Mask; + + while (TempMask) { + Priority += TempMask & 1; + TempMask >>= 1; + } + + // Now initialize all of his fields. + NewRTE->rte_priority = Priority; + + NewRTE->rte_dest = Destination; + NewRTE->rte_mask = Mask; + if (Mask == DEFAULT_MASK) { + // We're adding a default route. + DefGWConfigured++; + DefGWActive++; + } + NewRTE->rte_addr = FirstHop; + NewRTE->rte_metric = Metric; + NewRTE->rte_mtu = MTU; + NewRTE->rte_if = OutIF; + NewRTE->rte_rcelist = NULL; + NewRTE->rte_type = (ushort)RouteType; + NewRTE->rte_flags = RTE_VALID; + if (OutIF != (Interface *)&DummyInterface) { + NewRTE->rte_flags |= RTE_IF_VALID; + } + NewRTE->rte_proto = Proto; + NewRTE->rte_valid = Now; + NewRTE->rte_mtuchange = Now; + NewRTE->rte_admintype = AType; + NewRTE->rte_context = Context; + } + + // At this point, NewRTE points to an initialized RTE that is not in the + // table. We hold the lock on NewRTE and on the RouteTable. First we'll + // find where we'll eventually insert the new RTE (we can't insert it + // yet, because we'll still need to search the table again and we can't + // do that while we hold a lock on an element in the table). Then we'll + // search the table for the next best route (i.e. a route with a priority + // less than or equal to ours), and if we find one we'll promote RCEs from + // that route to us. We'll actually have to search a chain of RTEs. + + PrevRTE = FindInsertPoint(NewRTE); + OldRTE = LookupRTE(Destination, NULL_IP_ADDR, Priority); + + // If we found one, try to promote from him. + if (OldRTE != NULL) { + + // Found another RTE, and we have the lock on it. Starting with him, + // walk down the chain. At the start of this loop we know that the + // route described by OldRTE can reach our destination. If his metric + // is better than ours, we're done and we'll just insert our route. + // If his priority and metric are equal to ours we'll promote only + // those RCEs that exactly match our source address. Otherwise either + // his priority or metric is worse than ours, and we'll promote all + // appropriate RCEs. + // + // Since we specified the maximum priority in the call to LookupRTE + // as our priority, we know the priority of the route we found + // can't be greater than our priority. + + OldMetric = OldRTE->rte_metric; + OldPriority = OldRTE->rte_priority; + + // We'll do the search if his priority is less than ours, or his + // metric is > (i.e. worse than) our metric, or his metric equals + // our metric and the old route is on a different interface. We + // know OldPriority is <= our Priority, since we specified our + // priority in the call to LookupRTE above. + + CTEAssert(OldPriority <= Priority); + + if (OldPriority < Priority || OldMetric > Metric || + (OldMetric == Metric && OldRTE->rte_if != OutIF)) { + + // We're going to search. Figure out the mask to use in comparing + // source addresses. + if (OldPriority == Priority && OldMetric == Metric) + MoveAny = FALSE; // Only promote an exact match. + else + MoveAny = TRUE; // Promote any match. + + for (;;) { + IF = OldRTE->rte_if; + + PrevRCE = STRUCT_OF(RouteCacheEntry, &OldRTE->rte_rcelist, + rce_next); + CurrentRCE = PrevRCE->rce_next; + // Walk the list, promoting any that match. + while (CurrentRCE != NULL) { + CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle); + + // If the masked source address matches our masked address, + // and the destinations match, promote him. + if ((MoveAny || AddrOnIF(OutIF, CurrentRCE->rce_src)) && + IP_ADDR_EQUAL(CurrentRCE->rce_dest & Mask, Destination)) { + // He matches. Pull him from the list and mark him as invalid. + // This will force a new lookup of the route next time he's + // used, which as a side effect will cause the route to be + // connected in the dial-on-demand case. + CTEAssert(CurrentRCE->rce_flags & RCE_VALID); + PrevRCE->rce_next = CurrentRCE->rce_next; + CurrentRCE->rce_flags &= ~RCE_VALID; + CurrentRCE->rce_rte = (RouteTableEntry *)IF; + if (CurrentRCE->rce_usecnt == 0) { + // No one's currently using him, so invalidate him. + if (CurrentRCE->rce_flags & RCE_CONNECTED) { + (*(IF->if_invalidate))(IF->if_lcontext, CurrentRCE); +#ifdef _PNP_POWER + if (CurrentRCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + CurrentRCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } else { +#ifdef _PNP_POWER + CTEAssert(!(CurrentRCE->rce_flags & RCE_REFERENCED)); +#endif + } + } + + } else { + // Doesn't match. Try the next one. + PrevRCE = CurrentRCE; + } + CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle); + CurrentRCE = PrevRCE->rce_next; + } + + // We've walked the RCE list on that old RTE. Look at the + // next one. If it has the same priority and metric as the + // old one, and also matches our destination, check it also. We + // don't need to RTEs that don't match this criteria, since we know + // RCEs are always kept on the 'best' RTE. + OldRTE = OldRTE->rte_next; + if (OldRTE != NULL) { + // We have another one. Check it out. + if (OldRTE->rte_priority == Priority && + OldRTE->rte_metric == OldMetric && + IP_ADDR_EQUAL(Destination & OldRTE->rte_mask, + OldRTE->rte_dest)) + continue; // It matches, so try to promote + // RCEs. + } + break; // Exit out of the for (;;) loop. + } + } else { + // OldRTE is a better route than the one we're inserting, so don't + // do anything. + } + } + + // At this point we're promoted any routes we need to, we hold the lock + // on NewRTE which still isn't inserted, and PrevRTE describes where to + // insert NewRTE. Insert it, free the lock, and return success. + InsertAfterRTE(PrevRTE, NewRTE); + return IP_SUCCESS; + +} + +//* AddRoute - Add a route to the routing table. +// +// This is just a shell for the real add route routine. All we do is take +// the route table lock, and call the LockedAddRoute routine to deal with +// the request. This is done this way because there are certain routines that +// need to be able to atomically examine and add routes. +// +// Entry: Destination - Destination address for which route is being +// added. +// Mask - Mask for destination. +// FirstHop - First hop for address. Could be IPADDR_LOCAL. +// OutIF - Pointer to outgoing I/F. +// MTU - Maximum MTU for this route. +// Metric - Metric for this route. +// Proto - Protocol type to store in route. +// AType - Administrative type of route. +// Context - Context for this route. +// +// Returns: Status of attempt to add route. +// +IP_STATUS +AddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, + Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType, + ROUTE_CONTEXT Context) +{ + CTELockHandle TableHandle; + IP_STATUS Status; + + CTEGetLock(&RouteTableLock, &TableHandle); + Status = LockedAddRoute(Destination, Mask, FirstHop, OutIF, MTU, Metric, + Proto, AType, Context); + + CTEFreeLock(&RouteTableLock, TableHandle); + return Status; +} + +//* DeleteRoute - Delete a route from the routing table. +// +// Called by upper layer or management code to delete a route from the routing +// table. If we can't find the route we return an error. If we do find it, we +// remove it, and invalidate any RCEs associated with it. These RCEs will be +// reassigned the next time they're used. A route is uniquely identified by +// a (Destination, Mask, FirstHop, Interface) tuple. +// +// Entry: Destination - Destination address for which route is being +// deleted. +// Mask - Mask for destination. +// FirstHop - First hop on way to Destination. -1 means route is +// local. +// OutIF - Outgoing interface for route. +// +// Returns: Status of attempt to delete route. +// +IP_STATUS +DeleteRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, Interface *OutIF) +{ + RouteTableEntry *RTE; // RTE being deleted. + RouteTableEntry *PrevRTE; // Pointer to RTE in front of one + // being deleted. + CTELockHandle TableLock; // Lock handle for table. + + + // Look up the route by calling FindSpecificRTE. If we can't find it, + // fail the call. + CTEGetLock(&RouteTableLock, &TableLock); + RTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE); + + if (RTE == NULL) { + // Didn't find the route, so fail the call. + CTEFreeLock(&RouteTableLock, TableLock); + return IP_BAD_ROUTE; + } + +#ifndef NT +// +// Disable admin check for NT, because the RAS server needs to be able to +// delete a subnet route. This ability is restricted to Administrators only +// by NT security checks. +// +// + if (RTE->rte_admintype == ATYPE_PERM) { + // Can't delete a permanent route. + CTEFreeLock(&RouteTableLock, TableLock); + return IP_BAD_REQ; + } +#endif + + // When we reach here we hold the lock on the RTE to be deleted, and + // PrevRTE points to the RTE immediately ahead of ours in the table. + // Call DeleteRTE to delete him. + DeleteRTE(PrevRTE, RTE); + + CTEFreeLock(&RouteTableLock, TableLock); + return IP_SUCCESS; + + +} + +//** Redirect - Process a redirect request. +// +// This is the redirect handler . We treat all redirects as host redirects as per the +// host requirements RFC. We make a few sanity checks on the new first hop address, and then +// we look up the current route. If it's not through the source of the redirect, just return. +// If the current route to the destination is a host route, update the first hop and return. +// If the route is not a host route, remove any RCE for this route from the RTE, create a +// host route and place the RCE (if any) on the new RTE. +// +// Entry: NTE - Pointer to NetTableEntry for net on which Redirect +// arrived. +// RDSrc - IPAddress of source of redirect. +// Target - IPAddress being redirected. +// Src - Src IP address of DG that triggered RD. +// FirstHop - New first hop for Target. +// +// Returns: Nothing. +// +void +Redirect(NetTableEntry *NTE, IPAddr RDSrc, IPAddr Target, IPAddr Src, + IPAddr FirstHop) +{ + uint MTU; + RouteTableEntry *RTE; + CTELockHandle Handle; + + if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR) || IP_LOOPBACK(FirstHop)) + return; // Can't redirect to loopback + // address. + + CTEAssert(IP_ADDR_EQUAL(NTE->nte_addr, Src)); + + // First make sure that this came from the gateway we're currently using to + // get to Target, and then lookup up the route to the new first hop. The new + // firsthop must be directly reachable, and on the same subnetwork or + // physical interface on which we received the redirect. + + + CTEGetLock(&RouteTableLock, &Handle); + + // Make sure the source of the redirect is the current first hop gateway. + RTE = LookupRTE(Target, Src, HOST_ROUTE_PRI); + if (RTE == NULL || IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || + !IP_ADDR_EQUAL(RTE->rte_addr, RDSrc)) { + CTEFreeLock(&RouteTableLock, Handle); + return; // A bad redirect. + } + + CTEAssert(RTE->rte_flags & RTE_IF_VALID); + + // If the current first hop gateway is a default gateway, see if we have + // another default gateway at FirstHop that is down. If so, mark him as + // up and invalidate the RCEs on this guy. + if (RTE->rte_mask == DEFAULT_MASK && ValidateDefaultGWs(FirstHop) != 0) { + // Have a default gateway that's been newly activated. Invalidate RCEs + // on the route, and we're done. + InvalidateRCEChain(RTE); + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + // We really need to add a host route through FirstHop. Make sure he's + // a valid first hop. + RTE = LookupRTE(FirstHop, Src, HOST_ROUTE_PRI); + if (RTE == NULL) { + CTEFreeLock(&RouteTableLock, Handle); + return; // Can't get there from here. + } + + CTEAssert(RTE->rte_flags & RTE_IF_VALID); + + + // Check to make sure the new first hop is directly reachable, and is on the + // same subnet or physical interface we received the redirect on. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || // Not directly reachable + // or wrong subnet. + ((NTE->nte_addr & NTE->nte_mask) != (FirstHop & NTE->nte_mask))) { + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + MTU = RTE->rte_mtu; + + // Now add a host route. AddRoute will do the correct things with shifting + // RCEs around. We know that FirstHop is on the same subnet as NTE (from + // the check above), so it's valid to add the route to FirstHop as out + // going through NTE. + LockedAddRoute(Target, HOST_MASK, IP_ADDR_EQUAL(FirstHop, Target) ? IPADDR_LOCAL : + FirstHop, NTE->nte_if, MTU, 1, IRE_PROTO_ICMP, ATYPE_OVERRIDE, + RTE->rte_context); + + CTEFreeLock(&RouteTableLock, Handle); +} + +//* GetRaisedMTU - Get the next largest MTU in table.. +// +// A utility function to search the MTU table for a larger value. +// +// Input: PrevMTU - MTU we're currently using. We want the +// next largest one. +// +// Returns: New MTU size. +// +uint +GetRaisedMTU(uint PrevMTU) +{ + uint i; + + for (i = (sizeof(MTUTable)/sizeof(uint)) - 1; i != 0; i--) { + if (MTUTable[i] > PrevMTU) + break; + } + + return MTUTable[i]; +} + +//* GuessNewMTU - Guess a new MTU, giving a DG size too big. +// +// A utility function to search the MTU table. As input we take in an MTU +// size we believe to be too large, and search the table looking for the +// next smallest one. +// +// Input: TooBig - Size that's too big. +// +// Returns: New MTU size. +// +uint +GuessNewMTU(uint TooBig) +{ + uint i; + + for (i = 0; i < ((sizeof(MTUTable)/sizeof(uint)) - 1); i++) + if (MTUTable[i] < TooBig) + break; + + return MTUTable[i]; +} + +//* RouteFragNeeded - Handle being told we need to fragment. +// +// Called when we receive some external indication that we need to fragment +// along a particular path. If we're doing MTU discovery we'll try to +// update the route, if we can. We'll also notify the upper layers about +// the new MTU. +// +// Input: IPH - Pointer to IP Header of datagram needing +// fragmentation. +// NewMTU - New MTU to be used (may be 0). +// +// Returns: Nothing. +// +void +RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU) +{ + uint OldMTU; + CTELockHandle Handle; + RouteTableEntry *RTE; + ushort HeaderLength; + + // If we're not doing PMTU discovery, don't do anything. + if (PMTUDiscovery) { + + // We're doing PMTU discovery. Correct the given new MTU for the IP + // header size, which we don't save as we track MTUs. + if (NewMTU != 0) { + // Make sure the new MTU we got is at least the minimum valid size. + NewMTU = MAX(NewMTU, MIN_VALID_MTU); + NewMTU -= sizeof(IPHeader); + } + + HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + + // Get the current routing information. + + CTEGetLock(&RouteTableLock, &Handle); + + // Find an RTE for the destination. + RTE = LookupRTE(IPH->iph_dest, IPH->iph_src, HOST_ROUTE_PRI); + + // If we couldn't find one, or the existing MTU is less than the new + // MTU, give up now. + + if (RTE == NULL || (OldMTU = RTE->rte_mtu) < NewMTU) { + // No RTE, or an invalid new MTU. Just bail out now. + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + // If the new MTU is zero, figure out what the new MTU should be. + if (NewMTU == 0) { + ushort DGLength; + + // The new MTU is zero. We'll make a best guess what the new + // MTU should be. We have the RTE for this route already. + + + // Get the length of the datagram that triggered this. Since we'll + // be comparing it against MTU values that we track without the + // IP header size included, subtract off that amount. + DGLength = (ushort)net_short(IPH->iph_length) - sizeof(IPHeader); + + // We may need to correct this as per RFC 1191 for dealing with + // old style routers. + if (DGLength >= OldMTU) { + // The length of the datagram sent is not less than our + // current MTU estimate, so we need to back it down (assuming + // that the sending route has incorrectly added in the header + // length). + DGLength -= HeaderLength; + + } + + // If it's still larger than our current MTU, use the current + // MTU. This could happen if the upper layer sends a burst of + // packets which generate a sequence of ICMP discard messages. The + // first one we receive will cause us to lower our MTU. We then + // want to discard subsequent messages to avoid lowering it + // too much. This could conceivably be a problem if our + // first adjustment still results in an MTU that's too big, + // but we should converge adequately fast anyway, and it's + // better than accidentally underestimating the MTU. + + if (DGLength > OldMTU) + NewMTU = OldMTU; + else + // Move down the table to the next lowest MTU. + NewMTU = GuessNewMTU(DGLength); + } + + // We have the new MTU. Now add it to the table as a host route. + if (NewMTU != OldMTU) + LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr, RTE->rte_if, + NewMTU, RTE->rte_metric, IRE_PROTO_ICMP, ATYPE_OVERRIDE, + RTE->rte_context); + + CTEFreeLock(&RouteTableLock, Handle); + + // We've added the route. Now notify the upper layers of the change. + ULMTUNotify(IPH->iph_dest, IPH->iph_src, IPH->iph_protocol, + (void *)((uchar *)IPH + HeaderLength), NewMTU); + + } +} + +//** IPRouteTimeout - IP routeing timeout handler. +// +// The IP routeing timeout routine, called once a minute. We look at all +// host routes, and if we raise the MTU on them we do so. +// +// Entry: Timer - Timer being fired. +// Context - Pointer to NTE being time out. +// +// Returns: Nothing. +// +void +IPRouteTimeout(CTEEvent *Timer, void *Context) +{ + uint Now = CTESystemUpTime() / 1000L; + CTELockHandle Handle; + uint i; + RouteTableEntry *RTE, *PrevRTE; + uint RaiseMTU, Delta; + Interface *IF; + IPAddr Dest; + uint NewMTU; + NetTableEntry *NTE; + + CTEGetLock(&RouteTableLock, &Handle); + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) { + // Walk down each chain, looking at the host routes. If we're + // doing PMTU discovery, see if we can raise the MTU. + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next); + RTE = RouteTable[i]; + while (RTE != NULL && RTE->rte_mask == HOST_MASK) { + // Make sure he's valid. + if (RTE->rte_flags & RTE_VALID) { + if (PMTUDiscovery) { + // Check to see if we can raise the MTU on this guy. + Delta = Now - RTE->rte_mtuchange; + + if (RTE->rte_flags & RTE_INCREASE) + RaiseMTU = (Delta >= MTU_INCREASE_TIME ? 1 : 0); + else + RaiseMTU = (Delta >= MTU_DECREASE_TIME ? 1 : 0); + + if (RaiseMTU) { + // We need to raise this MTU. Set his change time to + // Now, so we don't do this again, and figure out + // what the new MTU should be. + RTE->rte_mtuchange = Now; + IF = RTE->rte_if; + if (RTE->rte_mtu < IF->if_mtu) { + + RTE->rte_flags |= RTE_INCREASE; + // This is a candidate for change. Figure out + // what it should be. + NewMTU = MIN(GetRaisedMTU(RTE->rte_mtu), + IF->if_mtu); + RTE->rte_mtu = NewMTU; + Dest = RTE->rte_dest; + + // We have the new MTU. Free the lock, and walk + // down the NTEs on the I/F. For each NTE, + // call up to the upper layer and tell him what + // his new MTU is. + CTEFreeLock(&RouteTableLock, Handle); + NTE = IF->if_nte; + while (NTE != NULL) { + if (NTE->nte_flags & NTE_VALID) { + ULMTUNotify(Dest, NTE->nte_addr, 0, NULL, + MIN(NewMTU, NTE->nte_mss)); + } + NTE = NTE->nte_ifnext; + } + + // We've notified everyone. Get the lock again, + // and start from the first element of this + // chain in case something's changed after we + // free the lock. We've updated the mtuchange + // time of this RTE, so we won't hit him again. + CTEGetLock(&RouteTableLock, &Handle); + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], + rte_next); + RTE = RouteTable[i]; + continue; + } else + RTE->rte_flags &= ~RTE_INCREASE; + } + } + // If this route came in via ICMP, and we have no RCEs on it, + // and it's at least 10 minutes old, delete it. + if (RTE->rte_proto == IRE_PROTO_ICMP && + RTE->rte_rcelist == NULL && + (Now - RTE->rte_valid) > MAX_ICMP_ROUTE_VALID) { + // He needs to be deleted. Call DeleteRTE to do this. + DeleteRTE(PrevRTE, RTE); + RTE = PrevRTE->rte_next; + continue; + } + } + PrevRTE = RTE; + RTE = RTE->rte_next; + } + } + + CTEFreeLock(&RouteTableLock, Handle); + CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL); + +} + +//* FreeFWPacket - Free a forwarding packet when we're done with it. +// +// +// Input: Packet - Packet to be freed. +// +// Returns: Nothing. +// +void +FreeFWPacket(PNDIS_PACKET Packet) +{ + CTELockHandle Handle; + FWContext *FWC; + +// BUGBUG - Portability issue + +#ifdef VXD + Packet->Private.Head = (PNDIS_BUFFER)NULL; + Packet->Private.Count = 0; + Packet->Private.PhysicalCount = 0; + Packet->Private.TotalLength = 0; +#else // VXD +#ifdef NT + // + // BUGBUG: This is inefficient. Need something better. + // + NdisReinitializePacket(Packet); +#else // NT +#error Need portable way to do this. +#endif // NT +#endif // VXD + + FWC = (FWContext *)Packet->ProtocolReserved; + if (FWC->fc_options) { + CTEFreeMem(FWC->fc_options); + FWC->fc_options = (uchar *)NULL; + } + + if (FWC->fc_buffhead) { + CTEGetLock(&FWBufFreeLock, &Handle); + FWC->fc_bufftail->Next = FWBufFree; // BUGBUG more portable. + FWBufFree = FWC->fc_buffhead; + CTEFreeLock(&FWBufFreeLock, Handle); + FWC->fc_buffhead = (PNDIS_BUFFER)NULL; + } +#ifdef _PNP_POWER + // If there's an interface pointer here, dereference in now. + if (FWC->fc_if != NULL) { + DerefIF(FWC->fc_if); + FWC->fc_if = NULL; + } +#endif + + CTEGetLock(&FWPacketFreeLock, &Handle); + FWC->fc_pc.pc_common.pc_link = FWPacketFree; + FWPacketFree = Packet; + CTEFreeLock(&FWPacketFreeLock, Handle); + +} + +//* GrowFWPackets - Grow the FW packet list, if we can. +// +// Called when we need to allocate a FW packet, but don't have one. We'll try to grow the +// FWPacket list now. +// +// Input: Nothing. +// +// Returns: TRUE if we succeeded in growing the list, FALSE otherwise. +// +uint +GrowFWPackets(void) +{ + CTELockHandle Handle; + IPHeader *HeaderPtr; + NDIS_HANDLE BufferPool; + NDIS_HANDLE PacketPool; + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + NDIS_STATUS Status; + uint i; + uint AmountToGrow; + + CTEGetLock(&FWPacketFreeLock, &Handle); + + AmountToGrow = MIN(MaxFWPackets - CurrentFWPackets, FWPACKET_GROW_AMOUNT); + HeaderPtr = NULL; + + if (AmountToGrow != 0) { + + // We have room to grow yet, so try to. First get the memory for our header buffers. + HeaderPtr = CTEAllocMem(AmountToGrow * sizeof(IPHeader)); + if (HeaderPtr == (IPHeader *)NULL) + goto failure; // Couldn't get it. + + // Now try to get NDIS buffers for the headers. + NdisAllocateBufferPool(&Status, &BufferPool, AmountToGrow); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; + } + + // Now try to get the packets themselves. + NdisAllocatePacketPool(&Status, &PacketPool, AmountToGrow, sizeof(FWContext)); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Since we have everything we need, update the current count. + CurrentFWPackets += AmountToGrow; + + CTEFreeLock(&FWPacketFreeLock, Handle); + + // We got the resources we need. Loop through and put them on. + for (i = 0; i < AmountToGrow; i++) { + FWContext *FWC; + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr, + sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + NdisAllocatePacket(&Status, &Packet, PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext)); + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_hndisbuff = Buffer; + FWC->fc_hbuff = HeaderPtr; + FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW; + FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP; + FWC->fc_pc.pc_pi = RtPI; + FWC->fc_pc.pc_context = Packet; + + FreeFWPacket(Packet); + HeaderPtr++; + } + return TRUE; + } + +failure: + CTEFreeLock(&FWPacketFreeLock, Handle); + if (HeaderPtr != NULL) { + CTEFreeMem(HeaderPtr); + } + return FALSE; +} + +//* GrowFWBuffer - Grow the FW buffer pool, if we can. +// +// Called when we need to grow the FW buffer pool. We'll grow it up to the maximum size +// specified by the user. +// +// Input: Nothing. +// +// Returns: TRUE if we succeeded in growing the pool, FALSE otherwise. +// +uint +GrowFWBuffer(void) +{ + CTELockHandle Handle; + uint AvailableBufferSpace; + uint NewBufferCount; + uint i; + uchar *BufferPtr = NULL; + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + NDIS_HANDLE BufferPool; + + CTEGetLock(&FWPacketFreeLock, &Handle); + AvailableBufferSpace = MIN(MaxFWBufferSize - CurrentFWBufferSize, FW_BUF_GROW_AMOUNT); + + // If we have room to grow, do so. + if (AvailableBufferSpace >= FW_BUF_SIZE) { + // We have room to grow the buffer, so do so. First, round to a multiple of our + // FW buffer size. + NewBufferCount = AvailableBufferSpace / FW_BUF_SIZE; + AvailableBufferSpace = NewBufferCount * FW_BUF_SIZE; + + // Allocate the resources we need. + BufferPtr = CTEAllocMem(AvailableBufferSpace); + if (BufferPtr == NULL) { + goto failure; + } + + NdisAllocateBufferPool(&Status, &BufferPool, NewBufferCount); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; + } + + // We got what we needed. Now loop through and put them on the list. + for (i = 0; i < NewBufferCount; i++) { + NdisAllocateBuffer(&Status, &Buffer, BufferPool, BufferPtr, FW_BUF_SIZE); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + + Buffer->Next = FWBufFree; + FWBufFree = Buffer; + BufferPtr += FW_BUF_SIZE; + } + + CurrentFWBufferSize += AvailableBufferSpace; + CTEFreeLock(&FWPacketFreeLock, Handle); + return TRUE; + + } + +failure: + CTEFreeLock(&FWPacketFreeLock, Handle); + if (BufferPtr != NULL) { + CTEFreeMem(BufferPtr); + } + return FALSE; + +} + +//* FWSendComplete - Complete the transmission of a forwarded packet. +// +// This is called when the send of a forwarded packet is done. We'll free the resources +// and get the next send going, if there is one. If there isn't, we'll decrement the pending +// count. +// +// Input: Packet - Packet being completed. +// Buffer - Pointer to buffer chain being completed. +// +// Returns: Nothing. +// +void +FWSendComplete(void *SendContext, PNDIS_BUFFER Buffer) +{ + PNDIS_PACKET Packet = (PNDIS_PACKET)SendContext; + FWContext *FWC = (FWContext *)Packet->ProtocolReserved; + RouteSendQ *RSQ; + CTELockHandle Handle; + FWQ *NewFWQ; + PNDIS_PACKET NewPacket; + + +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + + if (!IS_BCAST_DEST(FWC->fc_dtype)) + RSQ = &((RouteInterface *)FWC->fc_if)->ri_q; + else + RSQ = BCastRSQ; + + FreeFWPacket(Packet); + + CTEGetLock(&RSQ->rsq_lock, &Handle); + CTEAssert(RSQ->rsq_pending <= RSQ->rsq_maxpending); + + RSQ->rsq_pending--; + + CTEAssert(*(int *)&RSQ->rsq_pending >= 0); + + if (RSQ->rsq_qlength != 0) { // Have more to send. + // Make sure we're not already running through this. If we are, quit. + if (!RSQ->rsq_running) { + + // We could schedule this off for an event, but under NT that + // could me a context switch for every completing packet in the + // normal case. For now, just do it in a loop guarded with + // rsq_running. + RSQ->rsq_running = TRUE; + + // Loop while we haven't hit our send limit and we still have + // stuff to send. + while (RSQ->rsq_pending < RSQ->rsq_maxpending && + RSQ->rsq_qlength != 0) { +#ifdef DEBUG + if (RSQ->rsq_qh.fq_next == &RSQ->rsq_qh) + DEBUGCHK; // Empty Q! +#endif + // Pull one off the queue, and update qlength. + NewFWQ = RSQ->rsq_qh.fq_next; + RSQ->rsq_qh.fq_next = NewFWQ->fq_next; + NewFWQ->fq_next->fq_prev = NewFWQ->fq_prev; + RSQ->rsq_qlength--; + + // Update pending before we send. + RSQ->rsq_pending++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + NewPacket = PACKET_FROM_FWQ(NewFWQ); + TransmitFWPacket(NewPacket, + ((FWContext *)NewPacket->ProtocolReserved)->fc_datalength); + CTEGetLock(&RSQ->rsq_lock, &Handle); + } + + RSQ->rsq_running = FALSE; + } + } + + CTEFreeLock(&RSQ->rsq_lock, Handle); + +} + +//* TransmitFWPacket - Transmit a forwarded packet on a link. +// +// Called when we know we can send a packet. We fix up the header, and send it. +// +// Input: Packet - Packet to be sent. +// DataLength - Length of data. +// +// Returns: Nothing. +// +void +TransmitFWPacket(PNDIS_PACKET Packet, uint DataLength) +{ + FWContext *FC = (FWContext *)Packet->ProtocolReserved; + PNDIS_BUFFER HBuffer, Buffer; + IP_STATUS Status; + PVOID VirtualAddress; + UINT BufLen; + + // Fix up the packet. Remove the existing buffer chain, and put our header on + // the front. + + // BUGBUG - Get NDIS fixed to make this portable. +#ifdef VXD + Buffer = Packet->Private.Head; + HBuffer = FC->fc_hndisbuff; + Packet->Private.Head = HBuffer; + Packet->Private.Tail = HBuffer; + HBuffer->Next = (PNDIS_BUFFER)NULL; + Packet->Private.TotalLength = sizeof(IPHeader); + Packet->Private.Count = 1; + + Packet->Private.PhysicalCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(HBuffer->VirtualAddress, + sizeof(IPHeader)); +#else // VXD +#ifdef NT + Buffer = Packet->Private.Head; + HBuffer = FC->fc_hndisbuff; + Packet->Private.Head = HBuffer; + Packet->Private.Tail = HBuffer; + NDIS_BUFFER_LINKAGE(HBuffer) = (PNDIS_BUFFER)NULL; + Packet->Private.TotalLength = sizeof(IPHeader); + Packet->Private.Count = 1; + + NdisQueryBuffer(HBuffer, &VirtualAddress, &BufLen); + + Packet->Private.PhysicalCount = + ADDRESS_AND_SIZE_TO_SPAN_PAGES( + VirtualAddress, + sizeof(IPHeader) + ); +#else // NT +#error HELP! Need to make this code portable. +#endif // NT +#endif // VXD + + // Figure out how to send it. If it's not a broadcast we'll either send it or + // have it fragmented. If it is a broadcast we'll let our send broadcast routine + // handle it. + if (FC->fc_dtype != DEST_BCAST) { + + if ((DataLength + (uint)FC->fc_optlength) <= FC->fc_mtu) + Status = SendIPPacket(FC->fc_if, FC->fc_nexthop, Packet, Buffer, + FC->fc_hbuff, FC->fc_options, (uint)FC->fc_optlength); + else { // Need to fragment this. + BufferReference *BR = CTEAllocMem(sizeof(BufferReference)); + + if (BR == (BufferReference *)NULL) { // Couldn't get a BufferReference + FWSendComplete(Packet, Buffer); + return; + } + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + FC->fc_pc.pc_br = BR; + Status = IPFragment(FC->fc_if, FC->fc_mtu, FC->fc_nexthop, Packet, + FC->fc_hbuff, Buffer, DataLength, FC->fc_options, + (uint)FC->fc_optlength, (int *)NULL); + + // + // Fragmentation needed with the DF flag set should have been + // handled in IPForward. We don't have the original header + // any longer, so silently drop the packet. + // + CTEAssert(Status != IP_PACKET_TOO_BIG); + } + } else + Status = SendIPBCast(FC->fc_srcnte, FC->fc_nexthop, Packet, FC->fc_hbuff, + Buffer, DataLength, FC->fc_options, (uint)FC->fc_optlength, + FC->fc_sos, &FC->fc_index); + + if (Status != IP_PENDING) + FWSendComplete(Packet, Buffer); +} + +//* SendFWPacket - Send a packet that needs to be forwarded. +// +// This routine is invoked when we actually get around to sending a packet. +// We look and see if we can give another queued send to the outgoing link, +// and if so we send on that link. Otherwise we put it on the outgoing queue +// and remove it later. +// +// Input: SrcNTE - Source NTE of packet. +// Packet - Packet to be send, containg all needed context info. +// Status - Status of transfer data. +// DataLength - Length in bytes of data to be send. +// +// Returns: Nothing. +// +void +SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataLength) +{ + + FWContext *FC = (FWContext *)Packet->ProtocolReserved; + Interface *IF = FC->fc_if; + RouteSendQ *RSQ; + CTELockHandle Handle; + + if (Status == NDIS_STATUS_SUCCESS) { + // Figure out which logical queue it belongs on, and if we don't already + // have too many things going there, send it. If we can't send it now we'll + // queue it for later. + if (IS_BCAST_DEST(FC->fc_dtype)) + RSQ = BCastRSQ; + else + RSQ = &((RouteInterface *)IF)->ri_q; + + CTEGetLock(&RSQ->rsq_lock, &Handle); + + if (RSQ->rsq_pending < RSQ->rsq_maxpending && RSQ->rsq_qlength == 0) { + // We can send on this interface. + RSQ->rsq_pending++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + + TransmitFWPacket(Packet, DataLength); + + } else { // Need to queue this packet for later. + + FC->fc_datalength = DataLength; + FC->fc_q.fq_next = &RSQ->rsq_qh; + FC->fc_q.fq_prev = RSQ->rsq_qh.fq_prev; + RSQ->rsq_qh.fq_prev->fq_next = &FC->fc_q; + RSQ->rsq_qh.fq_prev = &FC->fc_q; + RSQ->rsq_qlength++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + } + } else{ + IPSInfo.ipsi_outdiscards++; + FreeFWPacket(Packet); + } + +} + +//* RemoveRandomFWPacket - Remove a random packet from the FW queue. +// +// Called when we run out of resources. We pick a random packet from the FW queue, +// free it, and return. The caller will hopefully then get it for his own use. +// +// Input: RSQ - Pointer to outgoing route send q.. +// +// Returns: TRUE if we free a packet, false if we didn't. +// +uchar +RemoveRandomFWPacket(RouteSendQ *RSQ) +{ + uint Now = (uint)CTESystemUpTime(); + CTELockHandle Handle; + uint PacketCount; + PNDIS_PACKET FreedPacket; + FWQ *CurrentFWQ; +#ifdef DEBUG + FWQ *FirstFWQ; +#endif + +#ifdef DEBUG + FirstFWQ = &RSQ->rsq_qh; +#endif + + CTEGetLock(&RSQ->rsq_lock, &Handle); + if (RSQ->rsq_qlength) { // We have a least one in the list. + + + PacketCount = Now % (RSQ->rsq_qlength + 1); + if (PacketCount == RSQ->rsq_qlength) { + CTEFreeLock(&RSQ->rsq_lock, Handle); + return FALSE; + } + + CurrentFWQ = RSQ->rsq_qh.fq_next; + while (PacketCount--) { +#ifdef DEBUG + if (CurrentFWQ == FirstFWQ) + DEBUGCHK; +#endif + CurrentFWQ = CurrentFWQ->fq_next; + } + + // We've got the proper packet. Splice him out. + CurrentFWQ->fq_next->fq_prev = CurrentFWQ->fq_prev; + CurrentFWQ->fq_prev->fq_next = CurrentFWQ->fq_next; + RSQ->rsq_qlength--; + CTEFreeLock(&RSQ->rsq_lock, Handle); + FreedPacket = PACKET_FROM_FWQ(CurrentFWQ); + FreeFWPacket(FreedPacket); + IPSInfo.ipsi_outdiscards++; + return TRUE; + } + CTEFreeLock(&RSQ->rsq_lock, Handle); + return FALSE; + + +} + +//* GetFWBuffer - Get a list of buffers for forwarding. +// +// This routine gets a list of buffers for forwarding, and puts the data into it. This +// may involve calling TransferData, or we may be able to copy directly into them +// ourselves. +// +// Input: SrcNTE - Pointer to NTE on which packet was received. +// Packet - Packet being forwarded, used for TD. +// Data - Pointer to data buffer being forwarded. +// DataLength - Length in bytes of Data. +// BufferLength - Length in bytes available in buffer pointer to by Data. +// Offset - Offset into original data from which to transfer. +// LContext1, LContext2 - Context values for the link layer. +// +// Returns: NDIS_STATUS of attempt to get buffer. +// +NDIS_STATUS +GetFWBuffer(NetTableEntry *SrcNTE, PNDIS_PACKET Packet, uchar *Data, + uint DataLength, uint BufferLength, uint Offset, NDIS_HANDLE LContext1, + uint LContext2) +{ + CTELockHandle Handle; + uint BufNeeded, i; + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + void *DestPtr; + Interface *SrcIF; + FWContext *FWC; + uint BufLen; + uint LastBufSize; +#ifdef DEBUG + uint TotalBufferSize; + PNDIS_BUFFER TempBuffer; +#endif + + // Figure out how many buffers we need. + BufNeeded = DataLength / FW_BUF_SIZE; + LastBufSize = DataLength % FW_BUF_SIZE; + if (LastBufSize != 0) + BufNeeded++; + +#ifdef DEBUG + if (!BufNeeded) + DEBUGCHK; +#endif + FWC = (FWContext *)Packet->ProtocolReserved; + + // Now run down the buffer free list, getting the buffers we need. If we + // can't get enough the first time, we'll free a random packet from our + // pending list and try again. + for (;;) { + CTEGetLock(&FWBufFreeLock, &Handle); + FirstBuffer = FWBufFree; + CurrentBuffer = STRUCT_OF(NDIS_BUFFER, &FWBufFree, Next); + i = 0; + do { + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + if (!CurrentBuffer) + break; + + // Zap this buffer length to the full buffer size, since it may + // have been modified from a previous send. + NdisAdjustBufferLength(CurrentBuffer, FW_BUF_SIZE); + i++; + } while (i < BufNeeded); + + if (i != BufNeeded) { // We ran out of buffers. Free a packet and try again. + RouteSendQ *RSQ; + + if ((MaxFWBufferSize - CurrentFWBufferSize) <= FW_BUF_SIZE) { + CTEFreeLock(&FWBufFreeLock, Handle); + if (GrowFWBuffer()) { + continue; + } + } else + CTEFreeLock(&FWBufFreeLock, Handle); + + if (!IS_BCAST_DEST(FWC->fc_dtype)) + RSQ = &((RouteInterface *)FWC->fc_if)->ri_q; + else + RSQ = BCastRSQ; + + if (!RemoveRandomFWPacket(RSQ)) { + if (IS_BCAST_DEST(FWC->fc_dtype)) + return NDIS_STATUS_RESOURCES; + + // Couldn't get one for a non-broadcast packet. If the qlen is + // 0 on the outgoing queue, we'll try other queues, on the + // presumption that traffic through some other interface is + // starving this one. + if (RSQ->rsq_qlength == 0) { + Interface *IF; + for (IF = IFList; IF != NULL; IF = IF->if_next) { + RSQ = &((RouteInterface *)IF)->ri_q; + if (RemoveRandomFWPacket(RSQ)) + break; + } + if (IF == NULL) + return NDIS_STATUS_RESOURCES; + } else + return NDIS_STATUS_RESOURCES; + } + + // Otherwise we'll fall through and try again, now that we have + // hopefully put some more on the free queue. + + } else { + // We have as many as we need. Update the free list. + FWBufFree = NDIS_BUFFER_LINKAGE(CurrentBuffer); + CTEFreeLock(&FWBufFreeLock, Handle); + NDIS_BUFFER_LINKAGE(CurrentBuffer) = (PNDIS_BUFFER)NULL; + + // If we have a non-full last buffer, adjust it's size. + if (LastBufSize != 0) + NdisAdjustBufferLength(CurrentBuffer, LastBufSize); + + FWC->fc_buffhead = FirstBuffer; + FWC->fc_bufftail = CurrentBuffer; + break; + } + } + + NdisChainBufferAtFront(Packet, FirstBuffer); + +#ifdef DEBUG + // Sanity check the buffer chain and packet. + TempBuffer = FirstBuffer; + TotalBufferSize = 0; + while (TempBuffer != NULL) { + TotalBufferSize += NdisBufferLength(TempBuffer); + TempBuffer = NDIS_BUFFER_LINKAGE(TempBuffer); + } + + CTEAssert(TotalBufferSize == DataLength); + NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalBufferSize); + CTEAssert(TotalBufferSize == DataLength); +#endif + + // First buffer points to the list of buffers we have. If we can copy the + // data here, do so, otherwise invoke the link's transfer data routine. + if ((DataLength <= BufferLength) && (SrcNTE->nte_flags & NTE_COPY)) { + while (DataLength) { + uint CopyLength; + +#ifdef VXD + DestPtr = FirstBuffer->VirtualAddress; +#else + // + // BUGBUG: This is inefficient. + // + NdisQueryBuffer(FirstBuffer, &DestPtr, &BufLen); +#endif + CopyLength = MIN(DataLength, FW_BUF_SIZE); + CTEAssert(CopyLength == NdisBufferLength(FirstBuffer)); + CTEMemCopy(DestPtr, Data, CopyLength); + Data += CopyLength; + DataLength -= CopyLength; + FirstBuffer = NDIS_BUFFER_LINKAGE(FirstBuffer); + } + return NDIS_STATUS_SUCCESS; + } + + // We need to call transfer data for this. + + SrcIF = SrcNTE->nte_if; + return (*(SrcIF->if_transfer))(SrcIF->if_lcontext, LContext1, LContext2, + Offset, DataLength, Packet, &DataLength); + + +} + + +//* GetFWPacket - Get a packet for forwarding. +// +// Called when we need to get a packet to forward a datagram. +// +// Entry: Packet - Pointer to where to return a packet. +// IF - Outgoing I/F for packet. +// DestType - Type of outgoing packet. +// +// Returns: Pointer to header buffer. +// +// +IPHeader * +GetFWPacket(PNDIS_PACKET *Packet, Interface *IF, uchar DestType) +{ + CTELockHandle Handle; + PNDIS_PACKET NewPacket; + RouteSendQ *RSQ; + + for (;;) { + CTEGetLock(&FWPacketFreeLock, &Handle); + if ((NewPacket = FWPacketFree) != (PNDIS_PACKET)NULL) { + FWContext *FWC; + + FWC = (FWContext *)NewPacket->ProtocolReserved; + FWPacketFree = FWC->fc_pc.pc_common.pc_link; + FWC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_IPHDR; + FWC->fc_pc.pc_br = NULL; + *Packet = NewPacket; + CTEFreeLock(&FWPacketFreeLock, Handle); + return FWC->fc_hbuff; + } + + // If we couldn't get one, try to grow the list if we can. + if (MaxFWPackets > CurrentFWPackets) { + CTEFreeLock(&FWPacketFreeLock, Handle); + // We're allowed to grow, so try to. + if (GrowFWPackets()) { + continue; // If we grew it, try again. + } + } else + CTEFreeLock(&FWPacketFreeLock, Handle); + + // Either we weren't allowed to grow the list, or we tried to but couldn't. Try yo + // get one that's on the queue already. + if (!IS_BCAST_DEST(DestType)) + RSQ = &((RouteInterface *)IF)->ri_q; + else + RSQ = BCastRSQ; + + if (!RemoveRandomFWPacket(RSQ)) + break; + } + + return (IPHeader *)NULL; +} + + +//** IPForward - Forward a packet. +// +// The routine called when we need to forward a packet. We check if we're supposed +// to act as a gateway, and if we are and the incoming packet is a bcast we check +// and see if we're supposed to forward broadcasts. Assuming we're supposed to +// forward it, we will process any options. If we find some, we do some validation +// to make sure everything is good. After that, we look up the next hop. If we can't +// find one, we'll issue an error. Then we get a packet and buffers, and send it. +// +// Input: SrcNTE - NTE for net on which we received this. +// Header - Pointer to received IPheader. +// HeaderLength - Length of header. +// Data - Pointer to data to be forwarded. +// BufferLength - Length in bytes available in the buffer. +// DestType - Type of destination. +// +// Returns: Nothing. +// +void +IPForward(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, uint HeaderLength, + void *Data, uint BufferLength, NDIS_HANDLE LContext1, uint LContext2, + uchar DestType) +{ + uchar *Options; + uchar OptLength; + OptIndex Index; + IPAddr DestAddr; // IP address we're routing towards. + uchar SendOnSource = FALSE; + IPAddr NextHop; // Next hop IP address. + PNDIS_PACKET Packet; + FWContext *FWC; + IPHeader *NewHeader; // New header. + NDIS_STATUS Status; + uint DataLength; + CTELockHandle TableHandle; + uchar ErrIndex; + IPAddr OutAddr; // Address of interface we're send out on. + Interface *IF; // Interface we're sending out on. + uint MTU; + + if (ForwardPackets) { + + DestAddr = Header->iph_dest; + + // If it's a broadcast, see if we can forward it. We won't forward it if broadcast + // forwarding is turned off, or the destination if the local (all one's) broadcast, + // or it's a multicast (Class D address). We'll pass through subnet broadcasts in + // case there's a source route. This would be odd - maybe we should disable this? + if (IS_BCAST_DEST(DestType)) { + if (!ForwardBCast) { + if (DestType > DEST_REMOTE) + IPSInfo.ipsi_inaddrerrors++; + return; + } + if ((DestAddr == IP_LOCAL_BCST) || + (DestAddr == IP_ZERO_BCST) || + (DestType == DEST_SN_BCAST) || + CLASSD_ADDR(DestAddr)) { + return; + } + } else + if (DestType == DEST_REMOTE) { + SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if); + if (SrcNTE == NULL) { + // Something bad happened. + return; + } + } + + // If the TTL would expire, send a message. + if (Header->iph_ttl <= 1) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED, TTL_IN_TRANSIT,0); + return; + } + + DataLength = net_short(Header->iph_length) - HeaderLength; + + Index.oi_srtype = NO_SR; // So we know we don't have a source route. + Index.oi_srindex = MAX_OPT_SIZE; + Index.oi_rrindex = MAX_OPT_SIZE; + Index.oi_tsindex = MAX_OPT_SIZE; + + // Now check for options, and process any we find. + if (HeaderLength != sizeof(IPHeader)) { + IPOptInfo OptInfo; + + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = HeaderLength - sizeof(IPHeader); + // Validate options, and set up indices. + if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, + PTR_VALID, ((ulong)ErrIndex + sizeof(IPHeader))); + return; + } + + Options = CTEAllocMem(OptInfo.ioi_optlength); + if (!Options) { + IPSInfo.ipsi_outdiscards++; + return; // Couldn't get an + } // option buffer, return; + + // Now copy into our buffer. + CTEMemCopy(Options, OptInfo.ioi_options, OptLength = OptInfo.ioi_optlength); + + // See if we have a source routing option, and if so we may need to process it. If + // we have one, and the destination in the header is us, we need to update the + // route and the header. + if (Index.oi_srindex != MAX_OPT_SIZE) { + if (DestType >= DEST_REMOTE) { // Not for us. + if (Index.oi_srtype == IP_OPT_SSRR) { + // This packet is strict source routed, but we're not the destination! + // We can't continue from here - perhaps we should send an ICMP, but + // I'm not sure which one it would be. + CTEFreeMem(Options); + IPSInfo.ipsi_inaddrerrors++; + return; + } + Index.oi_srindex = MAX_OPT_SIZE; // Don't need to update this. + + } else { // This came here, we need to update the destination address. + uchar *SROpt = Options + Index.oi_srindex; + uchar Pointer; + + Pointer = SROpt[IP_OPT_PTR] - 1; // Index starts from one. + + // Get the next hop address, and see if it's a broadcast. + DestAddr = *(IPAddr UNALIGNED *)&SROpt[Pointer]; + DestType = GetAddrType(DestAddr); // Find address type. + if (DestType == DEST_INVALID) { + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, SR_FAILED, 0); + IPSInfo.ipsi_inhdrerrors++; + CTEFreeMem(Options); + return; + } + + // If we came through here, any sort of broadcast needs to be sent out + // the way it came, so update that flag. + SendOnSource = TRUE; + } + } + } else { // No options. + Options = (uchar *)NULL; + OptLength = 0; + } + + IPSInfo.ipsi_forwdatagrams++; + + // We've processed the options. Now look up the next hop. If we can't + // find one, send back an error. + IF = LookupNextHopWithBuffer(DestAddr, SrcNTE->nte_addr, &NextHop, &MTU, + Header->iph_protocol, (uchar *)Data, BufferLength); + + if (IF == NULL) { + // Couldn't find an outgoing route. + IPSInfo.ipsi_outnoroutes++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + HOST_UNREACH, 0); + if (Options) + CTEFreeMem(Options); + return; + } + + // + // If the DF flag is set, make sure the packet doesn't need + // fragmentation. If this is the case, send an ICMP error + // now while we still have the original IP header. The ICMP + // message includes the MTU so the source host can perform + // Path MTU discovery. + // + if ( (Header->iph_offset & IP_DF_FLAG) && + ((DataLength + (uint)OptLength) > MTU) + ) + { + CTEAssert((MTU + sizeof(IPHeader)) >= 68); + CTEAssert((MTU + sizeof(IPHeader)) <= 0xFFFF); + + IPSInfo.ipsi_fragfails++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + FRAG_NEEDED, net_long((ulong)(MTU + sizeof(IPHeader))) + ); + + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + + // See if we need to filter this packet. If we do, call the filter routine + // to see if it's OK to forward it. + if (ForwardFilterPtr != NULL) { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(Header, Data, BufferLength, + SrcNTE->nte_if->if_filtercontext, IF->if_filtercontext); + + if (Action != FORWARD) { + IPSInfo.ipsi_outdiscards++; + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + // If we have a strict source route and the next hop is not the one + // specified, send back an error. + if (Index.oi_srtype == IP_OPT_SSRR) { + if (DestAddr != NextHop) { + IPSInfo.ipsi_outnoroutes++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + SR_FAILED, 0); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + // Update the options, if we can and we need to. + if ((DestType != DEST_BCAST) && Options != NULL) { + NetTableEntry *OutNTE; + + // Need to find a valid source address for the outgoing interface. + CTEGetLock(&RouteTableLock, &TableHandle); + OutNTE = BestNTEForIF(DestAddr, IF); + if (OutNTE == NULL) { + // No NTE for this IF. Something's wrong, just bail out. + CTEFreeLock(&RouteTableLock, TableHandle); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } else { + OutAddr = OutNTE->nte_addr; + CTEFreeLock(&RouteTableLock, TableHandle); + } + + ErrIndex = UpdateOptions(Options, &Index, + (IP_LOOPBACK(OutAddr) ? DestAddr : OutAddr)); + + if (ErrIndex != MAX_OPT_SIZE) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(OutAddr, Header, ICMP_PARAM_PROBLEM, PTR_VALID, + ((ulong)ErrIndex + sizeof(IPHeader))); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + + // Send a redirect, if we need to. We'll send a redirect if the packet + // is going out on the interface it came in on and the next hop address + // is on the same subnet as the NTE we received it on, and if there + // are no source route options. We also need to make sure that the + // source of the datagram is on the I/F we received it on, so we don't + // send a redirect to another gateway. + // SendICMPErr will check and not send a redirect if this is a broadcast. + if ((SrcNTE->nte_if == IF) && + IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask, + NextHop & SrcNTE->nte_mask) && + IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask, + Header->iph_src & SrcNTE->nte_mask)) + { + if (Index.oi_srindex == MAX_OPT_SIZE) + { + +#ifdef REDIRECT_DEBUG + +#define PR_IP_ADDR(x) \ + ((x)&0x000000ff),(((x)&0x0000ff00)>>8),(((x)&0x00ff0000)>>16),(((x)&0xff000000)>>24) + + + DbgPrint("IP: Sending Redirect. IF = %x SRC_NTE = %x SrcNteIF = %x\n", + IF,SrcNTE,SrcNTE->nte_if); + + DbgPrint("IP: SrcNteAddr = %d.%d.%d.%d Mask = %d.%d.%d.%d\n", + PR_IP_ADDR(SrcNTE->nte_addr), PR_IP_ADDR(SrcNTE->nte_mask)); + + DbgPrint("IP: NextHop = %d.%d.%d.%d Header Src = %d.%d.%d.%d, Dst = %d.%d.%d.%d\n", + PR_IP_ADDR(NextHop), + PR_IP_ADDR(Header->iph_src), + PR_IP_ADDR(Header->iph_dest)); + +#endif + + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_REDIRECT, + REDIRECT_HOST, NextHop); + } + } + + // We have the next hop. Now get a forwarding packet. + if ((NewHeader = GetFWPacket(&Packet, IF, DestType)) != + (IPHeader *)NULL) { + + // Got the header. Fill it in. + + NewHeader->iph_verlen = Header->iph_verlen; + NewHeader->iph_tos = Header->iph_tos; + NewHeader->iph_length = Header->iph_length; + NewHeader->iph_id = Header->iph_id; + NewHeader->iph_offset = Header->iph_offset; + NewHeader->iph_protocol = Header->iph_protocol; + NewHeader->iph_src = Header->iph_src; + + NewHeader->iph_dest = DestAddr; + NewHeader->iph_ttl = Header->iph_ttl - 1; + NewHeader->iph_xsum = 0; + + // Save the packet forwarding context info. + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_options = Options; + FWC->fc_optlength = OptLength; + FWC->fc_if = IF; + FWC->fc_mtu = MTU; + FWC->fc_srcnte = SrcNTE; + FWC->fc_nexthop = NextHop; + FWC->fc_sos = SendOnSource; + FWC->fc_dtype = DestType; + FWC->fc_index = Index; + + // Now that we have a packet, go ahead and transfer data the + // data in if we need to. + Status = GetFWBuffer(SrcNTE, Packet, Data, DataLength, BufferLength, + HeaderLength, LContext1, LContext2); + + // If the status is pending, don't do anything now. Otherwise, + // if the status is success send the packet. + if (Status != NDIS_STATUS_PENDING) + if (Status == NDIS_STATUS_SUCCESS) { + SendFWPacket(Packet, Status, DataLength); + } else { + // Some sort of failure. Free the packet. + IPSInfo.ipsi_outdiscards++; + FreeFWPacket(Packet); + } + } else { // Couldn't get a packet, so drop this. + IPSInfo.ipsi_outdiscards++; + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + } + } else { // Forward called, but forwarding + // turned off. + if (DestType != DEST_BCAST && DestType != DEST_SN_BCAST) { + // No need to go through here for strictly broadcast packets, + // although we want to bump the counters for remote bcast stuff. + IPSInfo.ipsi_inaddrerrors++; + + if (!IS_BCAST_DEST(DestType)) { + if (DestType == DEST_LOCAL) // Called when local, must be SR. + SendICMPErr(SrcNTE->nte_addr, Header, + ICMP_DEST_UNREACH, SR_FAILED, 0); + } + } + } + +} + +//* AddNTERoutes - Add the routes for an NTE. +// +// Called during initalization or during DHCP address assignment to add +// routes. We add routes for the address of the NTE, including routes +// to the subnet and the address itself. +// +// Input: NTE - NTE for which to add routes. +// +// Returns: TRUE if they were all added, FALSE if not. +// +uint +AddNTERoutes(NetTableEntry *NTE) +{ + IPMask Mask, SNMask; + Interface *IF; + CTELockHandle Handle; + IPAddr AllSNBCast; + IP_STATUS Status; + + // First, add the route to the address itself. This is a route through + // the loopback interface. + + if (AddRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if, + LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL) != IP_SUCCESS) + return FALSE; + + Mask = IPNetMask(NTE->nte_addr); + IF = NTE->nte_if; + + // Now add the route for the all-subnet's broadcast, if one doesn't already + // exist. There is special case code to handle this in SendIPBCast, so the + // exact interface we add this on doesn't really matter. + + CTEGetLock(&RouteTableLock, &Handle); + AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask); + if (LookupRTE(AllSNBCast, NULL_IP_ADDR, HOST_ROUTE_PRI) == NULL) { + + Status = LockedAddRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL); + + } else + Status = IP_SUCCESS; + + CTEFreeLock(&RouteTableLock, Handle); + + if (Status != IP_SUCCESS) + return FALSE; + + // If we're doing IGMP, add the route to the multicast address. + if (IGMPLevel != 0) { + if (AddRoute(CLASSD_MASK, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS) + return FALSE; + } + + if(NTE->nte_mask != HOST_MASK) + { + // And finally the route to the subnet. + SNMask = NTE->nte_mask; + if (AddRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS) + return FALSE; + } + + return TRUE; +} + + +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif + +uint BCastMinMTU = 0xffff; + +//* InitNTERouting - do per NTE route initialization. +// +// Called when we need to initialize per-NTE routing. For the specified NTE, +// call AddNTERoutes to add a route for a net bcast, subnet bcast, and local +// attached subnet. The net bcast entry is sort of a filler - net and +// global bcasts are always handled specially. For this reason we specify +// the FirstInterface when adding the route. Subnet bcasts are assumed to +// only go out on one interface, so the actual interface to be used is +// specifed. If two interfaces are on the same subnet the last interface is +// the one that will be used. +// +// Input: NTE - NTE for which routing is to be initialized. +// NumGWs - Number of default gateways to add. +// GWList - List of default gateways. +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +InitNTERouting(NetTableEntry *NTE, uint NumGWs, IPAddr *GWList) +{ + uint i; + Interface *IF; + + CTERefillMem(); + if (NTE != LoopNTE) { + BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss); + + IF = NTE->nte_if; + AddRoute(IF->if_bcast, HOST_MASK, IPADDR_LOCAL, FirstIF, + BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);// Route for local + // bcast. + if (NTE->nte_flags & NTE_VALID) { + if (!AddNTERoutes(NTE)) + return FALSE; + + // Now add the default routes that are present on this net. We + // don't check for errors here, but we should probably + // log an error. + for (i = 0; i < NumGWs;i++) { + IPAddr GWAddr; + + GWAddr = net_long(GWList[i]); + + if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) { + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, + IPADDR_LOCAL, NTE->nte_if, NTE->nte_mss, 1, + IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL); + } else + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, + net_long(GWList[i]), NTE->nte_if, NTE->nte_mss, 1, + IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL); + } + } + } + return TRUE; +} + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + +//* InitRouting - Initialize our routing table. +// +// Called during initialization to initialize the routing table. +// +// Entry: Nothing. +// +// Returns: True if we succeeded, False if we didn't. +// +int +InitRouting(IPConfigInfo *ci) +{ + int i; + + CTERefillMem(); + + CTEInitLock(&RouteTableLock); + + DefGWConfigured = 0; + DefGWActive = 0; + + CTEMemSet(&DummyInterface, 0, sizeof(DummyInterface)); + DummyInterface.ri_if.if_xmit = DummyXmit; + DummyInterface.ri_if.if_transfer = DummyXfer; + DummyInterface.ri_if.if_close = DummyClose; + DummyInterface.ri_if.if_invalidate = DummyInvalidate; + DummyInterface.ri_if.if_qinfo = DummyQInfo; + DummyInterface.ri_if.if_setinfo = DummySetInfo; + DummyInterface.ri_if.if_getelist = DummyGetEList; + DummyInterface.ri_if.if_addaddr = DummyAddAddr; + DummyInterface.ri_if.if_deladdr = DummyDelAddr; + DummyInterface.ri_if.if_bcast = IP_LOCAL_BCST; + DummyInterface.ri_if.if_speed = 10000000; + DummyInterface.ri_if.if_mtu = 1500; + DummyInterface.ri_if.if_index = INVALID_IF_INDEX; + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) + RouteTable[i] = (RouteTableEntry *)NULL; + + // We've created at least one net. We need to add routing table entries for + // the global broadcast address, as well as for subnet and net broadcasts, + // and routing entries for the local subnet. We alse need to add a loopback + // route for the loopback net. Below, we'll add a host route for ourselves + // through the loopback net. + AddRoute(LOOPBACK_ADDR & CLASSA_MASK, CLASSA_MASK, IPADDR_LOCAL, + LoopNTE->nte_if, LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL); + // Route for loopback. + RouterConfigured = (uchar)ci->ici_gateway; + + CTEInitTimer(&IPRouteTimer); + + CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL); + return TRUE; + +} + +//* InitGateway - Initialize our gateway functionality. +// +// Called during init. time to initialize our gateway functionality. If we're +// not connfigured as a router, we do nothing. If we are, we allocate the +// resources we need and do other router initialization. +// +// Input: ci - Config info. +// +// Returns: TRUE if we succeed, FALSE if don't. +// +uint +InitGateway(IPConfigInfo *ci) +{ + uint FWBufSize, FWPackets; + uint FWBufCount; + NDIS_STATUS Status; + NDIS_HANDLE BufferPool, FWBufferPool, PacketPool; + IPHeader *HeaderPtr = NULL; + uchar *FWBuffer = NULL; + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + RouteInterface *RtIF; + NetTableEntry *NTE; + uint i; + + // If we're going to be a router, allocate and initialize the resources we + // need for that. + BCastRSQ = NULL; + if (RouterConfigured) { + + + CTERefillMem(); + RtPI = CTEAllocMem(sizeof(ProtInfo)); + if (RtPI == (ProtInfo *)NULL) + goto failure; + + RtPI->pi_xmitdone = FWSendComplete; + + CTEInitLock(&FWPacketFreeLock); + CTEInitLock(&FWBufFreeLock); + + MaxFWBufferSize = ci->ici_maxfwbufsize; + MaxFWPackets = ci->ici_maxfwpackets; + FWBufSize = MIN(ci->ici_fwbufsize, MaxFWBufferSize); + FWPackets = MIN(ci->ici_fwpackets, MaxFWPackets); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + RtIF = (RouteInterface *)NTE->nte_if; + + RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_running = FALSE; + RtIF->ri_q.rsq_pending = 0; + RtIF->ri_q.rsq_qlength = 0; + CTEInitLock(&RtIF->ri_q.rsq_lock); + } + + BCastRSQ = CTEAllocMem(sizeof(RouteSendQ)); + + if (BCastRSQ == (RouteSendQ *)NULL) + goto failure; + + BCastRSQ->rsq_qh.fq_next = &BCastRSQ->rsq_qh; + BCastRSQ->rsq_qh.fq_prev = &BCastRSQ->rsq_qh; + BCastRSQ->rsq_pending = 0; + BCastRSQ->rsq_maxpending = DEFAULT_MAX_PENDING; + BCastRSQ->rsq_qlength = 0; + BCastRSQ->rsq_running = FALSE; + CTEInitLock(&BCastRSQ->rsq_lock); + + RtIF = (RouteInterface *)&LoopInterface; + RtIF->ri_q.rsq_maxpending = DEFAULT_MAX_PENDING; + + // Round the specified size down to a multiple of our FW buf size. + CTERefillMem(); + FWBufCount = FWBufSize / FW_BUF_SIZE; + FWBufSize = FWBufCount * FW_BUF_SIZE; + + // Allocate the buffers, packets, and memory for our header buffers. + HeaderPtr = CTEAllocMem(FWPackets * sizeof(IPHeader)); + if (HeaderPtr == (IPHeader *)NULL) + goto failure; + + NdisAllocateBufferPool(&Status, &BufferPool, FWPackets); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; // Couldn't be a router, fail. + } + + NdisAllocatePacketPool(&Status, &PacketPool, FWPackets, sizeof(FWContext)); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Allocate resources for our the buffer pool. + CTERefillMem(); + FWBuffer = CTEAllocMem(FWBufSize); + if (FWBuffer == NULL) { // Couldn't get buffer space. + NdisFreePacketPool(PacketPool); + NdisFreeBufferPool(BufferPool); + goto failure; + } + + NdisAllocateBufferPool(&Status, &FWBufferPool, FWBufCount); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(PacketPool); + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Everythings allocated. Put it all together and stick them on the + // free list. + for (i = 0; i < FWPackets; i++) { + FWContext *FWC; + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr, + sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + NdisAllocatePacket(&Status, &Packet, PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext)); + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_hndisbuff = Buffer; + FWC->fc_hbuff = HeaderPtr; + FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW; + FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP; + FWC->fc_pc.pc_pi = RtPI; + FWC->fc_pc.pc_context = Packet; + + FreeFWPacket(Packet); + HeaderPtr++; + } + + for (i = 0; i < FWBufCount; i++) { + NdisAllocateBuffer(&Status, &Buffer, FWBufferPool, FWBuffer, + FW_BUF_SIZE); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + + Buffer->Next = FWBufFree; // BUGBUG portability + FWBufFree = Buffer; + FWBuffer += FW_BUF_SIZE; + } + + CurrentFWPackets = FWPackets; + CurrentFWBufferSize = FWBufSize; + + +#if 0 + ForwardBCast = (uchar)ci->ici_fwbcast; +#else + ForwardBCast = FALSE; +#endif + ForwardPackets = TRUE; + } + + return TRUE; + +failure: + if (RtPI != NULL) + CTEFreeMem(RtPI); + if (BCastRSQ != NULL) + CTEFreeMem(BCastRSQ); + if (HeaderPtr != NULL) + CTEFreeMem(HeaderPtr); + if (FWBuffer != NULL) + CTEFreeMem(FWBuffer); + + ForwardBCast = FALSE; + ForwardPackets = FALSE; + RouterConfigured = FALSE; + return FALSE; + +} +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/iproute.h b/private/ntos/tdi/tcpip/ip/iproute.h new file mode 100644 index 000000000..e06af7100 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iproute.h @@ -0,0 +1,107 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPROUTE.H - IP routing definitions. +// +// This file contains all of the definitions for routing code that are +// visible to modules outside iproute.c + + +extern struct Interface *LookupNextHop(IPAddr Dest, IPAddr Src, + IPAddr *FirstHop, uint *MTU); +extern struct Interface *LookupNextHopWithBuffer(IPAddr Dest, IPAddr Src, + IPAddr *FirstHop, uint *MTU, uchar Protocol, + uchar *Buffer, uint Length); + +extern void FlushATCache(IPAddr Address); +extern uchar GetAddrType(IPAddr Address); +extern uint InvalidSourceAddress(IPAddr Address); +extern uchar GetLocalNTE(IPAddr Address, NetTableEntry **NTE); +extern uchar IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE); +extern void SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, + uint DataLength); +extern void IPForward(NetTableEntry *SrcNTE, + IPHeader UNALIGNED *Header, uint HeaderLength, + void *Data, uint BufferLength, + NDIS_HANDLE LContext1, uint LContext2, + uchar DestType); + +extern uint AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, + uchar *Buffer, uint Length); +extern void Redirect(NetTableEntry *NTE, IPAddr RDSrc, + IPAddr Target, IPAddr Src, IPAddr FirstHop); +extern IP_STATUS AddRoute(IPAddr Destination, IPMask Mask, + IPAddr FirstHop, Interface *OutIF, uint MTU, + uint Metric, uint Proto, uint AType, + void *Context); +extern IP_STATUS DeleteRoute(IPAddr Destination, IPMask Mask, + IPAddr FirstHop, Interface *OutIF); +extern void *GetRouteContext(IPAddr Destination, IPAddr Source); + +extern NetTableEntry *BestNTEForIF(IPAddr Dest, Interface *IF); +extern void RTWalk(uint (*CallFunc)(struct RouteTableEntry *, + void *, void *), void *Context, void *Context1); + +extern uint DeleteRTEOnIF(struct RouteTableEntry *RTE, + void *Context, void *Context1); +extern uint InvalidateRCEOnIF(struct RouteTableEntry *RTE, + void *Context, void *Context1); +extern uint SetMTUOnIF(struct RouteTableEntry *RTE, void *Context, + void *Context1); +extern uint SetMTUToAddr(struct RouteTableEntry *RTE, void *Context, + void *Context1); +extern uint AddNTERoutes(struct NetTableEntry *NTE); +extern void IPCheckRoute(IPAddr Dest, IPAddr Src); +extern void RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU); +extern IP_STATUS IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, + uint *MaxPathSpeed); +extern int InitRouting(struct IPConfigInfo *ci); +extern uint InitNTERouting(NetTableEntry *NTE, uint NumGWs, + IPAddr *GW); +extern uint InitGateway(struct IPConfigInfo *ci); +extern IPAddr OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, + uchar *Type, ushort *MSS, IPOptInfo *OptInfo); +extern void CloseRCE(RouteCacheEntry *RCE); +extern uint IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, + Interface *OutIF); + +EXTERNAL_LOCK(RouteTableLock) + +extern uint DeadGWDetect; +extern uint PMTUDiscovery; +extern uchar ForwardPackets; +extern uchar RouterConfigured; +// Pointer to callout routine for dial on demand. +extern IPMapRouteToInterfacePtr DODCallout; + +// Pointer to packet filter handler. +extern IPPacketFilterPtr ForwardFilterPtr; + +#define IPADDR_LOCAL 0xffffffff // Indicates that IP address is + // directly connected. + +#define IP_LOCAL_BCST 0xffffffff +#define IP_ZERO_BCST 0 + +#define HOST_MASK 0xffffffff +#define DEFAULT_MASK 0 + + +#ifdef NT +#define LOOPBACK_MSS (1500 - sizeof(IPHeader)) +#else // NT +#define LOOPBACK_MSS 256 +#endif // NT + + +#define LOOPBACK_ADDR 0x0100007f +#define IP_LOOPBACK(x) (((x) & CLASSA_MASK) == 0x7f) + +#define ATYPE_PERM 0 // A permanent route. +#define ATYPE_OVERRIDE 1 // Semi-permanent - can be + // overriden. +#define ATYPE_TEMP 2 // A temporary route. + diff --git a/private/ntos/tdi/tcpip/ip/iprtdef.h b/private/ntos/tdi/tcpip/ip/iprtdef.h new file mode 100644 index 000000000..1082646ba --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iprtdef.h @@ -0,0 +1,135 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#include "ipfilter.h" + +//** IPRTDEF.H - IP private routing definitions. +// +// This file contains all of the definitions private to the routing +// module. + +//* Route table entry. + +struct RouteTableEntry { + struct RouteTableEntry *rte_next; // Next in hash chain. + IPAddr rte_dest; // Destination of route. + IPMask rte_mask; // Mask to use when examining route. + IPAddr rte_addr; // First hop for this route. + uint rte_priority; // Priority of this route: + // essentially the number + // of bits set in mask. + uint rte_metric; // Metric of route. Lower + // is better. + uint rte_mtu; // MTU for this route. + struct Interface *rte_if; // Outbound interface. + RouteCacheEntry *rte_rcelist; + ushort rte_type; // Type of route. + ushort rte_flags; // Flags for route. + uint rte_admintype; // Admin type of route. + uint rte_proto; // How we learned about the + // route. + uint rte_valid; // Up time (in seconds) + // route was last known to be + // valid. + uint rte_mtuchange; // System up time (in seconds) + // MTU was changed. + ROUTE_CONTEXT rte_context; // Dial-on-demand context for this route. +}; /* RouteTableEntry */ + +#define ADDR_FROM_RTE(R, A) (IP_ADDR_EQUAL((R)->rte_addr, IPADDR_LOCAL) ? (A) : \ + (R)->rte_addr) +#define IF_FROM_RTE(R) ((R)->rte_if) +#define MTU_FROM_RTE(R) ((R)->rte_mtu) +#define SRC_FROM_RTE(R) ((R)->rte_src) + +#define RTE_VALID 1 +#define RTE_INCREASE 2 // Set if last MTU change was an + // increase. +#define RTE_IF_VALID 4 // Set to TRUE if rte_if is valid. + +#define IP_ROUTE_TIMEOUT 60L*1000L // Route timer fires once a minute. + +#define MTU_INCREASE_TIME 120 // Number of seconds after increase + // to re-increase. +#define MTU_DECREASE_TIME 600 // Number of seconds after decrease + // to re-increase. + +#define MAX_ICMP_ROUTE_VALID 600 // Time to timeout an unused ICMP + // derived route, in seconds. + +#define MIN_RT_VALID 60 // Minimum time a route is assumed + // to be valid, in seconds. + +#define MIN_VALID_MTU 68 // Minimum valid MTU we can have. +#define HOST_ROUTE_PRI 32 +#define DEFAULT_ROUTE_PRI 0 + +typedef struct RouteTableEntry RouteTableEntry; + +//* Forward Q linkage structure. +struct FWQ { + struct FWQ *fq_next; + struct FWQ *fq_prev; +}; /* FWQ */ + +typedef struct FWQ FWQ; + + +//* Forward context structure, used when TD'ing a packet to be forwarded. +struct FWContext { + PacketContext fc_pc; // Dummy packet context for send routines. + FWQ fc_q; // Queue structure. + PNDIS_BUFFER fc_hndisbuff; // Pointer to NDIS buffer for header. + IPHeader *fc_hbuff; // Header buffer. + PNDIS_BUFFER fc_buffhead; // Head of list of NDIS buffers. + PNDIS_BUFFER fc_bufftail; // Tail of list of NDIS buffers. + uchar *fc_options; // Options, + Interface *fc_if; // Destination interface. + IPAddr fc_outaddr; // IP address of interface. + uint fc_mtu; // Max MTU outgoing. + NetTableEntry *fc_srcnte; // Source NTE. + IPAddr fc_nexthop; // Next hop. + uint fc_datalength; // Length in bytes of data. + OptIndex fc_index; // Index of relevant options. + uchar fc_optlength; // Length of options. + uchar fc_sos; // Send on source indicator. + uchar fc_dtype; // Dest type. + uchar fc_pad; +}; /* FWContext */ + +typedef struct FWContext FWContext; + +#define PACKET_FROM_FWQ(_fwq_) (PNDIS_PACKET)((uchar *)(_fwq_) - (offsetof(struct FWContext, fc_q) + \ + offsetof(NDIS_PACKET, ProtocolReserved))) + +//* Route send queue structure. This consists of a dummy FWContext for use as +// a queue head, a count of sends pending on the interface, and a count of packets +// in the queue. +struct RouteSendQ { + FWQ rsq_qh; + uint rsq_pending; + uint rsq_maxpending; + uint rsq_qlength; + uint rsq_running; + DEFINE_LOCK_STRUCTURE(rsq_lock) +}; /* RouteSendQ */ + +typedef struct RouteSendQ RouteSendQ; + + +//* Routing interface, a superset of the ordinary interface when we're configured as a router. +struct RouteInterface { + Interface ri_if; + RouteSendQ ri_q; +}; /* RouteInterface */ + +typedef struct RouteInterface RouteInterface; + +extern IPMask IPMaskTable[]; + +#define IPNetMask(a) IPMaskTable[(*(uchar *)&(a)) >> 4] + + diff --git a/private/ntos/tdi/tcpip/ip/ipstatus.c b/private/ntos/tdi/tcpip/ip/ipstatus.c new file mode 100644 index 000000000..e71e36280 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipstatus.c @@ -0,0 +1,205 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** ipstatus.c - IP status routines. +// +// This module contains all routines related to status indications. +// + + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "llipif.h" +#include "iproute.h" +#include "ipinfo.h" + +#if 0 +EXTERNAL_LOCK(PILock) +#endif +extern ProtInfo IPProtInfo[]; // Protocol information table. +extern int NextPI; // Next PI field to be used. +extern ProtInfo *RawPI; // Raw IP protinfo + +//* FindULStatus - Find the upper layer status handler. +// +// Called when we need to find the upper layer status handler for a particular +// protocol. +// +// Entry: Protocol - Protocol to look up +// +// Returns: A pointer to the ULStatus proc, or NULL if it can't find one. +// +ULStatusProc +FindULStatus(uchar Protocol) +{ + ULStatusProc StatusProc = (ULStatusProc)NULL; + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + for ( i = 0; i < NextPI; i++) { + if (IPProtInfo[i].pi_protocol == Protocol) { + StatusProc = IPProtInfo[i].pi_status; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return StatusProc; + } + } + + if (RawPI != NULL) { + StatusProc = RawPI->pi_status; + } + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + + return StatusProc; +} + + +//* ULMTUNotify - Notify the upper layers of an MTU change. +// +// Called when we need to notify the upper layers of an MTU change. We'll +// loop through the status table, calling each status proc with the info. +// +// This routine doesn't do any locking of the protinfo table. We might need +// to check this. +// +// Input: Dest - Destination address affected. +// Src - Source address affected. +// Prot - Protocol that triggered change, if any. +// Ptr - Pointer to protocol info, if any. +// NewMTU - New MTU to tell them about. +// +// Returns: Nothing. +// +void +ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr, uint NewMTU) +{ + ULStatusProc StatusProc; + int i; + + // First, notify the specific client that a frame has been dropped + // and needs to be retransmitted. + + StatusProc = FindULStatus(Prot); + if (StatusProc != NULL) + (*StatusProc)(IP_NET_STATUS, IP_SPEC_MTU_CHANGE, Dest, Src, + NULL_IP_ADDR, NewMTU, Ptr); + + // Now notify all UL entities that the MTU has changed. + for (i = 0; i < NextPI; i++) { + StatusProc = IPProtInfo[i].pi_status; + + if (StatusProc != NULL) + (*StatusProc)(IP_HW_STATUS, IP_MTU_CHANGE, Dest, Src, NULL_IP_ADDR, + NewMTU, Ptr); + } +} + +#ifdef CHICAGO + +//* IPULUnloadNotify - Notify clients that we're unloading. +// +// Called when we receive an unload message. We'll notify the upper layers +// that we're unloading. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +IPULUnloadNotify(void) +{ + ULStatusProc StatusProc; + int i; + + // Now notify all UL entities that the MTU has changed. + for (i = 0; i < NextPI; i++) { + StatusProc = IPProtInfo[i].pi_status; + + if (StatusProc != NULL) + (*StatusProc)(IP_HW_STATUS, IP_UNLOAD, NULL_IP_ADDR, NULL_IP_ADDR, + NULL_IP_ADDR, 0, NULL); + } +} + +#endif + +//* IPStatus - Handle a link layer status call. +// +// This is the routine called by the link layer when some sort of 'important' +// status change occurs. +// +// Entry: Context - Context value we gave to the link layer. +// Status - Status change code. +// Buffer - Pointer to buffer of status information. +// BufferSize - Size of Buffer. +// +// Returns: Nothing. +// +void +IPStatus(void *Context, uint Status, void *Buffer, uint BufferSize) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + LLIPSpeedChange *LSC; + LLIPMTUChange *LMC; + LLIPAddrMTUChange *LAM; + uint NewMTU; + Interface *IF; + + + switch (Status) { + + case LLIP_STATUS_SPEED_CHANGE: + if (BufferSize < sizeof(LLIPSpeedChange)) + break; + LSC = (LLIPSpeedChange *)Buffer; + NTE->nte_if->if_speed = LSC->lsc_speed; + break; + case LLIP_STATUS_MTU_CHANGE: + if (BufferSize < sizeof(LLIPMTUChange)) + break; + // Walk through the NTEs on the IF, updating their MTUs. + IF = NTE->nte_if; + LMC = (LLIPMTUChange *)Buffer; + IF->if_mtu = LMC->lmc_mtu; + NewMTU = LMC->lmc_mtu - sizeof(IPHeader); + NTE = IF->if_nte; + while (NTE != NULL) { + NTE->nte_mss = NewMTU; + NTE = NTE->nte_ifnext; + } + RTWalk(SetMTUOnIF, IF, &NewMTU); + break; + case LLIP_STATUS_ADDR_MTU_CHANGE: + if (BufferSize < sizeof(LLIPAddrMTUChange)) + break; + // The MTU for a specific remote address has changed. Update all + // routes that use that remote address as a first hop, and then + // add a host route to that remote address, specifying the new + // MTU. + LAM = (LLIPAddrMTUChange *)Buffer; + NewMTU = LAM->lam_mtu - sizeof(IPHeader); + RTWalk(SetMTUToAddr, &LAM->lam_addr, &NewMTU); + AddRoute(LAM->lam_addr, HOST_MASK, IPADDR_LOCAL, NTE->nte_if, NewMTU, + 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, GetRouteContext(LAM->lam_addr, + NTE->nte_addr)); + break; + default: + break; + } + +} + diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.c b/private/ntos/tdi/tcpip/ip/ipxmit.c new file mode 100644 index 000000000..61ee32b90 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipxmit.c @@ -0,0 +1,1949 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** ipxmit.c - IP transmit routines. +// +// This module contains all transmit related IP routines. +// + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "info.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipfilter.h" + +typedef struct NdisResEntry { + struct NdisResEntry *nre_next; + NDIS_HANDLE nre_handle; + uchar *nre_buffer; +} NdisResEntry; + +extern BufferReference *GetBufferReference(void); + +extern NetTableEntry *NetTableList; // Pointer to the net table. +extern NetTableEntry *LoopNTE; // Pointer to loopback NTE. +extern NetTableEntry *DHCPNTE; // Pointer to NTE currently being + // DHCP'd. + +extern ulong TimeStamp; // Starting timestamp. +extern ulong TSFlag; // Mask to use on this. +extern uint NumNTE; + +//* Global variables for buffers and packets. +DEFINE_LOCK_STRUCTURE(HeaderLock) +#ifdef NT +SLIST_HEADER PacketList; +SLIST_HEADER HdrBufList; +#else +PNDIS_PACKET PacketList; +PNDIS_BUFFER HdrBufList = NULL; +#endif + +NdisResEntry *PacketPoolList = NULL; +NdisResEntry *HdrPoolList = NULL; + +uint CurrentPacketCount = 0; +uint MaxPacketCount = 0xfffffff; + +uint CurrentHdrBufCount = 0; +uint MaxHdrBufCount = 0xffffffff; + +NDIS_HANDLE BufferPool; + +#define HDR_BUF_GROW_COUNT 16 +#define PACKET_GROW_COUNT 16 + +//* Global IP ID. +ulong IPID; + +//** FreeIPHdrBuffer - Free a buffer back to the pool. +// +// Input: Buffer - Hdr buffer to be freed. +// +// Returns: Nothing. +// +void +FreeIPHdrBuffer(PNDIS_BUFFER Buffer) +{ + +#ifdef VXD + NDIS_BUFFER_LINKAGE(Buffer) = HdrBufList; + HdrBufList = Buffer; +#else + + ExInterlockedPushEntrySList( + &HdrBufList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next), + &HeaderLock + ); + +#endif + +} + +//** FreeIPBufferChain - Free a chain of IP buffers. +// +// This routine takes a chain of NDIS_BUFFERs, and frees them all. +// +// Entry: Buffer - Pointer to buffer chain to be freed. +// +// Returns: Nothing. +// +void +FreeIPBufferChain(PNDIS_BUFFER Buffer) +{ + PNDIS_BUFFER NextBuffer; + + while (Buffer != (PNDIS_BUFFER)NULL) { + NdisGetNextBuffer(Buffer, &NextBuffer); + NdisFreeBuffer(Buffer); + Buffer = NextBuffer; + } +} + +//* FreeIPPacket - Free an IP packet when we're done with it. +// +// Called when a send completes and a packet needs to be freed. We look at the +// packet, decide what to do with it, and free the appropriate components. +// +// Entry: Packet - Packet to be freed. +// +// Returns: Pointer to next unfreed buffer on packet, or NULL if all buffers freed +// (i.e. this was a fragmented packet). +// +PNDIS_BUFFER +FreeIPPacket(PNDIS_PACKET Packet) +{ + PNDIS_BUFFER NextBuffer, OldBuffer; + PacketContext *pc = (PacketContext *)Packet->ProtocolReserved; + + + // BUGBUG - Get NDIS fixed to make this portable. +#ifdef VXD + NextBuffer = Packet->Private.Head; +#else // VXD + NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL); +#endif // VXD + + // If there's no IP header on this packet, we have nothing else to do. + if (!(pc->pc_common.pc_flags & (PACKET_FLAG_IPHDR | PACKET_FLAG_FW))) { + CTEAssert(pc->pc_common.pc_flags == 0); + + NdisReinitializePacket(Packet); + +#ifdef VXD + pc->pc_common.pc_link = PacketList; + PacketList = Packet; +#else + ExInterlockedPushEntrySList( + &PacketList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next), + &HeaderLock + ); +#endif + + return NextBuffer; + } + + pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR; + + OldBuffer = NextBuffer; + CTEAssert(OldBuffer != NULL); + + NextBuffer = NDIS_BUFFER_LINKAGE(NextBuffer); + + if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) { // Have options with + // this packet. + PNDIS_BUFFER OptBuffer; + void *Options; + uint OptSize; + + OptBuffer = NextBuffer; + CTEAssert(OptBuffer != NULL); + + NdisGetNextBuffer(OptBuffer,&NextBuffer); + + CTEAssert(NextBuffer != NULL); + + NdisQueryBuffer(OptBuffer, &Options, &OptSize); + // If this is a FW packet, the options don't really belong to us, so + // don't free them. + if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) + CTEFreeMem(Options); + NdisFreeBuffer(OptBuffer); + pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS; + } + + if (pc->pc_common.pc_flags & PACKET_FLAG_IPBUF) { // This packet is all + // IP buffers. + (void)FreeIPBufferChain(NextBuffer); + NextBuffer = (PNDIS_BUFFER)NULL; + pc->pc_common.pc_flags &= ~PACKET_FLAG_IPBUF; + } + + + if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) { + FreeIPHdrBuffer(OldBuffer); + NdisReinitializePacket(Packet); +#ifdef _PNP_POWER + pc->pc_if = NULL; +#endif + +#ifdef VXD + pc->pc_common.pc_link = PacketList; + PacketList = Packet; +#else + ExInterlockedPushEntrySList( + &PacketList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next), + &HeaderLock + ); +#endif + } + + return NextBuffer; +} + +//** GrowIPPacketList - Grow the number of packets in our list. +// +// Called when we need to grow the number of packets in our list. We assume +// this routine is called with the HeaderLock held. We check to see if +// we've reached our limit on the number of packets, and if we haven't we'll +// grow the free list. +// +// Input: Nothing. +// +// Returns: Pointer to newly allocated packet, or NULL if this faild. +// +PNDIS_PACKET +GrowIPPacketList(void) +{ + NdisResEntry *NewEntry; + NDIS_STATUS Status; + PNDIS_PACKET Packet, ReturnPacket; + uint i; + CTELockHandle Handle; + + CTEGetLock(&HeaderLock, &Handle); + + if (CurrentPacketCount >= MaxPacketCount) + goto failure; + + // First, allocate a tracking structure. + NewEntry = CTEAllocMem(sizeof(NdisResEntry)); + if (NewEntry == NULL) + goto failure; + + // Got a tracking structure. Now allocate a packet pool. + NdisAllocatePacketPool(&Status, &NewEntry->nre_handle, PACKET_GROW_COUNT, + sizeof(PacketContext)); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewEntry); + goto failure; + } + + // We've allocated the pool. Now initialize the packets, and link them + // on the free list. + ReturnPacket = NULL; + + // Link the new NDIS resource tracker entry onto the list. + NewEntry->nre_next = PacketPoolList; + PacketPoolList = NewEntry; + CurrentPacketCount += PACKET_GROW_COUNT; + CTEFreeLock(&HeaderLock, Handle); + + for (i = 0; i < PACKET_GROW_COUNT; i++) { + PacketContext *PC; + + NdisAllocatePacket(&Status, &Packet, NewEntry->nre_handle); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(PacketContext)); + PC = (PacketContext *)Packet->ProtocolReserved; + PC->pc_common.pc_owner = PACKET_OWNER_IP; + if (i != 0) { + (void)FreeIPPacket(Packet); + } else + ReturnPacket = Packet; + + } + + // We've put all but the first one on the list. Return the first one. + return ReturnPacket; + +failure: + CTEFreeLock(&HeaderLock, Handle); + return NULL; + +} + + +//** GrowHdrBufList - Grow the our IP header buffer list. +// +// Called when we need to grow our header buffer list. We allocate a tracking +// structure, a buffer pool and a bunch of buffers. Put them all together +// and link them on the list. +// +// Input: Nothing. +// +// Returns: Pointer to newly header buffer, or NULL if this faild. +// +PNDIS_BUFFER +GrowHdrBufList(void) +{ + NdisResEntry *NewEntry; + NDIS_STATUS Status; + PNDIS_BUFFER Buffer, ReturnBuffer; + uchar *Hdr; + uint i; + CTELockHandle Handle; + + CTEGetLock(&HeaderLock, &Handle); + + // Make sure we can grow. + if (CurrentHdrBufCount >= MaxHdrBufCount) + goto failure; + + // First, allocate a tracking structure. + NewEntry = CTEAllocMem(sizeof(NdisResEntry)); + if (NewEntry == NULL) + goto failure; + + // Got a tracking structure. Now allocate a buffer pool. + NdisAllocateBufferPool(&Status, &NewEntry->nre_handle, HDR_BUF_GROW_COUNT); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewEntry); + goto failure; + } + + // We've allocated the pool. Now allocate memory for the buffers. + Hdr = CTEAllocMem(sizeof(IPHeader) * HDR_BUF_GROW_COUNT); + if (Hdr == NULL) { + // Couldn't get memory for the headers. + NdisFreeBufferPool(NewEntry->nre_handle); + CTEFreeMem(NewEntry); + goto failure; + } + + NewEntry->nre_buffer = Hdr; + + NewEntry->nre_next = HdrPoolList; + HdrPoolList = NewEntry; + ReturnBuffer = NULL; + CurrentHdrBufCount += HDR_BUF_GROW_COUNT; + CTEFreeLock(&HeaderLock, Handle); + + for (i = 0; i < HDR_BUF_GROW_COUNT; i++) { + + NdisAllocateBuffer(&Status, &Buffer, NewEntry->nre_handle, + Hdr, sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + if (i != 0) { + FreeIPHdrBuffer(Buffer); + } else + ReturnBuffer = Buffer; + + Hdr += sizeof(IPHeader); + + } + + // Update the count for any we didn't actually allocate. + CTEInterlockedAddUlong(&CurrentHdrBufCount, i - HDR_BUF_GROW_COUNT, + &HeaderLock); + + // We've put all but the first one on the list. Return the first one. + return ReturnBuffer; + +failure: + CTEFreeLock(&HeaderLock, Handle); + return NULL; + +} + +//** GetIPPacket - Get an NDIS packet to use. +// +// A routine to allocate an NDIS packet. +// +// Entry: Nothing. +// +// Returns: Pointer to NDIS_PACKET if allocated, or NULL. +// +PNDIS_PACKET +GetIPPacket(void) +{ + PNDIS_PACKET Packet; + + +#ifdef VXD + Packet = PacketList; + if (Packet != (PNDIS_PACKET)NULL) { + PacketContext *PC; + + PC = (PacketContext *)Packet->ProtocolReserved; + PacketList = PC->pc_common.pc_link; + return Packet; +#else + PSINGLE_LIST_ENTRY Link; + PacketContext *PC; + struct PCCommon *Common; + + Link = ExInterlockedPopEntrySList( + &PacketList, + &HeaderLock + ); + if (Link != NULL) { + Common = STRUCT_OF(struct PCCommon, Link, pc_link); + PC = STRUCT_OF(PacketContext, Common, pc_common); + Packet = STRUCT_OF(NDIS_PACKET, PC, ProtocolReserved); + + return Packet; +#endif + + + } else { + // Couldn't get a packet. Try to grow the list. + Packet = GrowIPPacketList(); + } + + return Packet; +} + + +//** GetIPHdrBuffer - Get an IP header buffer. +// +// A routine to allocate an IP header buffer, with an NDIS buffer. +// +// Entry: Nothing. +// +// Returns: Pointer to NDIS_BUFFER if allocated, or NULL. +// +PNDIS_BUFFER +GetIPHdrBuffer(void) +{ + PNDIS_BUFFER Buffer; + +#ifdef VXD + Buffer = HdrBufList; + if (Buffer != NULL) { + + HdrBufList = NDIS_BUFFER_LINKAGE(Buffer); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; +#else + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &HdrBufList, + &HeaderLock + ); + if (BufferLink != NULL) { + Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + + return Buffer; + +#endif + + } else { + Buffer = GrowHdrBufList(); + } + + return Buffer; + +} + + +//** GetIPHeader - Get a header buffer and packet. +// +// Called when we need to get a header buffer and packet. We allocate both, +// and chain them together. +// +// Input: Pointer to where to store packet. +// +// Returns: Pointer to IP header. +// +IPHeader * +GetIPHeader(PNDIS_PACKET *PacketPtr) +{ + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + + + Packet = GetIPPacket(); + if (Packet != NULL) { + Buffer = GetIPHdrBuffer(); + if (Buffer != NULL) { + PacketContext *PC = (PacketContext *)Packet->ProtocolReserved; + + NdisChainBufferAtBack(Packet, Buffer); + *PacketPtr = Packet; + PC->pc_common.pc_flags |= PACKET_FLAG_IPHDR; + return (IPHeader *)NdisBufferVirtualAddress(Buffer); + + } else + FreeIPPacket(Packet); + } + return NULL; +} + + +//** ReferenceBuffer - Reference a buffer. +// +// Called when we need to update the count of a BufferReference strucutre, either +// by a positive or negative value. If the count goes to 0, we'll free the buffer +// reference and return success. Otherwise we'll return pending. +// +// Entry: BR - Pointer to buffer reference. +// Count - Amount to adjust refcount by. +// +// Returns: Success, or pending. +// +int +ReferenceBuffer(BufferReference *BR, int Count) +{ + CTELockHandle handle; + int NewCount; + + CTEGetLock(&BR->br_lock, &handle); + BR->br_refcount += Count; + NewCount = BR->br_refcount; + CTEFreeLock(&BR->br_lock, handle); + return NewCount; +} + +//* IPSendComplete - IP send complete handler. +// +// Called by the link layer when a send completes. We're given a pointer to a +// net structure, as well as the completing send packet and the final status of +// the send. +// +// Entry: Context - Context we gave to the link layer. +// Packet - Completing send packet. +// Status - Final status of send. +// +// Returns: Nothing. +// +void +IPSendComplete(void *Context, PNDIS_PACKET Packet, NDIS_STATUS Status) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + PNDIS_BUFFER Buffer; + void (*xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine. + void *UContext; // Upper layer context. + BufferReference *BufRef; // Buffer reference, if any. +#ifdef _PNP_POWER + Interface *IF; // The interface on which this + // completed. +#endif + + xmitdone = PContext->pc_pi->pi_xmitdone; // Copy useful information from packet. + UContext = PContext->pc_context; + BufRef = PContext->pc_br; +#ifdef _PNP_POWER + IF = PContext->pc_if; +#endif + + Buffer = FreeIPPacket(Packet); + if (BufRef == (BufferReference *)NULL) { +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + (*xmitdone)(UContext, Buffer); + } else { + if (!ReferenceBuffer(BufRef, -1)) { + Buffer = BufRef->br_buffer; +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + CTEFreeMem(BufRef); + (*xmitdone)(UContext, Buffer); + } else { +#ifdef _PNP_POWER + // We're not done with the send yet, so NULL the IF to + // prevent dereferencing it. + IF = NULL; +#endif + } + } + +#ifdef _PNP_POWER + // We're done with the packet now, we may need to dereference + // the interface. + if (IF == NULL) { + return; + } else { + DerefIF(IF); + } +#endif + +} + + +#ifndef NT + +//** xsum - Checksum a flat buffer. +// +// This is the lowest level checksum routine. It returns the uncomplemented +// checksum of a flat buffer. +// +// Entry: Buffer - Buffer to be checksummed. +// Size - Size in bytes of Buffer. +// +// Returns: The uncomplemented checksum of buffer. +// +ushort +xsum(void *Buffer, int Size) +{ + ushort UNALIGNED *Buffer1 = (ushort UNALIGNED *)Buffer; // Buffer expressed as shorts. + ulong csum = 0; + + while (Size > 1) { + csum += *Buffer1++; + Size -= sizeof(ushort); + } + + if (Size) + csum += *(uchar *)Buffer1; // For odd buffers, add in last byte. + + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + return (ushort)csum; +} + +#endif // NT + + +//** SendIPPacket - Send an IP packet. +// +// Called when we have a filled in IP packet we need to send. Basically, we +// compute the xsum and send the thing. +// +// Entry: IF - Interface to send it on. +// FirstHop - First hop address to send it to. +// Packet - Packet to be sent. +// Buffer - Buffer to be sent. +// Header - Pointer to IP Header of packet. +// Options - Pointer to option buffer. +// OptionLength - Length of options. +// +// Returns: IP_STATUS of attempt to send. +IP_STATUS +SendIPPacket(Interface *IF, IPAddr FirstHop, PNDIS_PACKET Packet, + PNDIS_BUFFER Buffer, IPHeader *Header, uchar *Options, uint OptionSize) +{ + ulong csum; + NDIS_STATUS Status; + + + csum = xsum(Header, sizeof(IPHeader)); + if (Options) { // We have options, oh boy. + PNDIS_BUFFER OptBuffer; + PacketContext *pc = (PacketContext *)Packet->ProtocolReserved; + NdisAllocateBuffer(&Status, &OptBuffer, BufferPool, Options, OptionSize); + if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get the needed + // option buffer. + CTEFreeMem(Options); + FreeIPPacket(Packet); + return IP_NO_RESOURCES; + } + pc->pc_common.pc_flags |= PACKET_FLAG_OPTIONS; + NdisChainBufferAtBack(Packet, OptBuffer); + csum += xsum(Options, OptionSize); + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + } + Header->iph_xsum = ~(ushort)csum; + NdisChainBufferAtBack(Packet,Buffer); + + Status = (*(IF->if_xmit))(IF->if_lcontext, Packet, FirstHop, NULL); + + if (Status == NDIS_STATUS_PENDING) + return IP_PENDING; + + // Status wasn't pending. Free the packet, and map the status. + FreeIPPacket(Packet); + if (Status == NDIS_STATUS_SUCCESS) + return IP_SUCCESS; + else + return IP_HW_ERROR; +} + +//* SendDHCPPacket - Send a broadcast for DHCP. +// +// Called when somebody is sending a broadcast packet with a NULL source +// address. We assume this means they're sending a DHCP packet. We loop +// through the NTE table, and when we find an entry that's not valid we +// send out the interface associated with that entry. +// +// Input: Dest - Destination of packet. +// Packet - Packet to be send. +// Buffer - Buffer chain to be sent. +// Header - Pointer to header buffer being sent. +// +// Return: Status of send attempt. +// +IP_STATUS +SendDHCPPacket(IPAddr Dest, PNDIS_PACKET Packet, PNDIS_BUFFER Buffer, + IPHeader *IPH) +{ + if (DHCPNTE != NULL && ((DHCPNTE->nte_flags & (NTE_VALID | NTE_ACTIVE)) + == NTE_ACTIVE)) { + // The DHCP NTE is currently invalid, and active. Send on that + // interface. + return SendIPPacket(DHCPNTE->nte_if, Dest, Packet, Buffer, IPH, NULL, + 0); + } + + // Didn't find an invalid NTE! Free the resources, and return the failure. + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + return IP_DEST_HOST_UNREACHABLE; + +} + + +// +// Macros needed by IpCopyBuffer +// +#ifdef VXD + +#define NdisBufferLength(Buffer) (Buffer)->Length +#define NdisBufferVirtualAddress(Buffer) (Buffer)->VirtualAddress + +#else // VXD +#ifdef NT + +#define NdisBufferLength(Buffer) MmGetMdlByteCount(Buffer) +#define NdisBufferVirtualAddress(Buffer) MmGetSystemAddressForMdl(Buffer) + +#else // NT + +#error Need appropriate NDIS macros here + +#endif NT +#endif // VXD +//* IPCopyBuffer - Copy an NDIS buffer chain at a specific offset. +// +// This is the IP version of the function NdisCopyBuffer, which didn't +// get done properly in NDIS3. We take in an NDIS buffer chain, an offset, +// and a length, and produce a buffer chain describing that subset of the +// input buffer chain. +// +// This routine is not particularly efficient. Since only IPFragment uses +// it currently, it might be better to just incorporate this functionality +// directly into IPFragment. +// +// Input: OriginalBuffer - Original buffer chain to copy from. +// Offset - Offset from start to dup. +// Length - Length in bytes to dup. +// +// Returns: Pointer to new chain if we can make one, NULL if we can't. +// +PNDIS_BUFFER +IPCopyBuffer(PNDIS_BUFFER OriginalBuffer,uint Offset, uint Length) +{ + + PNDIS_BUFFER CurrentBuffer; // Pointer to current buffer. + PNDIS_BUFFER *NewBuffer; // Pointer to pointer to current new buffer. + PNDIS_BUFFER FirstBuffer; // First buffer in new chain. + UINT CopyLength; // Length of current copy. + NDIS_STATUS NewStatus; // Status of NdisAllocateBuffer operation. + + // First skip over the number of buffers we need to to reach Offset. + CurrentBuffer = OriginalBuffer; + + while (Offset >= NdisBufferLength(CurrentBuffer)) { + Offset -= NdisBufferLength(CurrentBuffer); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + if (CurrentBuffer == (PNDIS_BUFFER)NULL) + return NULL; + } + + // Now CurrentBuffer is the buffer from which we start building the new chain, and + // Offset is the offset into CurrentBuffer from which to start. + FirstBuffer = NULL; + NewBuffer = &FirstBuffer; + + do { + + CopyLength = MIN( + Length, + NdisBufferLength(CurrentBuffer) - Offset + ); + NdisAllocateBuffer(&NewStatus, NewBuffer, BufferPool, + ((uchar *)NdisBufferVirtualAddress(CurrentBuffer)) + Offset, + CopyLength); + if (NewStatus != NDIS_STATUS_SUCCESS) + break; + + Offset = 0; // No offset from next buffer. + NewBuffer = &(NDIS_BUFFER_LINKAGE(*NewBuffer)); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + Length -= CopyLength; + } while (Length != 0 && CurrentBuffer != (PNDIS_BUFFER)NULL); + + if (Length == 0) { // We succeeded + return FirstBuffer; + } else { // We exited the loop because of an error. + + // We need to free any allocated buffers, and return. + CurrentBuffer = FirstBuffer; + while (CurrentBuffer != (PNDIS_BUFFER)NULL) { + PNDIS_BUFFER Temp = CurrentBuffer; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + NdisFreeBuffer(Temp); + } + return NULL; + } +} + +//** IPFragment - Fragment and send an IP datagram. +// +// Called when an outgoing datagram is larger than the local MTU, and needs to be +// fragmented. This is a somewhat complicated operation. The caller gives us a +// prebuilt IP header, packet, and options. We use the header and packet on +// the last fragment of the send, as the passed in header already has the more +// fragments bit set correctly for the last fragment. +// +// The basic idea is to figure out the maximum size which we can send as a multiple +// of 8. Then, while we can send a maximum size fragment we'll allocate a header, packet, +// etc. and send it. At the end we'll send the final fragment using the provided header +// and packet. +// +// Entry: DestIF - Outbound interface of datagram. +// MTU - MTU to use in transmitting. +// FirstHop - First (or next) hop for this datagram. +// Packet - Packet to be sent. +// Header - Prebuilt IP header. +// Buffer - Buffer chain for data to be sent. +// DataSize - Size in bytes of data. +// Options - Pointer to option buffer, if any. +// OptionSize - Size in bytes of option buffer. +// SentCount - Pointer to where to return pending send count (may be NULL). +// +// Returns: IP_STATUS of send. +// +IP_STATUS +IPFragment(Interface *DestIF, uint MTU, IPAddr FirstHop, + PNDIS_PACKET Packet, IPHeader *Header, PNDIS_BUFFER Buffer, uint DataSize, + uchar *Options, uint OptionSize, int *SentCount) +{ + BufferReference *BR; // Buffer reference we'll use. + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + PacketContext *CurrentContext; // Current Context in use. + uint MaxSend; // Maximum size (in bytes) we can send here. + uint PendingSends = 0; // Counter of how many pending sends we have. + PNDIS_BUFFER CurrentBuffer; // Current buffer to be sent. + PNDIS_PACKET CurrentPacket; // Current packet we're using. + IP_STATUS SendStatus; // Status of send command. + IPHeader *CurrentHeader; // Current header buffer we're using. + ushort Offset = 0; // Current offset into fragmented packet. + ushort StartOffset; // Starting offset of packet. + ushort RealOffset; // Offset of new fragment. + uint FragOptSize = 0; // Size (in bytes) of fragment options. + uchar FragmentOptions[MAX_OPT_SIZE]; // Master copy of options sent for fragments. + uchar Error = FALSE; // Set if we get an error in our main loop. + + MaxSend = (MTU - OptionSize) & ~7; // Determine max send size. + +#ifdef DEBUG + if (MaxSend >= DataSize) + DEBUGCHK; +#endif + + BR = PContext->pc_br; // Get the buffer reference we'll need. + +#ifdef DEBUG + if (!BR) + DEBUGCHK; +#endif + + if (Header->iph_offset & IP_DF_FLAG) { // Don't fragment flag set. + // Error out. + FreeIPPacket(Packet); + if (Options) + CTEFreeMem(Options); + if (SentCount == (int *)NULL) // No sent count is to be + // returned. + CTEFreeMem(BR); + IPSInfo.ipsi_fragfails++; + return IP_PACKET_TOO_BIG; + } + + StartOffset = Header->iph_offset & IP_OFFSET_MASK; + StartOffset = net_short(StartOffset) * 8; + + // If we have any options, copy the ones that need to be copied, and figure + // out the size of these new copied options. + + if (Options != (uchar *)NULL) { // We have options. + uchar *TempOptions = Options; + const uchar *EndOptions = (const uchar *)(Options+OptionSize); + + // Copy the options into the fragment options buffer. + CTEMemSet(FragmentOptions, IP_OPT_EOL, MAX_OPT_SIZE); + while ((TempOptions[IP_OPT_TYPE] != IP_OPT_EOL) && + (TempOptions < EndOptions)) { + if (TempOptions[IP_OPT_TYPE] & IP_OPT_COPIED) { // This option needs + // to be copied. + uint TempOptSize; + + TempOptSize = TempOptions[IP_OPT_LENGTH]; + CTEMemCopy(&FragmentOptions[FragOptSize], TempOptions, + TempOptSize); + FragOptSize += TempOptSize; + TempOptions += TempOptSize; + } else { // A non-copied option, just + // skip over it. + if (TempOptions[IP_OPT_TYPE] == IP_OPT_NOP) + TempOptions++; + else + TempOptions += TempOptions[IP_OPT_LENGTH]; + } + } + // Round the copied size up to a multiple of 4. + FragOptSize = ((FragOptSize & 3) ? ((FragOptSize & ~3) + 4) : FragOptSize); + } + + PContext->pc_common.pc_flags |= PACKET_FLAG_IPBUF; + + // Now, while we can build maximum size fragments, do so. + do { + if ((CurrentHeader = GetIPHeader(&CurrentPacket)) == (IPHeader *)NULL) { + // Couldn't get a buffer. Break out, since no point in sending others. + Error = TRUE; + break; + } + + // Copy the buffer into a new one, if we can. + CurrentBuffer = IPCopyBuffer(Buffer, Offset, MaxSend); + if (CurrentBuffer == NULL) { // No buffer, free resources and + // break. + FreeIPPacket(CurrentPacket); + Error = TRUE; + break; + } + + // Options for this send are set up when we get here, either from the + // entry from the loop, or from the allocation below. + + // We have all the pieces we need. Put the packet together and send it. + CurrentContext = (PacketContext *)CurrentPacket->ProtocolReserved; + *CurrentContext = *PContext; + *CurrentHeader = *Header; + CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_FW; + CurrentHeader->iph_verlen = IP_VERSION + + ((OptionSize + sizeof(IPHeader)) >> 2); + CurrentHeader->iph_length = net_short(MaxSend+OptionSize+sizeof(IPHeader)); + RealOffset = (StartOffset + Offset) >> 3; + CurrentHeader->iph_offset = net_short(RealOffset) | IP_MF_FLAG; + + SendStatus = SendIPPacket(DestIF, FirstHop, CurrentPacket, + CurrentBuffer, CurrentHeader, Options, OptionSize); + if (SendStatus == IP_PENDING) + PendingSends++; + + IPSInfo.ipsi_fragcreates++; + Offset += MaxSend; + DataSize -= MaxSend; + + // If we have any fragmented options, set up to use them next time. + if (FragOptSize) { + Options = CTEAllocMem(OptionSize = FragOptSize); + if (Options == (uchar *)NULL) { // Can't get an option + // buffer. + Error = TRUE; + break; + } + CTEMemCopy(Options, FragmentOptions, OptionSize); + } else { + Options = (uchar *)NULL; + OptionSize = 0; + } + } while (DataSize > MaxSend); + + // We've sent all of the previous fragments, now send the last one. We already + // have the packet and header buffer, as well as options if there are any - + // we need to copy the appropriate data. + if (!Error) { // Everything went OK above. + CurrentBuffer = IPCopyBuffer(Buffer, Offset, DataSize); + if (CurrentBuffer == NULL) { // No buffer, free resources + // and stop. + if (Options) + CTEFreeMem(Options); // Free the option buffer + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + } else { // Everything's OK, send it. + Header->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2); + Header->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader)); + RealOffset = (StartOffset + Offset) >> 3; + Header->iph_offset = net_short(RealOffset) | + (Header->iph_offset & IP_MF_FLAG); + SendStatus = SendIPPacket(DestIF, FirstHop, Packet, + CurrentBuffer, Header, Options, OptionSize); + if (SendStatus == IP_PENDING) + PendingSends++; + IPSInfo.ipsi_fragcreates++; + IPSInfo.ipsi_fragoks++; + } + } else { // We had some sort of error. + // Free resources. + FreeIPPacket(Packet); + if (Options) + CTEFreeMem(Options); + IPSInfo.ipsi_outdiscards++; + } + + + // Now, figure out what error code to return and whether or not we need to + // free the BufferReference. + + if (SentCount == (int *)NULL) { // No sent count is to be + // returned. + if (!ReferenceBuffer(BR, PendingSends)) { + CTEFreeMem(BR); + return IP_SUCCESS; + } + return IP_PENDING; + } else + *SentCount += PendingSends; + + return IP_PENDING; + +} + +//* UpdateRouteOption - Update a SR or RR options. +// +// Called by UpdateOptions when it needs to update a route option. +// +// Input: RTOption - Pointer to route option to be updated. +// Address - Address to update with. +// +// Returns: TRUE if we updated, FALSE if we didn't. +// +uchar +UpdateRouteOption(uchar *RTOption, IPAddr Address) +{ + uchar Pointer; // Pointer value of option. + + Pointer = RTOption[IP_OPT_PTR] - 1; + if (Pointer < RTOption[IP_OPT_LENGTH]) { + if ((RTOption[IP_OPT_LENGTH] - Pointer) < sizeof(IPAddr)) { + return FALSE; + } + *(IPAddr UNALIGNED *)&RTOption[Pointer] = Address; + RTOption[IP_OPT_PTR] += sizeof(IPAddr); + } + + return TRUE; + +} + +//* UpdateOptions - Update an options buffer. +// +// Called when we need to update an options buffer outgoing. We stamp the indicated +// options with our local address. +// +// Input: Options - Pointer to options buffer to be updated. +// Index - Pointer to information about which ones to update. +// Address - Local address with which to update the options. +// +// Returns: Index of option causing the error, or MAX_OPT_SIZE if all goes well. +// +uchar +UpdateOptions(uchar *Options, OptIndex *Index, IPAddr Address) +{ + uchar *LocalOption; + uchar LocalIndex; + + // If we have both options and an index, update the options. + if (Options != (uchar *)NULL && Index != (OptIndex *)NULL) { + + // If we have a source route to update, update it. If this fails return the index + // of the source route. + LocalIndex = Index->oi_srindex; + if (LocalIndex != MAX_OPT_SIZE) + if (!UpdateRouteOption(Options+LocalIndex, Address)) + return LocalIndex; + + // Do the same thing for any record route option. + LocalIndex = Index->oi_rrindex; + if (LocalIndex != MAX_OPT_SIZE) + if (!UpdateRouteOption(Options+LocalIndex, Address)) + return LocalIndex; + + // Now handle timestamp. + if ((LocalIndex = Index->oi_tsindex) != MAX_OPT_SIZE) { + uchar Flags, Length, Pointer; + + LocalOption = Options + LocalIndex; + Pointer = LocalOption[IP_OPT_PTR] - 1; + Flags = LocalOption[IP_TS_OVFLAGS] & IP_TS_FLMASK; + + // If we have room in the option, update it. + if (Pointer < (Length = LocalOption[IP_OPT_LENGTH])) { + ulong Now; + ulong UNALIGNED *TSPtr; + + // Get the current time as milliseconds from midnight GMT, mod the number + // of milliseconds in 24 hours. + Now = ((TimeStamp + CTESystemUpTime()) | TSFlag) % (24*3600*1000); + Now = net_long(Now); + TSPtr = (ulong UNALIGNED *)&LocalOption[Pointer]; + + switch (Flags) { + + // Just record the TS. If there is some room but not enough for an IP + // address we have an error. + case TS_REC_TS: + if ((Length - Pointer) < sizeof(IPAddr)) + return LocalIndex; // Error - not enough room. + *TSPtr = Now; + LocalOption[IP_OPT_PTR] += sizeof(ulong); + break; + + // Record only matching addresses. + case TS_REC_SPEC: + // If we're not the specified address, break out, else fall through + // to the record address case. + if (*(IPAddr UNALIGNED *)TSPtr != Address) + break; + + // Record an address and timestamp pair. If there is some room + // but not enough for the address/timestamp pait, we have an error, + // so bail out. + case TS_REC_ADDR: + if ((Length - Pointer) < (sizeof(IPAddr) + sizeof(ulong))) + return LocalIndex; // Not enough room. + *(IPAddr UNALIGNED *)TSPtr = Address; // Store the address. + TSPtr++; // Update to where to put TS. + *TSPtr = Now; // Store TS + LocalOption[IP_OPT_PTR] += (sizeof(ulong) + sizeof(IPAddr)); + break; + default: // Unknown flag type. Just ignore it. + break; + } + } else { // Have overflow. + // We have an overflow. If the overflow field isn't maxed, increment it. If + // it is maxed we have an error. + if ((LocalOption[IP_TS_OVFLAGS] & IP_TS_OVMASK) != IP_TS_MAXOV) // Not maxed. + LocalOption[IP_TS_OVFLAGS] += IP_TS_INC; // So increment it. + else + return LocalIndex; // Would have overflowed. + } + } + } + return MAX_OPT_SIZE; +} + + +typedef struct { + IPAddr bsl_addr; + Interface *bsl_if; + uint bsl_mtu; + ushort bsl_flags; +} BCastSendList; + +//** SendIPBcast - Send a local BCast IP packet. +// +// This routine is called when we need to send a bcast packet. This may +// involve sending on multiple interfaces. We figure out which interfaces +// to send on, then loop through sending on them. +// +// Some care is needed to avoid sending the packet onto the same physical media +// multiple times. What we do is loop through the NTE table, deciding in we +// should send on the interface. As we go through we build up a list of +// interfaces to send on. Then we loop through this list, sending on each +// interface. This is a little cumbersome, but allows us to localize the +// decision on where to send datagrams into one spot. If SendOnSource is FALSE +// coming in we assume we've already sent on the specified source NTE and +// initialize data structures accordingly. This feature is used in routing +// datagrams. +// +// Entry: SrcNTE - NTE for source of send (unused if SendOnSource == TRUE). +// Destination - Destination address +// Packet - Prebuilt packet to broadcast. +// IPH - Pointer to header buffer +// Buffer - Buffer of data to be sent. +// DataSize - Size of data to be sent. +// Options - Pointer to options buffer. +// OptionSize - Size in bytes of options. +// SendOnSource - Indicator of whether or not this should be sent on the source net. +// Index - Pointer to opt index array; may be NULL; +// +// Returns: Status of attempt to send. +// +IP_STATUS +SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination, PNDIS_PACKET Packet, + IPHeader *IPH, PNDIS_BUFFER Buffer, uint DataSize, uchar *Options, + uint OptionSize, uchar SendOnSource, OptIndex *Index) +{ + BufferReference *BR; // Buffer reference to use for this + // buffer. + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + NetTableEntry *TempNTE; + uint i, j; + uint NeedFragment; // TRUE if we think we'll need to + // fragment. + int Sent = 0; // Count of how many we've sent. + IP_STATUS Status; + uchar *NewOptions; // Options we'll use on each send. + IPHeader *NewHeader; + PNDIS_BUFFER NewUserBuffer; + PNDIS_PACKET NewPacket; + BCastSendList *SendList; + uint NetsToSend; + IPAddr SrcAddr; + Interface *SrcIF; + IPHeader *Temp; + FORWARD_ACTION Action; + + + SendList = CTEAllocMem(sizeof(BCastSendList) * NumNTE); + + if (SendList == NULL) { + return(IP_NO_RESOURCES); + } + + CTEMemSet(SendList, 0, sizeof(BCastSendList) * NumNTE); + + // If SendOnSource, initalize SrcAddr and SrcIF to be non-matching. + // Otherwise initialize them to the masked source address and source + // interface. + if (SendOnSource) { + SrcAddr = NULL_IP_ADDR; + SrcIF = NULL; + } else { + CTEAssert(SrcNTE != NULL); + SrcAddr = (SrcNTE->nte_addr & SrcNTE->nte_mask); + SrcIF = SrcNTE->nte_if; + } + + + NeedFragment = FALSE; + // Loop through the NTE table, making a list of interfaces and + // corresponding addresses to send on. + for (NetsToSend = 0, TempNTE = NetTableList; TempNTE != NULL; + TempNTE = TempNTE->nte_next) { + IPAddr TempAddr; + + // Don't send through invalid or the loopback NTE. + if (!(TempNTE->nte_flags & NTE_VALID) || TempNTE == LoopNTE) + continue; + + TempAddr = TempNTE->nte_addr & TempNTE->nte_mask; + + // If he matches the source address or SrcIF, skip him. + if (IP_ADDR_EQUAL(TempAddr, SrcAddr) || TempNTE->nte_if == SrcIF) + continue; + + // If the destination isn't a broadcast on this NTE, skip him. + if (!IS_BCAST_DEST(IsBCastOnNTE(Destination, TempNTE))) + continue; + + // if this NTE is P2P then always add him to bcast list. + if ((TempNTE->nte_if)->if_flags & IF_FLAGS_P2P) { + j = NetsToSend ; + } else { + // Go through the list we've already build, looking for a match. + for (j = 0; j < NetsToSend; j++) { + + // if P2P NTE then skip it - we want to send bcasts to all P2P interfaces in + // addition to 1 non P2P interface even if they are on the same subnet. + if ((SendList[j].bsl_if)->if_flags & IF_FLAGS_P2P) + continue ; + + if (IP_ADDR_EQUAL(SendList[j].bsl_addr & TempNTE->nte_mask, TempAddr) + || SendList[j].bsl_if == TempNTE->nte_if) { + + // He matches this send list element. Shrink the MSS if + // we need to, and then break out. + SendList[j].bsl_mtu = MIN(SendList[j].bsl_mtu, TempNTE->nte_mss); + if ((DataSize + OptionSize) > SendList[j].bsl_mtu) + NeedFragment = TRUE; + break; + } + } + } + + if (j == NetsToSend) { + // This is a new one. Fill him in, and bump NetsToSend. + + SendList[j].bsl_addr = TempNTE->nte_addr; + SendList[j].bsl_if = TempNTE->nte_if; + SendList[j].bsl_mtu = TempNTE->nte_mss; + SendList[j].bsl_flags = TempNTE->nte_flags; + if ((DataSize + OptionSize) > SendList[j].bsl_mtu) + NeedFragment = TRUE; + NetsToSend++; + } + + } + + if (NetsToSend == 0) { + CTEFreeMem(SendList); + return IP_SUCCESS; // Nothing to send on. + } + + // OK, we've got the list. If we've got more than one interface to send + // on or we need to fragment, get a BufferReference. + if (NetsToSend > 1 || NeedFragment) { + if ((BR = CTEAllocMem(sizeof(BufferReference))) == + (BufferReference *)NULL) { + CTEFreeMem(SendList); + return IP_NO_RESOURCES; + } + + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + PContext->pc_br = BR; + } else { + BR = NULL; + PContext->pc_br = NULL; + } + + // + // We need to pass up the options and IP hdr in a contiguous buffer. + // Allocate the buffer once and re-use later. + // + if (ForwardFilterPtr != NULL) { + if (Options == NULL) { +#if FWD_DBG + DbgPrint("Options==NULL\n"); +#endif + Temp = IPH; + } else { + Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize); + if (Temp == NULL) { + CTEFreeMem(SendList); + return IP_NO_RESOURCES; + } + + *Temp = *IPH; +#if FWD_DBG + DbgPrint("Options!=NULL : alloced temp @ %lx\n", Temp); +#endif + + // + // done later... + // CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize); + } + } + + // Now, loop through the list. For each entry, send. + + for (i = 0; i < NetsToSend; i++) { + + // For all nets except the last one we're going to send on we need + // to make a copy of the header, packet, buffers, and any options. + // On the last net we'll use the user provided information. + + if (i != (NetsToSend - 1)) { + if ((NewHeader = GetIPHeader(&NewPacket)) == (IPHeader *)NULL) { + IPSInfo.ipsi_outdiscards++; + continue; // Couldn't get a header, skip this + // send. + } + + NewUserBuffer = IPCopyBuffer(Buffer, 0, DataSize); + if (NewUserBuffer == NULL) { // Couldn't get user buffer + // copied. + FreeIPPacket(NewPacket); + IPSInfo.ipsi_outdiscards++; + continue; + } + + *(PacketContext *)NewPacket->ProtocolReserved = *PContext; + *NewHeader = *IPH; + (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags |= PACKET_FLAG_IPBUF; + (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_FW; + if (Options) { + // We have options, make a copy. + if ((NewOptions = CTEAllocMem(OptionSize)) == (uchar *)NULL) { + FreeIPBufferChain(NewUserBuffer); + FreeIPPacket(NewPacket); + IPSInfo.ipsi_outdiscards++; + continue; + } + CTEMemCopy(NewOptions, Options, OptionSize); + } + else { + NewOptions = NULL; + } + } else { + NewHeader = IPH; + NewPacket = Packet; + NewOptions = Options; + NewUserBuffer = Buffer; + } + + UpdateOptions(NewOptions, Index, SendList[i].bsl_addr); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + if (ForwardFilterPtr != NULL) { + // + // Copy over the options. + // + if (NewOptions) { + CTEMemCopy((uchar *)(Temp + 1), NewOptions, OptionSize); + } + + Action = (*ForwardFilterPtr)(Temp, + NdisBufferVirtualAddress(NewUserBuffer), + NdisBufferLength(NewUserBuffer), + NULL, SendList[i].bsl_if->if_filtercontext); + +#if FWD_DBG + DbgPrint("ForwardFilterPtr: %lx, FORWARD is %lx\n", Action, FORWARD); +#endif + + if (Action != FORWARD) { + if (i != (NetsToSend - 1)) { + FreeIPBufferChain(NewUserBuffer); + if (NewOptions) { + CTEFreeMem(NewOptions); + } + } + continue; + } + } + + if ((DataSize + OptionSize) > SendList[i].bsl_mtu) {// This is too big + // Don't need to update Sent when fragmenting, as IPFragment + // will update the br_refcount field itself. It will also free + // the option buffer. + Status = IPFragment(SendList[i].bsl_if, SendList[i].bsl_mtu, + Destination, NewPacket, NewHeader,NewUserBuffer, DataSize, + NewOptions, OptionSize, &Sent); + + // IPFragment is done with the descriptor chain, so if this is + // a locally allocated chain free it now. + if (i != (NetsToSend - 1)) + FreeIPBufferChain(NewUserBuffer); + } + else { + Status = SendIPPacket(SendList[i].bsl_if, Destination, NewPacket, + NewUserBuffer, NewHeader, NewOptions, OptionSize); + if (Status == IP_PENDING) + Sent++; + } + } + + if (ForwardFilterPtr && Options) { + CTEFreeMem(Temp); + } + + // Alright, we've sent everything we need to. We'll adjust the reference count + // by the number we've sent. IPFragment may also have put some references + // on it. If the reference count goes to 0, we're done and we'll free the + // BufferReference structure. + + if (BR != NULL) { + if (!ReferenceBuffer(BR, Sent)) { + CTEFreeMem(SendList); + CTEFreeMem(BR); // Reference is 0, free the BR structure. + return IP_SUCCESS; + } else { + CTEFreeMem(SendList); + return IP_PENDING; + } + } else { + // Had only one I/F to send on. Just return the status. + CTEFreeMem(SendList); + return Status; + } + +} + +//** IPTransmit - Transmit a packet. +// +// This is the main transmit routine called by the upper layer. Conceptually, +// we process any options, look up the route to the destination, fragment the +// packet if needed, and send it. In reality, we use an RCE to cache the best +// route, and we have special case code here for dealing with the common +// case of no options, with everything fitting into one buffer. +// +// Entry: Context - Pointer to ProtInfo struc for protocol. +// SendContext - User provided send context, passed back on send cmplt. +// Protocol - Protocol field for packet. +// Buffer - NDIS_BUFFER chain of data to be sent. +// DataSize - Size in bytes of data to be sent. +// OptInfo - Pointer to optinfo structure. +// Dest - Destination to send to. +// Source - Source address to use. +// RCE - Pointer to an RCE structure that caches info. about path. +// +// Returns: Status of transmit command. +// +IP_STATUS +IPTransmit(void *Context, void *SendContext, PNDIS_BUFFER Buffer, uint DataSize, + IPAddr Dest, IPAddr Source, IPOptInfo *OptInfo, RouteCacheEntry *RCE, + uchar Protocol) +{ + ProtInfo *PInfo = (ProtInfo *)Context; + PacketContext *pc; + Interface *DestIF; // Outgoing interface to use. + IPAddr FirstHop; // First hop address of + // destination. + uint MTU; // MTU of route. + NDIS_STATUS Status; + IPHeader *IPH; + PNDIS_PACKET Packet; + PNDIS_BUFFER HeaderBuffer; + CTELockHandle LockHandle; + uchar *Options; + uint OptionSize; + BufferReference *BR; + RouteTableEntry *RTE; + uchar DType; + IP_STATUS SendStatus; +#ifdef _PNP_POWER + Interface *RoutedIF; +#endif + + IPSInfo.ipsi_outrequests++; + + // Allocate a packet that we need for all cases, and fill + // in the common stuff. If everything goes well, we'll send it + // here. Otherwise we'll break out into special case code for + // broadcasts, fragments, etc. + if ((Packet = GetIPPacket()) != (PNDIS_PACKET)NULL) { // Got a packet. + pc = (PacketContext *)Packet->ProtocolReserved; + pc->pc_br = (BufferReference *)NULL; + pc->pc_pi = PInfo; + pc->pc_context = SendContext; +#ifdef _PNP_POWER + CTEAssert(pc->pc_if == NULL); +#endif + + // Make sure that we have an RCE, that it's valid, etc. + + if (RCE != NULL) { + // We have an RCE. Make sure it's valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RTE is valid. + CTEInterlockedIncrementLong(&RCE->rce_usecnt); + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + + CTEFreeLock(&RCE->rce_lock, LockHandle); + + // Check that we have no options, this isn't a broadcast, and + // that everything will fit into one link level MTU. If this + // is the case, we'll send it in a hurry. + if (OptInfo->ioi_options == (uchar *)NULL) { + if (RCE->rce_dtype != DEST_BCAST) { + if (DataSize <= MTU) { + + + NdisBufferLength(Buffer) += sizeof(IPHeader); + NdisChainBufferAtBack(Packet, Buffer); + IPH = (IPHeader *)NdisBufferVirtualAddress(Buffer); + + IPH->iph_protocol = Protocol; + IPH->iph_xsum = 0; + IPH->iph_dest = Dest; + IPH->iph_src = Source; + IPH->iph_ttl = OptInfo->ioi_ttl; + IPH->iph_tos = OptInfo->ioi_tos; + IPH->iph_offset = + net_short(((OptInfo->ioi_flags & IP_FLAG_DF) + << 13)); + IPH->iph_id = + (ushort)CTEInterlockedExchangeAdd(&IPID, 1); + IPH->iph_verlen = DEFAULT_VERLEN; + IPH->iph_length = net_short(DataSize+sizeof(IPHeader)); + IPH->iph_xsum = ~xsum(IPH, sizeof(IPHeader)); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + + if (ForwardFilterPtr == NULL) { + Status = (*(DestIF->if_xmit))(DestIF->if_lcontext, + Packet, FirstHop, RCE); + + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + + if (Status != NDIS_STATUS_PENDING) { + FreeIPPacket(Packet); + return IP_SUCCESS; // BUGBUG - should map error + // code. + } + return IP_PENDING; + + } else { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(IPH, + (uchar *)(IPH + 1), + NdisBufferLength(Buffer) - + sizeof(IPHeader), + NULL, DestIF->if_filtercontext); + + if (Action == FORWARD) { + Status = (*(DestIF->if_xmit))( + DestIF->if_lcontext, + Packet, FirstHop, RCE); + } else { + Status = NDIS_STATUS_SUCCESS; + IPSInfo.ipsi_outdiscards++; + } + + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + + if (Status != NDIS_STATUS_PENDING) { + FreeIPPacket(Packet); + return IP_SUCCESS; // BUGBUG - should map error + // code. + } + return IP_PENDING; + } + } + } + } + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + DType = RCE->rce_dtype; + } else { + // We have an RCE, but there is no RTE for it. Call the + // routing code to fix this. + CTEFreeLock(&RCE->rce_lock, LockHandle); + if (!AttachRCEToRTE(RCE, PInfo->pi_protocol, + (uchar *)NdisBufferVirtualAddress(Buffer) + sizeof(IPHeader), + NdisBufferLength(Buffer))) { + IPSInfo.ipsi_outnoroutes++; + FreeIPPacket(Packet); + return IP_DEST_HOST_UNREACHABLE; + } + + // See if the RCE is now valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RCE is now valid, so use his info. + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + DType = RCE->rce_dtype; + } else + FirstHop = NULL_IP_ADDR; + CTEFreeLock(&RCE->rce_lock, LockHandle); + } + } else { + // We had no RCE, so we'll have to look it up the hard way. + FirstHop = NULL_IP_ADDR; + } + + // We bailed out of the fast path for some reason. Allocate a header + // buffer, and copy the data in the first buffer forward. Then figure + // out why we're off the fast path, and deal with it. If we don't have + // the next hop info, look it up now. + + HeaderBuffer = GetIPHdrBuffer(); + if (HeaderBuffer == NULL) { + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } else { + uchar *Temp1, *Temp2; + + // Got a buffer, copy the upper layer data forward. + + Temp1 = (uchar *)NdisBufferVirtualAddress(Buffer); + Temp2 = Temp1 + sizeof(IPHeader); + CTEMemCopy(Temp1, Temp2, NdisBufferLength(Buffer)); + } + + NdisChainBufferAtBack(Packet, HeaderBuffer); + + IPH = (IPHeader *)NdisBufferVirtualAddress(HeaderBuffer); + IPH->iph_protocol = Protocol; + IPH->iph_xsum = 0; + IPH->iph_src = Source; + IPH->iph_ttl = OptInfo->ioi_ttl; + IPH->iph_tos = OptInfo->ioi_tos; + IPH->iph_offset = net_short(((OptInfo->ioi_flags & IP_FLAG_DF) << 13)); + IPH->iph_id = (ushort)CTEInterlockedExchangeAdd(&IPID, 1); + pc = (PacketContext *)Packet->ProtocolReserved; + pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR; + + if (IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) { + IPH->iph_dest = Dest; + } + else { + // + // We have a source route, so we need to redo the + // destination and first hop information. + // + Dest = OptInfo->ioi_addr; + IPH->iph_dest = Dest; + + if (RCE != NULL) { + // We have an RCE. Make sure it's valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RTE is valid. + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + } + else { + FirstHop = NULL_IP_ADDR; + } + + CTEFreeLock(&RCE->rce_lock, LockHandle); + } + } + + if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR)) { + DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU, + PInfo->pi_protocol, (uchar *)NdisBufferVirtualAddress(Buffer), + NdisBufferLength(Buffer)); +#ifdef _PNP_POWER + pc->pc_if = DestIF; + RoutedIF = DestIF; +#endif + if (DestIF == NULL) { + // Lookup failed. Return an error. + FreeIPPacket(Packet); + IPSInfo.ipsi_outnoroutes++; + return IP_DEST_HOST_UNREACHABLE; + } + + DType = GetAddrType(Dest); +#ifdef DEBUG + if (DType == DEST_INVALID) + DEBUGCHK; +#endif + } else { +#ifdef _PNP_POWER + RoutedIF = NULL; +#endif + } + + // See if we have any options. If we do, copy them now. + if (OptInfo->ioi_options != NULL) { + // If we have a SSRR, make sure that we're sending straight to the + // first hop. + if (OptInfo->ioi_flags & IP_FLAG_SSRR) { + if (!IP_ADDR_EQUAL(Dest, FirstHop)) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + IPSInfo.ipsi_outnoroutes++; + return IP_DEST_HOST_UNREACHABLE; + } + } + Options = CTEAllocMem(OptionSize = OptInfo->ioi_optlength); + if (Options == (uchar *)NULL) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + CTEMemCopy(Options, OptInfo->ioi_options, OptionSize); + } else { + Options = (uchar *)NULL; + OptionSize = 0; + } + + // The options have been taken care of. Now see if it's some sort + // of broadcast. + IPH->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2); + IPH->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader)); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + + if (ForwardFilterPtr != NULL) { + IPHeader *Temp; + FORWARD_ACTION Action; + + if (Options == NULL) { + Temp = IPH; + } else { + Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize); + if (Temp == NULL) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + CTEFreeMem(Options); + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + + *Temp = *IPH; + CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize); + } + + Action = (*ForwardFilterPtr)(Temp, + NdisBufferVirtualAddress(Buffer), + NdisBufferLength(Buffer), + NULL, DestIF->if_filtercontext); + + if (Options != NULL) { + CTEFreeMem(Temp); + } + + if (Action != FORWARD) { + // + // If this is a bcast pkt, dont fail the send here since we might send this + // pkt over some other NTE; instead, let SendIPBCast deal with the Filtering + // for broadcast pkts. + // + // NOTE: We shd actually not call into ForwardFilterPtr here at all since we + // deal with it in BCast, but we do so in order to avoid a check above and hence + // take a double call hit in the bcast case. + // + if (DType != DEST_BCAST) { + + if (Options) + CTEFreeMem(Options); + FreeIPPacket(Packet); + +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + IPSInfo.ipsi_outdiscards++; + return IP_DEST_HOST_UNREACHABLE; + } +#if FWD_DBG + else { + DbgPrint("IPTransmit: ignoring return %lx\n", Action); + } +#endif + + } + } + + // If this is a broadcast address, call our broadcast send handler + // to deal with this. The broadcast address handler will free the + // option buffer for us, if needed. Otherwise if it's a fragment, call + // the fragmentation handler. + if (DType == DEST_BCAST) { + if (IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) { + SendStatus = SendDHCPPacket(Dest, Packet, Buffer, IPH); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } else { + SendStatus= SendIPBCast(NULL, Dest, Packet, IPH, Buffer, DataSize, + Options, OptionSize, TRUE, NULL); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + } + + // Not a broadcast. If it needs to be fragmented, call our + // fragmenter to do it. The fragmentation routine needs a + // BufferReference structure, so we'll need one of those first. + if ((DataSize + OptionSize) > MTU) { + BR = CTEAllocMem(sizeof(BufferReference)); + if (BR == (BufferReference *)NULL) { + // Couldn't get a BufferReference + if (Options) + CTEFreeMem(Options); + FreeIPPacket(Packet); + +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + pc->pc_br = BR; + SendStatus = IPFragment(DestIF, MTU, FirstHop, Packet, IPH, Buffer, + DataSize, Options, OptionSize, (int *)NULL); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + + // If we've reached here, we aren't sending a broadcast and don't need to + // fragment anything. Presumably we got here because we have options. + // In any case, we're ready now. + + SendStatus = SendIPPacket(DestIF, FirstHop, Packet, Buffer, IPH, Options, + OptionSize); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + + // Couldn't get a buffer. Return 'no resources' + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; +} + + + diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.h b/private/ntos/tdi/tcpip/ip/ipxmit.h new file mode 100644 index 000000000..65842967e --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipxmit.h @@ -0,0 +1,34 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPXMIT.H - IP transmit definitions. +// +// This file contains all of the definitions for the transmit code visible +// to modules outside IPXMIT.C +extern IP_STATUS SendIPPacket(Interface *IF, IPAddr FirstHop, + PNDIS_PACKET Packet, PNDIS_BUFFER Buffer, + IPHeader *Header, uchar *Options, + uint OptionSize); +extern IP_STATUS IPFragment(Interface *DestIF, uint MTU, + IPAddr FirstHop, PNDIS_PACKET Packet, + IPHeader *Header, PNDIS_BUFFER Buffer, + uint DataSize, uchar *Options, + uint OptionSize, int *SentCount); +extern uchar UpdateOptions(uchar *Options, OptIndex *Index, + IPAddr Address); +extern IP_STATUS SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination, + PNDIS_PACKET Packet, IPHeader *IPH, + PNDIS_BUFFER Buffer, uint DataSize, + uchar *Options, uint OptionSize, + uchar SendOnSource, OptIndex *Index); +extern IP_STATUS IPTransmit(void *Context, void *SendContext, + PNDIS_BUFFER Buffer, uint DataSize, + IPAddr Dest, IPAddr Source, + IPOptInfo *OptInfo, RouteCacheEntry *RCE, + uchar Protocol); + + + diff --git a/private/ntos/tdi/tcpip/ip/mp/makefile b/private/ntos/tdi/tcpip/ip/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/ip/mp/sources b/private/ntos/tdi/tcpip/ip/mp/sources new file mode 100644 index 000000000..301687c9a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/mp/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +!include ..\sources.inc diff --git a/private/ntos/tdi/tcpip/ip/ntip.c b/private/ntos/tdi/tcpip/ip/ntip.c new file mode 100644 index 000000000..040f025e3 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntip.c @@ -0,0 +1,3361 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntip.c + +Abstract: + + NT specific routines for loading and configuring the IP driver. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#define _CTYPE_DISABLE_MACROS + +#include +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include +#include +#include + +// +// Debugging macros +// +#if DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#else // DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#endif // DBG + + +// +// definitions needed by inet_addr. +// +#define INADDR_NONE 0xffffffff +#define INADDR_ANY 0 +#define htonl(x) net_long(x) + +// +// Other local constants +// +#define WORK_BUFFER_SIZE 256 + +// +// Configuration defaults +// +#define DEFAULT_IGMP_LEVEL 2 +#define DEFAULT_IP_NETS 8 + + +// +// Local types +// +typedef struct _PerNetConfigInfo { + uint UseZeroBroadcast; + uint Mtu; + uint NumberOfGateways; + uint MaxForwardPending; // max routing packets pending +} PER_NET_CONFIG_INFO, *PPER_NET_CONFIG_INFO; + + +// +// Global variables. +// +PDRIVER_OBJECT IPDriverObject; +PDEVICE_OBJECT IPDeviceObject; +IPConfigInfo *IPConfiguration; +uint ArpUseEtherSnap = FALSE; +uint ArpAlwaysSourceRoute = FALSE; +uint IPAlwaysSourceRoute = TRUE; +uint ArpCacheLife = DEFAULT_ARP_CACHE_LIFE; +PWCHAR TempAdapterName; + +#ifndef _PNP_POWER + +NameMapping *AdptNameTable; +DriverRegMapping *DriverNameTable; +uint NumRegDrivers = 0; +uint NetConfigSize = DEFAULT_IP_NETS; + +#endif // _PNP_POWER + +// Used in the conversion of 100ns times to milliseconds. +static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; + + +// +// External variables +// +extern LIST_ENTRY PendingEchoList; // def needed for initialization +extern LIST_ENTRY PendingIPSetNTEAddrList; // def needed for initialization +extern IPSNMPInfo IPSInfo; +EXTERNAL_LOCK(RouteTableLock) + +// +// Macros +// + +//++ +// +// LARGE_INTEGER +// CTEConvertMillisecondsTo100ns( +// IN LARGE_INTEGER MsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// MsTime - Time in milliseconds. +// +// Return Value: +// +// Time in hundreds of nanoseconds. +// +//-- + +#define CTEConvertMillisecondsTo100ns(MsTime) \ + RtlExtendedIntegerMultiply(MsTime, 10000) + + +//++ +// +// LARGE_INTEGER +// CTEConvert100nsToMilliseconds( +// IN LARGE_INTEGER HnsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// HnsTime - Time in hundreds of nanoseconds. +// +// Return Value: +// +// Time in milliseconds. +// +//-- + +#define SHIFT10000 13 +extern LARGE_INTEGER Magic10000; + +#define CTEConvert100nsToMilliseconds(HnsTime) \ + RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) + + +// +// External function prototypes +// +extern int +IPInit( + void + ); + +long +IPSetInfo( + TDIObjectID *ID, + void *Buffer, + uint Size + ); + +NTSTATUS +IPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ); + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ); + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ); + +NTSTATUS +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ); + +uint +RTReadNext( + void *Context, + void *Buffer + ); + +uint +RTValidateContext( + void *Context, + uint *Valid + ); + +// +// Local funcion prototypes +// +NTSTATUS +IPDriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +IPProcessConfiguration( + VOID + ); + +NTSTATUS +IPProcessAdapterSection( + WCHAR *DeviceName, + WCHAR *AdapterName + ); + +#ifndef _PNP_POWER + +NTSTATUS +IPProcessIPAddressList( + HANDLE AdapterKey, + WCHAR *DeviceName, + WCHAR *AdapterName, + WCHAR *IpAddressList, + WCHAR *SubnetMaskList, + NDIS_STRING *LowerInterfaceString, + uint LowerInterfaceType, + PPER_NET_CONFIG_INFO PerNetConfigInfo + ); + +#else // _PNP_POWER + +uint +GetGeneralIFConfig( + IFGeneralConfig *ConfigInfo, + NDIS_HANDLE Handle + ); + +int +IsLLInterfaceValueNull( + NDIS_HANDLE Handle + ); + +IFAddrList * +GetIFAddrList( + UINT *NumAddr, + NDIS_HANDLE Handle + ); + +#endif // _PNP_POWER + +UINT +OpenIFConfig( + PNDIS_STRING ConfigName, + NDIS_HANDLE *Handle + ); + +VOID +CloseIFConfig( + NDIS_HANDLE Handle + ); + +IPConfigInfo * +IPGetConfig( + void + ); + +void +IPFreeConfig( + IPConfigInfo *ConfigInfo + ); + +ulong +GetGMTDelta( + void + ); + +ulong +GetTime( + void + ); + +BOOLEAN +IPConvertStringToAddress( + IN PWCHAR AddressString, + OUT PULONG IpAddress + ); + +uint +UseEtherSNAP( + PNDIS_STRING Name + ); + +void +GetAlwaysSourceRoute( + uint *pArpAlwaysSourceRoute, + uint *pIPAlwaysSourceRoute + ); + +uint +GetArpCacheLife( + void + ); + +ULONG +RouteMatch( + IN WCHAR *RouteString, + IN IPAddr Address, + IN IPMask Mask, + OUT IPAddr *DestVal, + OUT IPMask *DestMask, + OUT IPAddr *GateVal, + OUT ULONG *Metric + ); + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ); + +ULONG +GetCurrentRouteTable( + IPRouteEntry **ppRouteTable + ); + + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, IPDriverEntry) +#pragma alloc_text(INIT, IPProcessConfiguration) +#pragma alloc_text(INIT, IPProcessAdapterSection) +#pragma alloc_text(INIT, IPGetConfig) +#pragma alloc_text(INIT, IPFreeConfig) +#pragma alloc_text(INIT, GetGMTDelta) +#pragma alloc_text(INIT, GetTime) + +#ifndef _PNP_POWER + +#pragma alloc_text(INIT, IPProcessIPAddressList) +#pragma alloc_text(INIT, UseEtherSNAP) +#pragma alloc_text(INIT, GetAlwaysSourceRoute) +#pragma alloc_text(INIT, GetArpCacheLife) + +#else // _PNP_POWER + +#pragma alloc_text(PAGE, GetGeneralIFConfig) +#pragma alloc_text(PAGE, IsLLInterfaceValueNull) +#pragma alloc_text(PAGE, GetIFAddrList) +#pragma alloc_text(PAGE, UseEtherSNAP) +#pragma alloc_text(PAGE, GetAlwaysSourceRoute) +#pragma alloc_text(PAGE, GetArpCacheLife) + +#endif // _PNP_POWER + +#pragma alloc_text(PAGE, OpenIFConfig) +#pragma alloc_text(PAGE, CloseIFConfig) +#pragma alloc_text(PAGE, RouteMatch) +#pragma alloc_text(PAGE, SetPersistentRoutesForNTE) +#pragma alloc_text(PAGE, IPConvertStringToAddress) + +#endif // ALLOC_PRAGMA + +// +// Function definitions +// +NTSTATUS +IPDriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + Initialization routine for the IP driver. + +Arguments: + + DriverObject - Pointer to the IP driver object created by the system. + DeviceDescription - The name of IP's node in the registry. + +Return Value: + + The final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + + + IPDriverObject = DriverObject; + + // + // Create the device object. IoCreateDevice zeroes the memory + // occupied by the object. + // + + RtlInitUnicodeString(&deviceName, DD_IP_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_NETWORK, + 0, + FALSE, + &IPDeviceObject + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP initialization failed: Unable to create device object %ws, status %lx.", + DD_IP_DEVICE_NAME, + status + )); + + CTELogEvent( + DriverObject, + EVENT_TCPIP_CREATE_DEVICE_FAILED, + 1, + 1, + &deviceName.Buffer, + 0, + NULL + ); + + return(status); + } + + // + // Intialize the device object. + // + IPDeviceObject->Flags |= DO_DIRECT_IO; + + // + // Initialize the list of pending echo request IRPs. + // + InitializeListHead(&PendingEchoList); + + // + // Initialize the list of pending SetAddr request IRPs. + // + InitializeListHead(&PendingIPSetNTEAddrList); + + // + // Finally, read our configuration parameters from the registry. + // + status = IPProcessConfiguration(); + + if (status != STATUS_SUCCESS) { + IoDeleteDevice(IPDeviceObject); + } + + return(status); +} + +NTSTATUS +IPProcessConfiguration( + VOID + ) + +/*++ + +Routine Description: + + Reads the IP configuration information from the registry and constructs + the configuration structure expected by the IP driver. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS or an error status if an operation fails. + +--*/ + +{ + NTSTATUS status; + HANDLE myRegKey = NULL; + UNICODE_STRING bindString; + WCHAR *aName, + *endOfString; + WCHAR IPParametersRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + WCHAR IPLinkageRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Linkage"; + uint ArpTRSingleRoute; + + + bindString.Buffer = NULL; + + IPConfiguration = CTEAllocMem(sizeof(IPConfigInfo)); + + if (IPConfiguration == NULL) { + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 1, + 0, + NULL, + 0, + NULL + ); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEMemSet(IPConfiguration, 0, sizeof(IPConfigInfo)); + +#ifndef _PNP_POWER + + IPConfiguration->ici_netinfo = CTEAllocMem( + sizeof(NetConfigInfo) * DEFAULT_IP_NETS + ); + + if (IPConfiguration->ici_netinfo == NULL) { + + CTEFreeMem(IPConfiguration); + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 2, + 0, + NULL, + 0, + NULL + ); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEMemSet( + IPConfiguration->ici_netinfo, + 0, + sizeof(NetConfigInfo) * DEFAULT_IP_NETS + ); + +#endif // _PNP_POWER + + // + // Process the Ip\Parameters section of the registry + // + status = OpenRegKey(&myRegKey, IPParametersRegistryKey); + + if (NT_SUCCESS(status)) { + // + // Expected configuration values. We use reasonable defaults if they + // aren't available for some reason. + // + status = GetRegDWORDValue( + myRegKey, + L"IpEnableRouter", + &(IPConfiguration->ici_gateway) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read IpEnableRouter value from the registry.\n" + " Routing will be disabled.\n" + )); + IPConfiguration->ici_gateway = 0; + } + + // + // Optional (hidden) values + // + (VOID)InitRegDWORDParameter( + myRegKey, + L"ForwardBufferMemory", + &(IPConfiguration->ici_fwbufsize), + DEFAULT_FW_BUFSIZE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"MaxForwardBufferMemory", + &(IPConfiguration->ici_maxfwbufsize), + DEFAULT_MAX_FW_BUFSIZE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ForwardBroadcasts", + &(IPConfiguration->ici_fwbcast), + FALSE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"NumForwardPackets", + &(IPConfiguration->ici_fwpackets), + DEFAULT_FW_PACKETS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"MaxNumForwardPackets", + &(IPConfiguration->ici_maxfwpackets), + DEFAULT_MAX_FW_PACKETS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"IGMPLevel", + &(IPConfiguration->ici_igmplevel), + DEFAULT_IGMP_LEVEL + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"EnableDeadGWDetect", + &(IPConfiguration->ici_deadgwdetect), + TRUE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"EnablePMTUDiscovery", + &(IPConfiguration->ici_pmtudiscovery), + TRUE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"DefaultTTL", + &(IPConfiguration->ici_ttl), + DEFAULT_TTL + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"DefaultTOS", + &(IPConfiguration->ici_tos), + DEFAULT_TOS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpUseEtherSnap", + &ArpUseEtherSnap, + FALSE + ); + + // + // we check for the return status here because if this parameter was + // not defined, then we want the default behavior for both arp + // and ip broadcasts. For arp, the behavior is to not source route + // and source router alternately. For ip, it is to always source + // route. If the parameter is defined and is 0, then for arp the + // behavior does not change. For ip however, we do not source route + // at all. Ofcourse, when the parameter is set to a non-zero value, + // we always source route for both. + // + status = InitRegDWORDParameter( + myRegKey, + L"ArpAlwaysSourceRoute", + &ArpAlwaysSourceRoute, + FALSE + ); + + if (NT_SUCCESS(status)) + { + IPAlwaysSourceRoute = ArpAlwaysSourceRoute; + } + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpTRSingleRoute", + &ArpTRSingleRoute, + FALSE + ); + + if (ArpTRSingleRoute) { + TrRii = TR_RII_SINGLE; + } else { + TrRii = TR_RII_ALL; + } + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpCacheLife", + &ArpCacheLife, + DEFAULT_ARP_CACHE_LIFE + ); + + ZwClose(myRegKey); + myRegKey = NULL; + } + else { + // + // Use reasonable defaults. + // + IPConfiguration->ici_fwbcast = 0; + IPConfiguration->ici_gateway = 0; + IPConfiguration->ici_fwbufsize = DEFAULT_FW_BUFSIZE; + IPConfiguration->ici_fwpackets = DEFAULT_FW_PACKETS; + IPConfiguration->ici_maxfwbufsize = DEFAULT_MAX_FW_BUFSIZE; + IPConfiguration->ici_maxfwpackets = DEFAULT_MAX_FW_PACKETS; + IPConfiguration->ici_igmplevel = DEFAULT_IGMP_LEVEL; + IPConfiguration->ici_deadgwdetect = FALSE; + IPConfiguration->ici_pmtudiscovery = FALSE; + IPConfiguration->ici_ttl = DEFAULT_TTL; + IPConfiguration->ici_tos = DEFAULT_TOS; + + TCPTRACE(( + "IP: Unable to open Tcpip\\Parameters registry key. Using defaults.\n" + )); + } + + + // + // Process the Ip\Linkage section of the registry + // + status = OpenRegKey(&myRegKey, IPLinkageRegistryKey); + + if (NT_SUCCESS(status)) { + + bindString.Buffer = CTEAllocMem(WORK_BUFFER_SIZE * sizeof(WCHAR)); + + if (bindString.Buffer == NULL) { + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 3, + 0, + NULL, + 0, + NULL + ); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto error_exit; + } + + bindString.Buffer[0] = UNICODE_NULL; + bindString.Length = 0; + bindString.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR); + + status = GetRegMultiSZValue( + myRegKey, + L"Bind", + &bindString + ); + + if (NT_SUCCESS(status)) { + aName = bindString.Buffer; + + if (bindString.Length > 0) { + // + // bindString is a MULTI_SZ which is a series of strings separated + // by NULL's with a double NULL at the end. + // + while (*aName != UNICODE_NULL) { + PWCHAR deviceName; + + deviceName = aName; + + // + // Find the end of the current string in the MULTI_SZ. + // + while (*aName != UNICODE_NULL) { + aName++; + ASSERT( + aName < + (PWCHAR) ( ((PUCHAR)bindString.Buffer) + + bindString.MaximumLength + ) + ); + } + + endOfString = aName; + + // + // Backtrack to the first backslash. + // + while ((aName >= bindString.Buffer) && (*aName-- != L'\\')); + + aName += 2; + + status = IPProcessAdapterSection( + deviceName, + aName + ); + + aName = endOfString + 1; + } + } + } +#ifndef _PNP_POWER + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_BINDINGS, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open Tcpip\\Linkage\\Bind registry value.\n" + " Only the local loopback interface will be accessible.\n" + )); + + } +#endif _PNP_POWER + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_BINDINGS, + 2, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open registry key Tcpip\\Linkage.\n" + " Only the local loopback interface will be accessible.\n" + )); + } + + +#ifndef _PNP_POWER + + // + // Allocate the Driver and Adapter name tables + // + + DriverNameTable = (DriverRegMapping *) CTEAllocMem( + (IPConfiguration->ici_numnets + 1) * + sizeof(DriverRegMapping) + ); + + AdptNameTable = (NameMapping *) CTEAllocMem( + (IPConfiguration->ici_numnets + 1) * + sizeof(NameMapping) + ); + + if ((DriverNameTable != NULL) && (AdptNameTable != NULL)) { + CTEMemSet( + DriverNameTable, + 0, + sizeof(DriverRegMapping) * (IPConfiguration->ici_numnets + 1) + ); + CTEMemSet( + AdptNameTable, + 0, + sizeof(NameMapping) * (IPConfiguration->ici_numnets + 1) + ); + +#endif // _PNP_POWER + + if (!IPInit()) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_IP_INIT_FAILED, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(("IP initialization failed.\n")); + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + +#ifndef _PNP_POWER + + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_IP_INIT_FAILED, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(("IP initialization failed.\n")); + status = STATUS_UNSUCCESSFUL; + } + +#endif // _PNP_POWER + +error_exit: + + if (bindString.Buffer != NULL) { + CTEFreeMem(bindString.Buffer); + } + +#ifndef _PNP_POWER + + if (AdptNameTable != NULL) { + CTEFreeMem(AdptNameTable); + } + + if (DriverNameTable != NULL) { + CTEFreeMem(DriverNameTable); + } + +#endif // _PNP_POWER + + if (myRegKey != NULL) { + ZwClose(myRegKey); + } + + if (IPConfiguration != NULL) { + IPFreeConfig(IPConfiguration); + } + + return(status); +} + + +NTSTATUS +IPProcessAdapterSection( + WCHAR *DeviceName, + WCHAR *AdapterName + ) + +/*++ + +Routine Description: + + Reads all of the information needed under the Parameters\TCPIP section + of an adapter to which IP is bound. + +Arguments: + + DeviceName - The name of the IP device. + AdapterName - The registry key for the adapter for this IP net. + +Return Value: + + STATUS_SUCCESS or an error status if an operation fails. + +--*/ + +{ + HANDLE myRegKey; + UNICODE_STRING valueString; + NTSTATUS status; + ULONG valueType; + ulong invalidNetContext = 0xFFFF; + WCHAR TcpipParametersKey[] = L"\\Parameters\\TCPIP"; + WCHAR ServicesRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; +#ifndef _PNP_POWER + uint numberOfGateways = 0; + uint llInterfaceType; + WCHAR *ipAddressBuffer = NULL; + WCHAR *subnetMaskBuffer = NULL; + NDIS_STRING llInterfaceString; + PER_NET_CONFIG_INFO perNetConfigInfo; + NetConfigInfo *NetConfiguration; + PWCHAR temp; + + + RtlInitUnicodeString(&llInterfaceString, NULL); + + NetConfiguration = IPConfiguration->ici_netinfo + + IPConfiguration->ici_numnets; + +#endif // _PNP_POWER + + // + // Get the size of the AdapterName string the easy way. + // + RtlInitUnicodeString(&valueString, AdapterName); + + valueString.MaximumLength += sizeof(ServicesRegistryKey) + + sizeof(TcpipParametersKey); + + valueString.Buffer = CTEAllocMem(valueString.MaximumLength); + + if (valueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for reg key name\n")); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + valueString.Length = 0; + valueString.Buffer[0] = UNICODE_NULL; + + // + // Build the key name for the tcpip parameters section and open key. + // + status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append services name to key string\n")); + + goto exit2; + } + + status = RtlAppendUnicodeToString(&valueString, AdapterName); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 2, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append adapter name to key string\n")); + + goto exit2; + } + + status = RtlAppendUnicodeToString(&valueString, TcpipParametersKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 3, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append parameters name to key string\n")); + + goto exit2; + } + + status = OpenRegKey(&myRegKey, valueString.Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open adapter registry key %ws\n", + valueString.Buffer + )); + + goto exit2; + } + + // + // Invalidate the interface context for DHCP. + // When the first net is successfully configured, we'll write in the + // proper values. + // + status = SetRegDWORDValue( + myRegKey, + L"IPInterfaceContext", + &(invalidNetContext) + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to Invalidate IPInterfaceContext value for adapter %ws.\n" + " DHCP may fail on this adapter.\n", + AdapterName + )); + + goto exit1; + } + +#ifndef _PNP_POWER + + // + // Process the gateway MultiSZ. The end is signified by a double NULL. + // This list currently only applies to the first IP address configured + // on this interface. + // + status = GetRegMultiSZValue( + myRegKey, + L"DefaultGateway", + &valueString + ); + + if (NT_SUCCESS(status)) { + PWCHAR addressString = valueString.Buffer; + + while (*addressString != UNICODE_NULL) { + IPAddr addressValue; + BOOLEAN conversionStatus; + + if (numberOfGateways >= MAX_DEFAULT_GWS) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_GATEWAYS, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + break; + } + + conversionStatus = IPConvertStringToAddress( + addressString, + &addressValue + ); + + if (conversionStatus && (addressValue != 0xFFFFFFFF)) { + if (addressValue != INADDR_ANY) { + NetConfiguration->nci_gw[numberOfGateways++] = addressValue; + } + } + else { + PWCHAR stringList[2]; + + stringList[0] = addressString; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_DEFAULT_GATEWAY, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid default gateway address %ws specified for adapter %ws.\n" + " Remote networks may not be reachable as a result.\n", + addressString, + AdapterName + )); + } + + // + // Walk over the entry we just processed. + // + while (*addressString++ != UNICODE_NULL); + } + } + else { + TCPTRACE(( + "IP: Unable to read DefaultGateway value for adapter %ws.\n" + " Initialization will continue.\n", + AdapterName + )); + } + + perNetConfigInfo.NumberOfGateways = numberOfGateways; + + // + // Figure out which lower layer driver to bind. + // + status = GetRegSZValue( + myRegKey, + L"LLInterface", + &valueString, + &valueType + ); + + if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) { + llInterfaceType = NET_TYPE_WAN; + + if (!CTEAllocateString( + &llInterfaceString, + CTELengthString(&valueString) + ) + ) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP initialization failure: Unable to allocate memory " + "for LLInterface string for adapter %ws.\n", + AdapterName + )); + status = STATUS_INSUFFICIENT_RESOURCES; + goto exit1; + } + + CTECopyString( + &llInterfaceString, + &valueString + ); + } + else { + // + // If the key isn't present or is empty, we use ARP + // + llInterfaceType = NET_TYPE_LAN; + } + + // + // Are we using zeros broadcasts? + // + status = GetRegDWORDValue( + myRegKey, + L"UseZeroBroadcast", + &(perNetConfigInfo.UseZeroBroadcast) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n" + " All-nets broadcasts will be addressed to 255.255.255.255.\n", + AdapterName + )); + perNetConfigInfo.UseZeroBroadcast = FALSE; // default to off + } + + // + // Has anyone specified an MTU? + // + status = GetRegDWORDValue( + myRegKey, + L"MTU", + &(perNetConfigInfo.Mtu) + ); + + if (!NT_SUCCESS(status)) { + perNetConfigInfo.Mtu = 0xFFFFFFF; // The stack will pick one. + } + + // + // Have we been configured for more routing packets? + // + status = GetRegDWORDValue( + myRegKey, + L"MaxForwardPending", + &(perNetConfigInfo.MaxForwardPending) + ); + + if (!NT_SUCCESS(status)) { + perNetConfigInfo.MaxForwardPending = DEFAULT_MAX_PENDING; + } + + // + // Read the IP address and Subnet Mask lists + // + status = GetRegMultiSZValue( + myRegKey, + L"IpAddress", + &valueString + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADDRESS_LIST, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the IP address list for adapter %ws.\n" + " IP will not be operational on this adapter\n", + AdapterName + )); + goto exit1; + } + + ipAddressBuffer = ExAllocatePool(NonPagedPool, valueString.Length); + + if (ipAddressBuffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + goto exit1; + } + + RtlCopyMemory(ipAddressBuffer, valueString.Buffer, valueString.Length); + + status = GetRegMultiSZValue( + myRegKey, + L"Subnetmask", + &valueString + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK_LIST, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the subnet mask list for adapter %ws.\n" + " IP will not be operational on this adapter.\n", + AdapterName + )); + goto exit1; + } + + subnetMaskBuffer = ExAllocatePool(NonPagedPool, valueString.Length); + + if (subnetMaskBuffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 3, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n")); + goto exit1; + } + + RtlCopyMemory(subnetMaskBuffer, valueString.Buffer, valueString.Length); + + // + // Initialize each net in the list + // + status = IPProcessIPAddressList( + myRegKey, + DeviceName, + AdapterName, + ipAddressBuffer, + subnetMaskBuffer, + &llInterfaceString, + llInterfaceType, + &perNetConfigInfo + ); + + if (status == STATUS_SUCCESS) { + // + // We leave the registry key open. It will be closed when + // initialization is completed. + // + goto exit2; + } + +#endif // ndef _PNP_POWER + + +exit1: + + ZwClose(myRegKey); + +exit2: + + if (valueString.Buffer != NULL) { + CTEFreeMem(valueString.Buffer); + } + +#ifndef _PNP_POWER + + if (ipAddressBuffer != NULL) { + ExFreePool(ipAddressBuffer); + } + + if (subnetMaskBuffer != NULL) { + ExFreePool(subnetMaskBuffer); + } + + if (llInterfaceString.Buffer != NULL) { + CTEFreeString(&llInterfaceString); + } + +#endif // _PNP_POWER + + return(status); +} + +#ifndef _PNP_POWER + +NTSTATUS +IPProcessIPAddressList( + HANDLE AdapterKey, + WCHAR *DeviceName, + WCHAR *AdapterName, + WCHAR *IpAddressList, + WCHAR *SubnetMaskList, + NDIS_STRING *LowerInterfaceString, + uint LowerInterfaceType, + PPER_NET_CONFIG_INFO PerNetConfigInfo + ) + +/*++ + +Routine Description: + + Processes the IP address string for an adapter and creates entries + in the IP configuration structure for each interface. + +Arguments: + + AdapterKey - The registry key for the adapter for this IP net. + DeviceName - The name of the IP device. + AdapterName - The name of the adapter being configured. + IpAddressList - The REG_MULTI_SZ list of IP address strings for + this adapter. + SubnetMaskList - The REG_MULTI_SZ list of subnet masks to match the + the addresses in IpAddressList. + LowerInterfaceString - The name of the link layer interface driver + supporting this adapter. + LowerInterfaceType - The type of link layer interface (LAN, WAN, etc). + PerNetConfigInfo - Miscellaneous information that applies to all + network interfaces on an adapter. + +Return Value: + + An error status if an error occurs which prevents configuration + from continuing, else STATUS_SUCCESS. Events will be logged for + non-fatal errors. + +--*/ + +{ + IPAddr addressValue; + BOOLEAN firstTime = TRUE; + BOOLEAN configuredOne = FALSE; + NetConfigInfo *NetConfiguration; + UNICODE_STRING adapterString; + UNICODE_STRING configString; + PWCHAR configName = L"\\Parameters\\Tcpip"; + + + while (*IpAddressList != UNICODE_NULL) { + BOOLEAN conversionStatus; + + + if (IPConfiguration->ici_numnets >= ((int) (NetConfigSize - 1))) { + NetConfigInfo *NewInfo; + + NewInfo = CTEAllocMem( + (NetConfigSize + DEFAULT_IP_NETS) * + sizeof(NetConfigInfo) + ); + + if (NewInfo == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_NETS, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: bound to too many nets. Further bindings, starting with\n" + " network %ws on adapter %ws cannot be made\n", + IpAddressList, + AdapterName + )); + + break; + } + + CTEMemCopy( + NewInfo, + IPConfiguration->ici_netinfo, + NetConfigSize * sizeof(NetConfigInfo) + ); + + CTEMemSet( + (NewInfo + NetConfigSize), + 0, + DEFAULT_IP_NETS + ); + + CTEFreeMem(IPConfiguration->ici_netinfo); + IPConfiguration->ici_netinfo = NewInfo; + NetConfigSize += DEFAULT_IP_NETS; + } + + NetConfiguration = IPConfiguration->ici_netinfo + + IPConfiguration->ici_numnets; + + if (*SubnetMaskList == UNICODE_NULL) { + PWCHAR stringList[2]; + + stringList[0] = IpAddressList; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: No subnet specified for IP address %ws and all\n" + " subsequent IP addresses on adapter %ws. These\n" + " interfaces will not be initialized.\n", + IpAddressList, + AdapterName + )); + + break; + } + + conversionStatus = IPConvertStringToAddress( + IpAddressList, + &addressValue + ); + + if (!conversionStatus || (addressValue == 0xFFFFFFFF)) { + PWCHAR stringList[2]; + + stringList[0] = IpAddressList; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_ADDRESS, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid IP address %ws specified for adapter %ws.\n" + " This interface will not be initialized.\n", + IpAddressList, + AdapterName + )); + firstTime = FALSE; + goto next_entry; + } + + NetConfiguration->nci_addr = addressValue; + + conversionStatus = IPConvertStringToAddress( + SubnetMaskList, + &addressValue + ); + + if (!conversionStatus || (addressValue == 0xFFFFFFFF)) { + PWCHAR stringList[3]; + + stringList[0] = SubnetMaskList; + stringList[1] = IpAddressList; + stringList[2] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_MASK, + 1, + 3, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid subnet Mask %ws specified for IP address %ws " + "on adapter %ws\n" + " This interface will not be initialized\n", + SubnetMaskList, + IpAddressList, + AdapterName + )); + firstTime = FALSE; + goto next_entry; + } + + NetConfiguration->nci_mask = addressValue; + + NetConfiguration->nci_mtu = PerNetConfigInfo->Mtu; + NetConfiguration->nci_maxpending = PerNetConfigInfo->MaxForwardPending; + NetConfiguration->nci_zerobcast = PerNetConfigInfo->UseZeroBroadcast; + NetConfiguration->nci_type = LowerInterfaceType; + + NetConfiguration->nci_numgws = PerNetConfigInfo->NumberOfGateways; + PerNetConfigInfo->NumberOfGateways = 0; + // this only applies to the first interface. + + NetConfiguration->nci_type = LowerInterfaceType; + + RtlInitUnicodeString( + &(NetConfiguration->nci_name), + DeviceName + ); + + RtlInitUnicodeString(&configString, configName); + RtlInitUnicodeString(&adapterString, AdapterName); + + if (!CTEAllocateString( + &(NetConfiguration->nci_configname), + (adapterString.Length + configString.Length) + ) + ) + { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to allocate ConfigName string for interface\n" + " %ws on adapter %ws. This interface and all subsequent\n" + " interfaces on this adapter will be unavailable.\n", + IpAddressList, + AdapterName + )); + break; + } + + CTECopyString( + &(NetConfiguration->nci_configname), + &adapterString + ); + + RtlAppendUnicodeStringToString( + &(NetConfiguration->nci_configname), + &configString + ); + + if (LowerInterfaceType != NET_TYPE_LAN) { + + if (!CTEAllocateString( + &(NetConfiguration->nci_driver), + CTELengthString(LowerInterfaceString) + ) + ) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to allocate LLInterface string for interface\n" + " %ws on adapter %ws. This interface and all subsequent\n" + " interfaces on this adapter will be unavailable.\n", + IpAddressList, + AdapterName + )); + break; + } + + CTECopyString( + &(NetConfiguration->nci_driver), + LowerInterfaceString + ); + } + else { + RtlInitUnicodeString(&(NetConfiguration->nci_driver), NULL); + } + + if (firstTime) { + firstTime = FALSE; + NetConfiguration->nci_reghandle = AdapterKey; + } + else { + NetConfiguration->nci_reghandle = NULL; + } + + IPConfiguration->ici_numnets++; + configuredOne = TRUE; + +next_entry: + + while(*IpAddressList++ != UNICODE_NULL); + while(*SubnetMaskList++ != UNICODE_NULL); + } + + if (configuredOne == FALSE) { + ZwClose(AdapterKey); + } + + return(STATUS_SUCCESS); +} + + +#else // ndef _PNP_POWER + + +uint +GetGeneralIFConfig( + IFGeneralConfig *ConfigInfo, + NDIS_HANDLE Handle + ) + +/*++ + + Routine Description: + + A routine to get the general per-interface config info, such as MTU, + type of broadcast, etc. The caller gives us a structure to be filled in + and a handle, and we fill in the structure if we can. + + Arguments: + ConfigInfo - Structure to be filled in. + Handle - Config handle from OpenIFConfig(). + + Return Value: + TRUE if we got all the required info, FALSE otherwise. + +--*/ + +{ + UNICODE_STRING valueString; + NTSTATUS status; + UINT numberOfGateways = 0; + UCHAR TempBuffer[WORK_BUFFER_SIZE]; + ULONG ulAddGateway,ulTemp; + + PAGED_CODE(); + + // + // Process the gateway MultiSZ. The end is signified by a double NULL. + // This list currently only applies to the first IP address configured + // on this interface. + // + + ConfigInfo->igc_numgws = 0; + + ulAddGateway = TRUE; + + CTEMemSet(ConfigInfo->igc_gw, 0, sizeof(IPAddr) * MAX_DEFAULT_GWS); + + valueString.Length = 0; + valueString.MaximumLength = WORK_BUFFER_SIZE; + valueString.Buffer = (PWCHAR)TempBuffer; + + ulTemp = 0; + + status = GetRegDWORDValue(Handle, + L"DontAddDefaultGateway", + &ulTemp); + + if(NT_SUCCESS(status)) + { + if(ulTemp == 1) + { + ulAddGateway = FALSE; + } + } + + if(ulAddGateway) + { + status = GetRegMultiSZValue( + Handle, + L"DefaultGateway", + &valueString + ); + + if (NT_SUCCESS(status)) { + PWCHAR addressString = valueString.Buffer; + + while (*addressString != UNICODE_NULL) { + IPAddr addressValue; + BOOLEAN conversionStatus; + + if (numberOfGateways >= MAX_DEFAULT_GWS) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_GATEWAYS, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + break; + } + + conversionStatus = IPConvertStringToAddress( + addressString, + &addressValue + ); + + if (conversionStatus && (addressValue != 0xFFFFFFFF)) { + if (addressValue != INADDR_ANY) { + ConfigInfo->igc_gw[numberOfGateways++] = addressValue; + } + } + else { + PWCHAR stringList[2]; + + stringList[0] = addressString; + stringList[1] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_DEFAULT_GATEWAY, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid default gateway address %ws specified for adapter %ws.\n" + " Remote networks may not be reachable as a result.\n", + addressString, + TempAdapterName + )); + } + + // + // Walk over the entry we just processed. + // + while (*addressString++ != UNICODE_NULL); + } + } + else { + TCPTRACE(( + "IP: Unable to read DefaultGateway value for adapter %ws.\n" + " Initialization will continue.\n", + TempAdapterName + )); + } + + ConfigInfo->igc_numgws = numberOfGateways; + } + + // + // Are we using zeros broadcasts? + // + status = GetRegDWORDValue( + Handle, + L"UseZeroBroadcast", + &(ConfigInfo->igc_zerobcast) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n" + " All-nets broadcasts will be addressed to 255.255.255.255.\n", + TempAdapterName + )); + ConfigInfo->igc_zerobcast = FALSE; // default to off + } + + // + // Has anyone specified an MTU? + // + status = GetRegDWORDValue( + Handle, + L"MTU", + &(ConfigInfo->igc_mtu) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_mtu = 0xFFFFFFF; // The stack will pick one. + } + + // + // Have we been configured for more routing packets? + // + status = GetRegDWORDValue( + Handle, + L"MaxForwardPending", + &(ConfigInfo->igc_maxpending) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_maxpending = DEFAULT_MAX_PENDING; + } + // + // Has Router Discovery been configured? + // + + status = GetRegDWORDValue( + Handle, + L"PerformRouterDiscovery", + &(ConfigInfo->igc_rtrdiscovery) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_rtrdiscovery = 1; + } + + // + // [BUGBUG] Only for 4.0 sp2 Turn off ICMP rtr discovery. + // + ConfigInfo->igc_rtrdiscovery = 0; + + // + // Has Router Discovery Address been configured? + // + + status = GetRegDWORDValue( + Handle, + L"SolicitationAddressBCast", + &ulTemp + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; + } else { + if (ulTemp == 1) { + ConfigInfo->igc_rtrdiscaddr = 0xffffffff; + } else { + ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; + } + } + + return TRUE; +} + + +int +IsLLInterfaceValueNull( + NDIS_HANDLE Handle + ) +/*++ + + Routine Description: + + Called to see if the LLInterface value in the registry key for which the + handle is provided, is NULL or not. + + Arguments: + Handle - Handle to use for reading config. + + Return Value: + + FALSE if value is not null + TRUE if it is null + +--*/ +{ + UNICODE_STRING valueString ; + ULONG valueType ; + NTSTATUS status ; + + + PAGED_CODE(); + + valueString.MaximumLength = 200 ; + valueString.Buffer = CTEAllocMem(valueString.MaximumLength) ; + + status = GetRegSZValue( + Handle, + L"LLInterface", + &valueString, + &valueType + ); + + if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) { + CTEFreeMem (valueString.Buffer) ; + return FALSE ; + } else { + CTEFreeMem (valueString.Buffer) ; + return TRUE ; + } +} + + +IFAddrList * +GetIFAddrList( + UINT *NumAddr, + NDIS_HANDLE Handle + ) +/*++ + + Routine Description: + + Called to read the list of IF addresses and masks for an interface. + We'll get the address pointer first, then walk the list counting + to find out how many addresses we have. Then we allocate memory for the + list, and walk down the list converting them. After that we'll get + the mask list and convert it. + + Arguments: + NumAddr - Where to return number of address we have. + Handle - Handle to use for reading config. + + Return Value: + + Pointer to IF address list if we get one, or NULL otherwise. + +--*/ +{ + UNICODE_STRING ValueString; + NTSTATUS Status; + UINT AddressCount = 0; + UINT GoodAddresses = 0; + PWCHAR CurrentAddress; + PWCHAR CurrentMask; + PWCHAR AddressString; + PWCHAR MaskString; + IFAddrList *AddressList; + UINT i; + BOOLEAN ConversionStatus; + IPAddr AddressValue; + IPAddr MaskValue; + UCHAR TempBuffer[WORK_BUFFER_SIZE]; + + + PAGED_CODE(); + + ValueString.Length = 0; + ValueString.MaximumLength = WORK_BUFFER_SIZE; + ValueString.Buffer = (PWCHAR)CTEAllocMem(WORK_BUFFER_SIZE); + + if (ValueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list WB\n")); + return NULL; + } + + // First, try to read the IpAddress string. + + Status = GetRegMultiSZValue( + Handle, + L"IpAddress", + &ValueString + ); + + if (!NT_SUCCESS(Status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADDRESS_LIST, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the IP address list for adapter %ws.\n" + " IP will not be operational on this adapter\n", + TempAdapterName + )); + ExFreePool(ValueString.Buffer); + return NULL; + } + + AddressString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength); + + if (AddressString == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + ExFreePool(ValueString.Buffer); + return NULL; + } + + RtlCopyMemory(AddressString, ValueString.Buffer, ValueString.MaximumLength); + + Status = GetRegMultiSZValue( + Handle, + L"Subnetmask", + &ValueString + ); + + if (!NT_SUCCESS(Status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK_LIST, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the subnet mask list for adapter %ws.\n" + " IP will not be operational on this adapter.\n", + TempAdapterName + )); + + ExFreePool(AddressString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + MaskString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength); + + if (MaskString == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 3, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n")); + ExFreePool(AddressString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + RtlCopyMemory(MaskString, ValueString.Buffer, ValueString.MaximumLength); + + + CurrentAddress = AddressString; + CurrentMask = MaskString; + + while (*CurrentAddress != UNICODE_NULL && + *CurrentMask != UNICODE_NULL) { + + // We have a potential IP address. + + AddressCount++; + + // Skip this one. + while (*CurrentAddress++ != UNICODE_NULL); + while (*CurrentMask++ != UNICODE_NULL); + } + + if (AddressCount == 0) { + + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + // Allocate memory. + AddressList = CTEAllocMem(sizeof(IFAddrList) * AddressCount); + + if (AddressList == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + // Walk the list again, converting each address. + + CurrentAddress = AddressString; + CurrentMask = MaskString; + + for (i = 0; i < AddressCount; i++) { + ConversionStatus = IPConvertStringToAddress( + CurrentAddress, + &AddressValue + ); + + if (!ConversionStatus || (AddressValue == 0xFFFFFFFF)) { + PWCHAR stringList[2]; + + stringList[0] = CurrentAddress; + stringList[1] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_ADDRESS, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid IP address %ws specified for adapter %ws.\n" + " This interface will not be initialized.\n", + CurrentAddress, + TempAdapterName + )); + + goto nextone; + + } + + // Now do the current mask. + + ConversionStatus = IPConvertStringToAddress( + CurrentMask, + &MaskValue + ); + + if (!ConversionStatus) { + PWCHAR stringList[3]; + + stringList[0] = CurrentMask; + stringList[1] = CurrentAddress; + stringList[2] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_MASK, + 1, + 3, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid subnet Mask %ws specified for IP address %ws " + "on adapter %ws\n" + " This interface will not be initialized\n", + CurrentMask, + CurrentAddress, + TempAdapterName + )); + } else { + AddressList[GoodAddresses].ial_addr = AddressValue; + AddressList[GoodAddresses].ial_mask = MaskValue; + GoodAddresses++; + } + +nextone: + while(*CurrentAddress++ != UNICODE_NULL); + while(*CurrentMask++ != UNICODE_NULL); + + } + + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + + *NumAddr = GoodAddresses; + + if (GoodAddresses == 0) { + ExFreePool(AddressList); + AddressList = NULL; + } + + return AddressList; +} + +#endif // PNP_POWER + + +UINT +OpenIFConfig( + PNDIS_STRING ConfigName, + NDIS_HANDLE *Handle + ) + +/*++ + + Routine Description: + + Called when we want to open our per-info config info. We do so if we can, + otherwise we fail the request. + + Arguments: + ConfigName - Name of interface to open. + Handle - Where to return the handle. + + Return Value: + TRUE if we succeed, FALSE if we don't. + + +--*/ + +{ + NTSTATUS status; + HANDLE myRegKey; + UNICODE_STRING valueString; + WCHAR ServicesRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; + UINT RetStatus = FALSE; + + + PAGED_CODE(); + + TempAdapterName = ConfigName->Buffer; + + // + // Get the size of the ConfigName string the easy way. + // + RtlInitUnicodeString(&valueString, (PWCHAR)ConfigName->Buffer); + + valueString.MaximumLength += sizeof(ServicesRegistryKey); + + valueString.Buffer = ExAllocatePool( + NonPagedPool, + valueString.MaximumLength + ); + + if (valueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for reg key name\n")); + + return(FALSE); + } + + valueString.Length = 0; + valueString.Buffer[0] = UNICODE_NULL; + + // + // Build the key name for the tcpip parameters section and open key. + // + status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 1, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append services name to key string\n")); + + goto done; + } + + status = RtlAppendUnicodeToString(&valueString, ConfigName->Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 2, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append adapter name to key string\n")); + + goto done; + } + + status = OpenRegKey(&myRegKey, valueString.Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 4, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open adapter registry key %ws\n", + valueString.Buffer + )); + + } else { + RetStatus = TRUE; + *Handle = myRegKey; + } + +done: + ExFreePool(valueString.Buffer); + + return RetStatus; +} + + +VOID +CloseIFConfig( + NDIS_HANDLE Handle + ) + +/*++ + + Routine Description: + + Close a per-interface config handle opened via OpenIFConfig(). + + Arguments: + Handle - Handle to be closed. + + Return Value: + + +--*/ + +{ + PAGED_CODE(); + + ZwClose(Handle); +} + + +IPConfigInfo * +IPGetConfig( + void + ) + +/*++ + +Routine Description: + + Provides IP configuration information for the NT environment. + +Arguments: + + None + +Return Value: + + A pointer to a structure containing the configuration information. + +--*/ + +{ + return(IPConfiguration); +} + + +void +IPFreeConfig( + IPConfigInfo *ConfigInfo + ) + +/*++ + +Routine Description: + + Frees the IP configuration structure allocated by IPGetConfig. + +Arguments: + + ConfigInfo - Pointer to the IP configuration information structure to free. + +Return Value: + + None. + +--*/ + +{ + int i; + +#ifndef _PNP_POWER + + NetConfigInfo *netConfiguration; + + + if (IPConfiguration != NULL) { + for (i = 0; i < IPConfiguration->ici_numnets; i++ ) { + netConfiguration = &(IPConfiguration->ici_netinfo[i]); + + if (netConfiguration->nci_driver.Buffer != NULL) { + CTEFreeString(&(netConfiguration->nci_driver)); + } + + if (netConfiguration->nci_configname.Buffer != NULL) { + CTEFreeString(&(netConfiguration->nci_configname)); + } + + if (netConfiguration->nci_reghandle != NULL) { + ZwClose(netConfiguration->nci_reghandle); + } + } + + CTEFreeMem(IPConfiguration->ici_netinfo); + CTEFreeMem(IPConfiguration); + } + +#else // _PNP_POWER + + if (IPConfiguration != NULL) { + CTEFreeMem(IPConfiguration); + } + +#endif // _PNP_POWER + + IPConfiguration = NULL; + + return; +} + +ulong +GetGMTDelta( + void + ) + +/*++ + +Routine Description: + + Returns the offset in milliseconds of the time zone of this machine + from GMT. + +Arguments: + + None. + +Return Value: + + Time in milliseconds between this time zone and GMT. + +--*/ + +{ + LARGE_INTEGER localTime, systemTime; + + // + // Get time zone bias in 100ns. + // + localTime.LowPart = 0; + localTime.HighPart = 0; + ExLocalTimeToSystemTime(&localTime, &systemTime); + + if ((localTime.LowPart != 0) || (localTime.HighPart != 0)) { + localTime = CTEConvert100nsToMilliseconds(systemTime); + } + + ASSERT(localTime.HighPart == 0); + + return(localTime.LowPart); +} + + +ulong +GetTime( + void + ) + +/*++ + +Routine Description: + + Returns the time in milliseconds since midnight. + +Arguments: + + None. + +Return Value: + + Time in milliseconds since midnight. + +--*/ + +{ + LARGE_INTEGER ntTime; + TIME_FIELDS breakdownTime; + ulong returnValue; + + KeQuerySystemTime(&ntTime); + RtlTimeToTimeFields(&ntTime, &breakdownTime); + + returnValue = breakdownTime.Hour * 60; + returnValue = (returnValue + breakdownTime.Minute) * 60; + returnValue = (returnValue + breakdownTime.Second) * 1000; + returnValue = returnValue + breakdownTime.Milliseconds; + + return(returnValue); +} + + +ulong +GetUnique32BitValue( + void + ) + +/*++ + +Routine Description: + + Returns a reasonably unique 32-bit number based on the system clock. + In NT, we take the current system time, convert it to milliseconds, + and return the low 32 bits. + +Arguments: + + None. + +Return Value: + + A reasonably unique 32-bit value. + +--*/ + +{ + LARGE_INTEGER ntTime, tmpTime; + + KeQuerySystemTime(&ntTime); + + tmpTime = CTEConvert100nsToMilliseconds(ntTime); + + return(tmpTime.LowPart); +} + + +uint +UseEtherSNAP( + PNDIS_STRING Name + ) + +/*++ + +Routine Description: + + Determines whether the EtherSNAP protocol should be used on an interface. + +Arguments: + + Name - The device name of the interface in question. + +Return Value: + + Nonzero if SNAP is to be used on the interface. Zero otherwise. + +--*/ + +{ + UNREFERENCED_PARAMETER(Name); + + // + // We currently set this on a global basis. + // + return(ArpUseEtherSnap); +} + + +void +GetAlwaysSourceRoute( + uint *pArpAlwaysSourceRoute, + uint *pIPAlwaysSourceRoute + ) + +/*++ + +Routine Description: + + Determines whether ARP should always turn on source routing in queries. + +Arguments: + + None. + +Return Value: + + Nonzero if source routing is always to be used. Zero otherwise. + +--*/ + +{ + // + // We currently set this on a global basis. + // + *pArpAlwaysSourceRoute = ArpAlwaysSourceRoute; + *pIPAlwaysSourceRoute = IPAlwaysSourceRoute; + return; +} + + +uint +GetArpCacheLife( + void + ) + +/*++ + +Routine Description: + + Get ArpCacheLife in seconds. + +Arguments: + + None. + +Return Value: + + Set to default if not found. + +--*/ + +{ + // + // We currently set this on a global basis. + // + return(ArpCacheLife); +} + + +#define IP_ADDRESS_STRING_LENGTH (16+2) // +2 for double NULL on MULTI_SZ + + +BOOLEAN +IPConvertStringToAddress( + IN PWCHAR AddressString, + OUT PULONG IpAddress + ) + +/*++ + +Routine Description + + This function converts an Internet standard 4-octet dotted decimal + IP address string into a numeric IP address. Unlike inet_addr(), this + routine does not support address strings of less than 4 octets nor does + it support octal and hexadecimal octets. + +Arguments + + AddressString - IP address in dotted decimal notation + IpAddress - Pointer to a variable to hold the resulting address + +Return Value: + + TRUE if the address string was converted. FALSE otherwise. + +--*/ + +{ + UNICODE_STRING unicodeString; + STRING aString; + UCHAR dataBuffer[IP_ADDRESS_STRING_LENGTH]; + NTSTATUS status; + PUCHAR addressPtr, cp, startPointer, endPointer; + ULONG digit, multiplier; + int i; + + + PAGED_CODE(); + + aString.Length = 0; + aString.MaximumLength = IP_ADDRESS_STRING_LENGTH; + aString.Buffer = dataBuffer; + + RtlInitUnicodeString(&unicodeString, AddressString); + + status = RtlUnicodeStringToAnsiString( + &aString, + &unicodeString, + FALSE + ); + + if (!NT_SUCCESS(status)) { + return(FALSE); + } + + *IpAddress = 0; + addressPtr = (PUCHAR) IpAddress; + startPointer = dataBuffer; + endPointer = dataBuffer; + i = 3; + + while (i >= 0) { + // + // Collect the characters up to a '.' or the end of the string. + // + while ((*endPointer != '.') && (*endPointer != '\0')) { + endPointer++; + } + + if (startPointer == endPointer) { + return(FALSE); + } + + // + // Convert the number. + // + + for ( cp = (endPointer - 1), multiplier = 1, digit = 0; + cp >= startPointer; + cp--, multiplier *= 10 + ) { + + if ((*cp < '0') || (*cp > '9') || (multiplier > 100)) { + return(FALSE); + } + + digit += (multiplier * ((ULONG) (*cp - '0'))); + } + + if (digit > 255) { + return(FALSE); + } + + addressPtr[i] = (UCHAR) digit; + + // + // We are finished if we have found and converted 4 octets and have + // no other characters left in the string. + // + if ( (i-- == 0) && + ((*endPointer == '\0') || (*endPointer == ' ')) + ) { + return(TRUE); + } + + if (*endPointer == '\0') { + return(FALSE); + } + + startPointer = ++endPointer; + } + + return(FALSE); +} + + +ULONG +RouteMatch( + IN WCHAR *RouteString, + IN IPAddr Address, + IN IPMask Mask, + OUT IPAddr *DestVal, + OUT IPMask *DestMask, + OUT IPAddr *GateVal, + OUT ULONG *Metric + ) + +/*++ + +Routine Description + + This function checks if a perisitent route should be assigned to + a given interface based on the interface address & mask. + +Arguments + + RouteString - A NULL-terminated route laid out as Dest,Mask,Gate. + Address - The IP address of the interface being processed. + Mask - The subnet mask of the interface being processed. + DestVal - A pointer to the decoded destination IP address. + DestVal - A pointer to the decoded destination subnet mask. + DestVal - A pointer to the decoded destination first hop gateway. + Metric - A pointer to the decoded route metric. + +Return Value: + + The route type, IRE_TYPE_DIRECT or IRE_TYPE_INDIRECT, if the route + should be added to the interface, IRE_TYPE_INVALID otherwise. + +--*/ + +{ +#define ROUTE_SEPARATOR L',' + + WCHAR *labelPtr; + WCHAR *indexPtr = RouteString; + ULONG i; + UNICODE_STRING ustring; + NTSTATUS status; + BOOLEAN noMetric = FALSE; + + + PAGED_CODE(); + + // + // The route is laid out in the string as "Dest,Mask,Gateway,Metric". + // The metric may not be there if this system was upgraded from + // NT 3.51. + // + // Parse the string and convert each label. + // + + for (i=0; i<4; i++) { + + labelPtr = indexPtr; + + while (1) { + + if (*indexPtr == UNICODE_NULL) { + if ((i < 2) || (indexPtr == labelPtr)) { + return(IRE_TYPE_INVALID); + } + + if (i == 2) { + // + // Old route - no metric. + // + noMetric = TRUE; + } + + break; + } + + if (*indexPtr == ROUTE_SEPARATOR) { + *indexPtr = UNICODE_NULL; + break; + } + + indexPtr++; + } + + switch(i) { + case 0: + if (!IPConvertStringToAddress(labelPtr, DestVal)) { + return(IRE_TYPE_INVALID); + } + break; + + case 1: + if (!IPConvertStringToAddress(labelPtr, DestMask)) { + return(IRE_TYPE_INVALID); + } + break; + + case 2: + if (!IPConvertStringToAddress(labelPtr, GateVal)) { + return(IRE_TYPE_INVALID); + } + break; + + case 3: + RtlInitUnicodeString(&ustring, labelPtr); + + status = RtlUnicodeStringToInteger( + &ustring, + 0, + Metric + ); + + if (!NT_SUCCESS(status)) { + return(IRE_TYPE_INVALID); + } + + break; + + default: + ASSERT(0); + return(IRE_TYPE_INVALID); + } + + if (noMetric) { + // + // Default to 1. + // + *Metric = 1; + break; + } + + indexPtr++; + } + + if (IP_ADDR_EQUAL(*GateVal, Address)) { + return(IRE_TYPE_DIRECT); + } + + if ( IP_ADDR_EQUAL((*GateVal & Mask), (Address & Mask)) ) { + return(IRE_TYPE_INDIRECT); + } + + return(IRE_TYPE_INVALID); +} + +ULONG +GetCurrentRouteTable( + IPRouteEntry **ppRouteTable + ) +/*++ + Routine Description + Allocates memory from non paged pool and fills it with the current route table + The caller must free the memory to non-paged pool + + Arguments + ppRouteTable Pointer to pointer to array of routes + + Return Value: + Count of routes in the table. If memory is allocated, *ppRouteTable will be non NULL + +--*/ +{ + ULONG ulTableCount,ulRouteCount; + uint uiValid,uiDataLeft; + IPRouteEntry routeEntry; + CTELockHandle Handle; + UCHAR ucContext[CONTEXT_SIZE]; + +#define ROUTE_TABLE_OVERFLOW 20 // This MUST NOT be zero + + ulTableCount = IPSInfo.ipsi_numroutes + ROUTE_TABLE_OVERFLOW; + + *ppRouteTable = ExAllocatePoolWithTag(NonPagedPool, + ulTableCount * sizeof(IPRouteEntry), + 'pI'); + ulRouteCount = 0; + + if(*ppRouteTable == NULL) + { + TCPTRACE(("IP: Couldnt allocate memory for route table\n")); + } + else + { + CTEGetLock(&RouteTableLock, &Handle); + + RtlZeroMemory((PVOID)ucContext,CONTEXT_SIZE); + + uiDataLeft = RTValidateContext((PVOID)ucContext, &uiValid); + + if(!uiValid) + { + CTEFreeLock(&RouteTableLock, Handle); + } + else + { + while(uiDataLeft) + { + if(ulRouteCount < ulTableCount) + { + uiDataLeft = RTReadNext((PVOID)ucContext, &routeEntry); + + (*ppRouteTable)[ulRouteCount++] = routeEntry; + } + else + { + TCPTRACE(("IP: Couldnt read out all routes. Increase ROUTE_TABLE_OVERFLOW\n")); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + } + } + + return ulRouteCount; +} + + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ) + +/*++ + +Routine Description + + Adds persistent routes that match an interface. The routes are read + from a list in the registry. + +Arguments + + Address - The address of the new interface + Mask - The subnet mask of the new interface. + IFIndex - The index of the new interface. + +Return Value: + + None. + +--*/ + +{ +#define ROUTE_DATA_STRING_SIZE (51 * sizeof(WCHAR)) +#define BASIC_INFO_SIZE ( sizeof(KEY_VALUE_BASIC_INFORMATION) - \ + sizeof(WCHAR) + ROUTE_DATA_STRING_SIZE ) + IPAddr destVal; + IPMask destMask; + IPAddr gateVal; + ULONG metric; + ULONG enumIndex = 0; + UCHAR workbuf[BASIC_INFO_SIZE]; + PKEY_VALUE_BASIC_INFORMATION basicInfo = + (PKEY_VALUE_BASIC_INFORMATION) workbuf; + ULONG resultLength; + HANDLE regKey; + WCHAR IPRoutesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\PersistentRoutes"; + TDIObjectID id; + IPRouteEntry routeEntry,*pRouteTable; + ULONG ulRouteCount,i; + NTSTATUS status; + + PAGED_CODE(); + + pRouteTable = NULL; + + ulRouteCount = GetCurrentRouteTable(&pRouteTable); + + id.toi_entity.tei_entity = CL_NL_ENTITY; + id.toi_entity.tei_instance = 0; + id.toi_class = INFO_CLASS_PROTOCOL; + id.toi_type = INFO_TYPE_PROVIDER; + id.toi_id = IP_MIB_RTTABLE_ENTRY_ID; + + routeEntry.ire_index = IFIndex; + routeEntry.ire_metric2 = (ULONG) -1; + routeEntry.ire_metric3 = (ULONG) -1; + routeEntry.ire_metric4 = (ULONG) -1; + routeEntry.ire_proto = IRE_PROTO_LOCAL; + routeEntry.ire_age = 0; + routeEntry.ire_metric5 = (ULONG) -1; + + status = OpenRegKey(®Key, IPRoutesRegistryKey); + + if (NT_SUCCESS(status)) { + ULONG type; + + do { + status = ZwEnumerateValueKey( + regKey, + enumIndex, + KeyValueBasicInformation, + basicInfo, + BASIC_INFO_SIZE - sizeof(WCHAR), + &resultLength + ); + + if (!NT_SUCCESS(status)) { + if (status == STATUS_BUFFER_OVERFLOW) { + continue; + } + + break; + } + + if (basicInfo->Type != REG_SZ) { + continue; + } + + // + // Ensure NULL termination + // + basicInfo->Name[basicInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL; + basicInfo->NameLength += sizeof(WCHAR); + + type = RouteMatch( + basicInfo->Name, + Address, + Mask, + &destVal, + &destMask, + &gateVal, + &metric + ); + + if (type != IRE_TYPE_INVALID) + { + long setStatus; + ULONG ulFound; + + routeEntry.ire_dest = net_long(destVal), + routeEntry.ire_mask = net_long(destMask), + routeEntry.ire_nexthop = net_long(gateVal); + routeEntry.ire_type = type; + routeEntry.ire_metric1 = metric; + + ulFound = FALSE; + + for(i = 0; i < ulRouteCount; i++) + { + if((routeEntry.ire_dest == pRouteTable[i].ire_dest) && + (routeEntry.ire_mask == pRouteTable[i].ire_mask)) + { + ulFound = TRUE; + + break; + } + } + + if(!ulFound) + { + + setStatus = IPSetInfo( + &id, + &routeEntry, + sizeof(IPRouteEntry) + ); +#if DBG + if (setStatus != IP_SUCCESS) + { + TCPTRACE(( + "IP: set of sticky route [%x, %x, %x] failed, status %d\n", + destVal, + destMask, + gateVal, + metric, + setStatus + )); + } +#endif // DBG + } + + } + + } while (++enumIndex); + + ZwClose(regKey); + } + + if(pRouteTable) + { + ExFreePool(pRouteTable); + } +} + diff --git a/private/ntos/tdi/tcpip/ip/ntirp.c b/private/ntos/tdi/tcpip/ip/ntirp.c new file mode 100644 index 000000000..3dd34565b --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntirp.c @@ -0,0 +1,1458 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntirp.c + +Abstract: + + NT specific routines for dispatching and handling IRPs. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include "icmp.h" +#include +#include +#include + + +// +// Local structures. +// +typedef struct pending_irp { + LIST_ENTRY Linkage; + PIRP Irp; + PFILE_OBJECT FileObject; + PVOID Context; +} PENDING_IRP, *PPENDING_IRP; + + +// +// Global variables +// +LIST_ENTRY PendingEchoList; +LIST_ENTRY PendingIPSetNTEAddrList; + + +// +// External prototypes +// +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ); + +ulong +ICMPEchoComplete( + EchoControl *ControlBlock, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ); + +IP_STATUS +IPSetNTEAddr( + uint Index, + IPAddr Addr, + IPMask Mask, + SetAddrControl *ControlBlock, + SetAddrRtn Callback + ); + +uint +IPAddDynamicNTE( + ushort InterfaceContext, + IPAddr NewAddr, + IPMask NewMask, + ushort *NTEContext, + ulong *NTEInstance + ); + +uint +IPDeleteDynamicNTE( + ushort NTEContext + ); + +uint +IPGetNTEInfo( + ushort NTEContext, + ulong *NTEInstance, + IPAddr *Address, + IPMask *SubnetMask, + ushort *NTEFlags + ); + +uint +SetDHCPNTE( + uint Context + ); + +// +// Local prototypes +// +NTSTATUS +IPDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPDispatchInternalDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPCreate( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPCleanup( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPClose( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +DispatchEchoRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +void +CompleteEchoRequest( + void *Context, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ); + +NTSTATUS +DispatchIPSetNTEAddrRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +void +CompleteIPSetNTEAddrRequest( + void *Context, + IP_STATUS Status + ); + + +#ifdef _PNP_POWER +extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo) ; +extern void IPDelInterface(void *Context) ; +#endif + + +// +// All of this code is pageable. +// +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, IPDispatch) +#pragma alloc_text(PAGE, IPDispatchDeviceControl) +#pragma alloc_text(PAGE, IPDispatchInternalDeviceControl) +#pragma alloc_text(PAGE, IPCreate) +#pragma alloc_text(PAGE, IPClose) +#pragma alloc_text(PAGE, DispatchEchoRequest) + +#endif // ALLOC_PRAGMA + + +// +// Dispatch function definitions +// +NTSTATUS +IPDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for IP. + +Arguments: + + DeviceObject - Pointer to device object for target device + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + NTSTATUS status; + + + UNREFERENCED_PARAMETER(DeviceObject); + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + switch (irpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + return IPDispatchDeviceControl(Irp, irpSp); + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return IPDispatchDeviceControl(Irp, irpSp); + + case IRP_MJ_CREATE: + status = IPCreate(Irp, irpSp); + break; + + case IRP_MJ_CLEANUP: + status = IPCleanup(Irp, irpSp); + break; + + case IRP_MJ_CLOSE: + status = IPClose(Irp, irpSp); + break; + + default: + CTEPrint("IPDispatch: Invalid major function "); + CTEPrintNum(irpSp->MajorFunction ); + CTEPrintCRLF(); + status = STATUS_NOT_IMPLEMENTED; + break; + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); + +} // IPDispatch + + +NTSTATUS +IPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + ULONG code; + + + PAGED_CODE(); + + Irp->IoStatus.Information = 0; + + code = IrpSp->Parameters.DeviceIoControl.IoControlCode; + + switch(code) { + + case IOCTL_ICMP_ECHO_REQUEST: + return(DispatchEchoRequest(Irp, IrpSp)); + + case IOCTL_IP_SET_ADDRESS: + return(DispatchIPSetNTEAddrRequest(Irp, IrpSp)); + + case IOCTL_IP_ADD_NTE: + { + PIP_ADD_NTE_REQUEST request; + PIP_ADD_NTE_RESPONSE response; + BOOLEAN retval; + + + request = Irp->AssociatedIrp.SystemBuffer; + response = (PIP_ADD_NTE_RESPONSE) request; + + // + // Validate input parameters + // + if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_ADD_NTE_REQUEST) + ) + && + (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(IP_ADD_NTE_RESPONSE)) + + ) + { + retval = IPAddDynamicNTE( + request->InterfaceContext, + request->Address, + request->SubnetMask, + &(response->Context), + &(response->Instance) + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + Irp->IoStatus.Information = sizeof(IP_ADD_NTE_RESPONSE); + status = STATUS_SUCCESS; + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_DELETE_NTE: + { + PIP_DELETE_NTE_REQUEST request; + BOOLEAN retval; + + + request = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate input parameters + // + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_DELETE_NTE_REQUEST) + ) + { + retval = IPDeleteDynamicNTE( + request->Context + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_GET_NTE_INFO: + { + PIP_GET_NTE_INFO_REQUEST request; + PIP_GET_NTE_INFO_RESPONSE response; + BOOLEAN retval; + ushort nteFlags; + + + request = Irp->AssociatedIrp.SystemBuffer; + response = (PIP_GET_NTE_INFO_RESPONSE) request; + + // + // Validate input parameters + // + if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_GET_NTE_INFO_REQUEST) + ) + && + (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(IP_GET_NTE_INFO_RESPONSE)) + + ) + { + retval = IPGetNTEInfo( + request->Context, + &(response->Instance), + &(response->Address), + &(response->SubnetMask), + &nteFlags + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + Irp->IoStatus.Information = + sizeof(IP_GET_NTE_INFO_RESPONSE); + response->Flags = 0; + + if (nteFlags & NTE_DYNAMIC) { + response->Flags |= IP_NTE_DYNAMIC; + } + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_SET_DHCP_INTERFACE: + { + PIP_SET_DHCP_INTERFACE_REQUEST request; + BOOLEAN retval; + + request = Irp->AssociatedIrp.SystemBuffer; + retval = SetDHCPNTE( + request->Context + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_IF_CONTEXT: + { + PIP_SET_IF_CONTEXT_INFO info; + + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetIFContext(info->Index, info->Context); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_FILTER_POINTER: + { + PIP_SET_FILTER_HOOK_INFO info; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetFilterPtr(info->FilterPtr); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_MAP_ROUTE_POINTER: + { + PIP_SET_MAP_ROUTE_HOOK_INFO info; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetMapRoutePtr(info->MapRoutePtr); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + +#ifdef _PNP_POWER + + case IOCTL_IP_GET_PNP_ARP_POINTERS: + { + PIP_GET_PNP_ARP_POINTERS info = (PIP_GET_PNP_ARP_POINTERS) Irp->AssociatedIrp.SystemBuffer; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info->IPAddInterface = (IPAddInterfacePtr)IPAddInterface ; + info->IPDelInterface = (IPDelInterfacePtr)IPDelInterface ; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(IP_GET_PNP_ARP_POINTERS); + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + return STATUS_SUCCESS;; + + } + break; +#endif + + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != IP_PENDING) { + Irp->IoStatus.Status = status; + // Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + return status; + +} // IPDispatchDeviceControl + +NTSTATUS +IPDispatchInternalDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + + + PAGED_CODE(); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return status; + +} // IPDispatchDeviceControl + + +NTSTATUS +IPCreate( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAGED_CODE(); + + return(STATUS_SUCCESS); + +} // IPCreate + + +NTSTATUS +IPCleanup( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PPENDING_IRP pendingIrp; + PLIST_ENTRY entry, nextEntry; + KIRQL oldIrql; + LIST_ENTRY completeList; + PIRP cancelledIrp; + + + InitializeListHead(&completeList); + + // + // Collect all of the pending IRPs on this file object. + // + IoAcquireCancelSpinLock(&oldIrql); + + entry = PendingEchoList.Flink; + + while ( entry != &PendingEchoList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + + if (pendingIrp->FileObject == IrpSp->FileObject) { + nextEntry = entry->Flink; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + InsertTailList(&completeList, &(pendingIrp->Linkage)); + entry = nextEntry; + } + else { + entry = entry->Flink; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + // + // Complete them. + // + entry = completeList.Flink; + + while ( entry != &completeList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + cancelledIrp = pendingIrp->Irp; + entry = entry->Flink; + + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + cancelledIrp->IoStatus.Information = 0; + cancelledIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT); + } + + InitializeListHead(&completeList); + + // + // Collect all of the pending IRPs on this file object. + // + IoAcquireCancelSpinLock(&oldIrql); + + entry = PendingIPSetNTEAddrList.Flink; + + while ( entry != &PendingIPSetNTEAddrList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + + if (pendingIrp->FileObject == IrpSp->FileObject) { + nextEntry = entry->Flink; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + InsertTailList(&completeList, &(pendingIrp->Linkage)); + entry = nextEntry; + } + else { + entry = entry->Flink; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + // + // Complete them. + // + entry = completeList.Flink; + + while ( entry != &completeList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + cancelledIrp = pendingIrp->Irp; + entry = entry->Flink; + + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + cancelledIrp->IoStatus.Information = 0; + cancelledIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT); + } + + return(STATUS_SUCCESS); + +} // IPCleanup + + +NTSTATUS +IPClose( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAGED_CODE(); + + return(STATUS_SUCCESS); + +} // IPClose + + +// +// ICMP Echo function definitions +// +VOID +CancelEchoRequest( + IN PDEVICE_OBJECT Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels an outstanding Echo request Irp. + +Arguments: + + Device - The device on which the request was issued. + Irp - Pointer to I/O request packet to cancel. + +Return Value: + + None. + +Notes: + + This function is called with cancel spinlock held. It must be + released before the function returns. + + The echo control block associated with this request cannot be + freed until the request completes. The completion routine will + free it. + +--*/ + +{ + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + + + for ( entry = PendingEchoList.Flink; + entry != &PendingEchoList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Irp == Irp) { + pendingIrp = item; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + break; + } + } + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (pendingIrp != NULL) { + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + return; + +} // CancelEchoRequest + +// +// IP Set Addr function definitions +// +VOID +CancelIPSetNTEAddrRequest( + IN PDEVICE_OBJECT Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels an outstanding IP Set Addr request Irp. + +Arguments: + + Device - The device on which the request was issued. + Irp - Pointer to I/O request packet to cancel. + +Return Value: + + None. + +Notes: + + This function is called with cancel spinlock held. It must be + released before the function returns. + + The IP Set Addr control block associated with this request cannot be + freed until the request completes. The completion routine will + free it. + +--*/ + +{ + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + + + for ( entry = PendingIPSetNTEAddrList.Flink; + entry != &PendingIPSetNTEAddrList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Irp == Irp) { + pendingIrp = item; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + break; + } + } + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (pendingIrp != NULL) { + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + return; + +} // CancelIPSetNTEAddrRequest + + +void +CompleteEchoRequest( + void *Context, + IP_STATUS Status, + void *Data, OPTIONAL + uint DataSize, + struct IPOptInfo *OptionInfo OPTIONAL + ) + +/*++ + +Routine Description: + + Handles the completion of an ICMP Echo request + +Arguments: + + Context - Pointer to the EchoControl structure for this request. + Status - The IP status of the transmission. + Data - A pointer to data returned in the echo reply. + DataSize - The length of the returned data. + OptionInfo - A pointer to the IP options in the echo reply. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + EchoControl *controlBlock; + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + ULONG bytesReturned; + + + controlBlock = (EchoControl *) Context; + + // + // Find the echo request IRP on the pending list. + // + IoAcquireCancelSpinLock(&oldIrql); + + for ( entry = PendingEchoList.Flink; + entry != &PendingEchoList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Context == controlBlock) { + pendingIrp = item; + irp = pendingIrp->Irp; + IoSetCancelRoutine(irp, NULL); + RemoveEntryList(entry); + break; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + if (pendingIrp == NULL) { + // + // IRP must have been cancelled. PENDING_IRP struct + // was freed by cancel routine. Free control block. + // + CTEFreeMem(controlBlock); + return; + } + + irpSp = IoGetCurrentIrpStackLocation(irp); + + bytesReturned = ICMPEchoComplete( + controlBlock, + Status, + Data, + DataSize, + OptionInfo + ); + + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + // + // Complete the IRP. + // + irp->IoStatus.Information = (ULONG) bytesReturned; + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + return; + +} // CompleteEchoRequest + +void +CompleteIPSetNTEAddrRequest( + void *Context, + IP_STATUS Status + ) + +/*++ + +Routine Description: + + Handles the completion of an IP Set Addr request + +Arguments: + + Context - Pointer to the SetAddrControl structure for this request. + Status - The IP status of the transmission. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + SetAddrControl *controlBlock; + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + ULONG bytesReturned; + + + controlBlock = (SetAddrControl *) Context; + + // + // Find the echo request IRP on the pending list. + // + IoAcquireCancelSpinLock(&oldIrql); + + for ( entry = PendingIPSetNTEAddrList.Flink; + entry != &PendingIPSetNTEAddrList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Context == controlBlock) { + pendingIrp = item; + irp = pendingIrp->Irp; + IoSetCancelRoutine(irp, NULL); + RemoveEntryList(entry); + break; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + if (pendingIrp == NULL) { + // + // IRP must have been cancelled. PENDING_IRP struct + // was freed by cancel routine. Free control block. + // + CTEFreeMem(controlBlock); + return; + } + + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + // + // Complete the IRP. + // + irp->IoStatus.Information = 0; + if (Status == IP_SUCCESS) { + irp->IoStatus.Status = STATUS_SUCCESS; + } else { + irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + } + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + return; + +} // CompleteIPSetNTEAddrRequest + + +BOOLEAN +PrepareEchoIrpForCancel( + PIRP Irp, + PPENDING_IRP PendingIrp + ) +/*++ + +Routine Description: + + Prepares an Echo IRP for cancellation. + +Arguments: + + Irp - Pointer to I/O request packet to initialize for cancellation. + PendingIrp - Pointer to the PENDING_IRP structure for this IRP. + +Return Value: + + TRUE if the IRP was cancelled before this routine was called. + FALSE otherwise. + +--*/ + +{ + BOOLEAN cancelled = TRUE; + KIRQL oldIrql; + + + IoAcquireCancelSpinLock(&oldIrql); + + ASSERT(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + IoSetCancelRoutine(Irp, CancelEchoRequest); + InsertTailList(&PendingEchoList, &(PendingIrp->Linkage)); + cancelled = FALSE; + } + + IoReleaseCancelSpinLock(oldIrql); + + return(cancelled); + +} // PrepareEchoIrpForCancel + +BOOLEAN +PrepareIPSetNTEAddrIrpForCancel( + PIRP Irp, + PPENDING_IRP PendingIrp + ) +/*++ + +Routine Description: + + Prepares an IPSetNTEAddr IRP for cancellation. + +Arguments: + + Irp - Pointer to I/O request packet to initialize for cancellation. + PendingIrp - Pointer to the PENDING_IRP structure for this IRP. + +Return Value: + + TRUE if the IRP was cancelled before this routine was called. + FALSE otherwise. + +--*/ + +{ + BOOLEAN cancelled = TRUE; + KIRQL oldIrql; + + + IoAcquireCancelSpinLock(&oldIrql); + + ASSERT(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + IoSetCancelRoutine(Irp, CancelIPSetNTEAddrRequest); + InsertTailList(&PendingIPSetNTEAddrList, &(PendingIrp->Linkage)); + cancelled = FALSE; + } + + IoReleaseCancelSpinLock(oldIrql); + + return(cancelled); + +} // PrepareIPSetNTEAddrIrpForCancel + + +NTSTATUS +DispatchEchoRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes an ICMP request. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether NT-specific processing of the request was + successful. The status of the actual request is returned in + the request buffers. + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + IP_STATUS ipStatus; + PPENDING_IRP pendingIrp; + EchoControl *controlBlock; + PICMP_ECHO_REPLY replyBuffer; + BOOLEAN cancelled; + + + PAGED_CODE(); + + pendingIrp = CTEAllocMem(sizeof(PENDING_IRP)); + + if (pendingIrp == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto echo_error; + } + + controlBlock = CTEAllocMem(sizeof(EchoControl)); + + if (controlBlock == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + CTEFreeMem(pendingIrp); + goto echo_error; + } + + pendingIrp->Irp = Irp; + pendingIrp->FileObject = IrpSp->FileObject; + pendingIrp->Context = controlBlock; + + controlBlock->ec_starttime = CTESystemUpTime(); + controlBlock->ec_replybuf = Irp->AssociatedIrp.SystemBuffer; + controlBlock->ec_replybuflen = + IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + IoMarkIrpPending(Irp); + + cancelled = PrepareEchoIrpForCancel(Irp, pendingIrp); + + if (!cancelled) { + ipStatus = ICMPEchoRequest( + Irp->AssociatedIrp.SystemBuffer, // request buf + IrpSp->Parameters.DeviceIoControl.InputBufferLength, // request len + controlBlock, // echo ctrl + CompleteEchoRequest // cmplt rtn + ); + + if (ipStatus == IP_PENDING) { + ntStatus = STATUS_PENDING; + } + else { + ASSERT(ipStatus != IP_SUCCESS); + + // + // An internal error of some kind occurred. Complete the + // request. + // + CompleteEchoRequest( + controlBlock, + ipStatus, + NULL, + 0, + NULL + ); + + // + // The NT ioctl was successful, even if the request failed. The + // request status was passed back in the first reply block. + // + ntStatus = STATUS_SUCCESS; + } + + return(ntStatus); + } + + // + // Irp has already been cancelled. + // + ntStatus = STATUS_CANCELLED; + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + +echo_error: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(ntStatus); + +} // DispatchEchoRequest + + +NTSTATUS +DispatchIPSetNTEAddrRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes an IP Set Addr request. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether NT-specific processing of the request was + successful. The status of the actual request is returned in + the request buffers. + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + IP_STATUS ipStatus; + PPENDING_IRP pendingIrp; + SetAddrControl *controlBlock; + BOOLEAN cancelled; + + + PAGED_CODE(); + + pendingIrp = CTEAllocMem(sizeof(PENDING_IRP)); + + if (pendingIrp == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto setnteaddr_error; + } + + controlBlock = CTEAllocMem(sizeof(SetAddrControl)); + + if (controlBlock == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + CTEFreeMem(pendingIrp); + goto setnteaddr_error; + } + + pendingIrp->Irp = Irp; + pendingIrp->FileObject = IrpSp->FileObject; + pendingIrp->Context = controlBlock; + + IoMarkIrpPending(Irp); + + cancelled = PrepareIPSetNTEAddrIrpForCancel(Irp, pendingIrp); + + if (!cancelled) { + + PIP_SET_ADDRESS_REQUEST request; + + request = Irp->AssociatedIrp.SystemBuffer; + ipStatus = IPSetNTEAddr( + request->Context, + request->Address, + request->SubnetMask, + controlBlock, + CompleteIPSetNTEAddrRequest + ); + + if (ipStatus == IP_PENDING) { + ntStatus = STATUS_PENDING; + } + else { + + // + // A request completed which did not pend. + // + CompleteIPSetNTEAddrRequest( + controlBlock, + ipStatus + ); + + // + // The NT ioctl was successful, even if the request failed. The + // request status was passed back in the first reply block. + // + ntStatus = STATUS_SUCCESS; + } + + return(ntStatus); + } + + // + // Irp has already been cancelled. + // + ntStatus = STATUS_CANCELLED; + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + +setnteaddr_error: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(ntStatus); + +} // DispatchIPSetNTEAddrRequest diff --git a/private/ntos/tdi/tcpip/ip/ntreg.c b/private/ntos/tdi/tcpip/ip/ntreg.c new file mode 100644 index 000000000..e9c53c208 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntreg.c @@ -0,0 +1,672 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntreg.c + +Abstract: + + This source file contains the routines to to access the NT Registry for + configuration info. + + +Author: + + Mike Massa (mikemas) September 3, 1993 + + (taken from routines by jballard) + +Revision History: + +--*/ + + +#include +#include +#include + + +#define WORK_BUFFER_SIZE 512 + + +// +// Local function prototypes +// +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ); + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +GetRegStringValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PKEY_VALUE_PARTIAL_INFORMATION *ValueData, + PUSHORT ValueSize + ); + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ); + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ); + +VOID +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ); + + +#ifdef ALLOC_PRAGMA +// +// All of the init code can be discarded +// + +#ifndef _PNP_POWER + +#pragma alloc_text(INIT, GetRegDWORDValue) +#pragma alloc_text(INIT, SetRegDWORDValue) +#pragma alloc_text(INIT, InitRegDWORDParameter) + +#else // _PNP_POWER + +#pragma alloc_text(PAGE, GetRegDWORDValue) +#pragma alloc_text(PAGE, SetRegDWORDValue) +#pragma alloc_text(PAGE, InitRegDWORDParameter) + +#endif // _PNP_POWER + +// +// This code is pagable. +// +#pragma alloc_text(PAGE, OpenRegKey) +#pragma alloc_text(PAGE, GetRegStringValue) +#pragma alloc_text(PAGE, GetRegSZValue) +#pragma alloc_text(PAGE, GetRegMultiSZValue) + + +#endif // ALLOC_PRAGMA + + + +// +// Function definitions +// +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ) + +/*++ + +Routine Description: + + Opens a Registry key and returns a handle to it. + +Arguments: + + HandlePtr - The varible into which to write the opened handle. + KeyName - The name of the Registry key to open. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING UKeyName; + + + PAGED_CODE(); + + RtlInitUnicodeString(&UKeyName, KeyName); + + memset(&ObjectAttributes, 0, sizeof(OBJECT_ATTRIBUTES)); + InitializeObjectAttributes(&ObjectAttributes, + &UKeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(HandlePtr, + KEY_READ, + &ObjectAttributes); + + return Status; +} + + + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ) + +/*++ + +Routine Description: + + Reads a REG_DWORD value from the registry into the supplied variable. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - The variable into which to read the data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_FULL_INFORMATION keyValueFullInformation; + UCHAR keybuf[WORK_BUFFER_SIZE]; + UNICODE_STRING UValueName; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + RtlInitUnicodeString(&UValueName, ValueName); + + keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf; + RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation)); + + + status = ZwQueryValueKey(KeyHandle, + &UValueName, + KeyValueFullInformation, + keyValueFullInformation, + WORK_BUFFER_SIZE, + &resultLength); + + if (NT_SUCCESS(status)) { + if (keyValueFullInformation->Type != REG_DWORD) { + status = STATUS_INVALID_PARAMETER_MIX; + } else { + *ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation + + keyValueFullInformation->DataOffset)); + } + } + + return status; +} + + + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ) + +/*++ + +Routine Description: + + Writes the contents of a variable to a REG_DWORD value. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to write. + ValueName - The name of the value to write. + ValueData - The variable from which to write the data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING UValueName; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + RtlInitUnicodeString(&UValueName, ValueName); + + status = ZwSetValueKey(KeyHandle, + &UValueName, + 0, + REG_DWORD, + ValueData, + sizeof(ULONG)); + + return status; +} + + + +NTSTATUS +GetRegStringValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PKEY_VALUE_PARTIAL_INFORMATION *ValueData, + PUSHORT ValueSize + ) + +/*++ + +Routine Description: + + Reads a REG_*_SZ string value from the Registry into the supplied + key value buffer. If the buffer string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination for the read data. + ValueSize - Size of the ValueData buffer. Updated on output. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + RtlInitUnicodeString(&UValueName, ValueName); + + status = ZwQueryValueKey( + KeyHandle, + &UValueName, + KeyValuePartialInformation, + *ValueData, + (ULONG) *ValueSize, + &resultLength + ); + + if ( (status == STATUS_BUFFER_OVERFLOW) || + (status == STATUS_BUFFER_TOO_SMALL) + ) + { + PVOID temp; + + // + // Free the old buffer and allocate a new one of the + // appropriate size. + // + + ASSERT(resultLength > (ULONG) *ValueSize); + + if (resultLength <= 0xFFFF) { + + temp = CTEAllocMem(resultLength); + + if (temp != NULL) { + + if (*ValueData != NULL) { + CTEFreeMem(*ValueData); + } + + *ValueData = temp; + *ValueSize = (USHORT) resultLength; + + status = ZwQueryValueKey(KeyHandle, + &UValueName, + KeyValuePartialInformation, + *ValueData, + *ValueSize, + &resultLength + ); + + ASSERT( (status != STATUS_BUFFER_OVERFLOW) && + (status != STATUS_BUFFER_TOO_SMALL) + ); + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + return(status); +} + + + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ) + +/*++ + +Routine Description: + + Reads a REG_MULTI_SZ string value from the Registry into the supplied + Unicode string. If the Unicode string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination Unicode string for the value data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + ValueData->Length = 0; + + status = GetRegStringValue( + KeyHandle, + ValueName, + (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer), + &(ValueData->MaximumLength) + ); + + if (NT_SUCCESS(status)) { + + keyValuePartialInformation = + (PKEY_VALUE_PARTIAL_INFORMATION) ValueData->Buffer; + + if (keyValuePartialInformation->Type == REG_MULTI_SZ) { + + ValueData->Length = (USHORT) + keyValuePartialInformation->DataLength; + + RtlCopyMemory( + ValueData->Buffer, + &(keyValuePartialInformation->Data), + ValueData->Length + ); + } + else { + status = STATUS_INVALID_PARAMETER_MIX; + } + } + + return status; + +} // GetRegMultiSZValue + + + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ) + +/*++ + +Routine Description: + + Reads a REG_SZ string value from the Registry into the supplied + Unicode string. If the Unicode string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination Unicode string for the value data. + ValueType - On return, contains the Registry type of the value read. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + ValueData->Length = 0; + + status = GetRegStringValue( + KeyHandle, + ValueName, + (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer), + &(ValueData->MaximumLength) + ); + + if (NT_SUCCESS(status)) { + + keyValuePartialInformation = + (PKEY_VALUE_PARTIAL_INFORMATION)ValueData->Buffer; + + if ((keyValuePartialInformation->Type == REG_SZ) || + (keyValuePartialInformation->Type == REG_EXPAND_SZ) + ) + { + WCHAR *src; + WCHAR *dst; + ULONG dataLength; + + + *ValueType = keyValuePartialInformation->Type; + dataLength = keyValuePartialInformation->DataLength; + + ASSERT(dataLength <= ValueData->MaximumLength); + + dst = ValueData->Buffer; + src = (PWCHAR) &(keyValuePartialInformation->Data); + + while (ValueData->Length <= dataLength) { + + if ( (*dst++ = *src++) == UNICODE_NULL ) { + break; + } + + ValueData->Length += sizeof(WCHAR); + } + + if (ValueData->Length < (ValueData->MaximumLength - 1)) { + ValueData->Buffer[ValueData->Length/sizeof(WCHAR)] = + UNICODE_NULL; + } + } + else { + status = STATUS_INVALID_PARAMETER_MIX; + } + } + + return status; +} + + + +VOID +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ) + +/*++ + +Routine Description: + + Reads a REG_DWORD parameter from the Registry into a variable. If the + read fails, the variable is initialized to a default. + +Arguments: + + RegKey - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + Value - Destination variable into which to read the data. + DefaultValue - Default to assign if the read fails. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + status = GetRegDWORDValue( + RegKey, + ValueName, + Value + ); + + if (!NT_SUCCESS(status)) { + // + // These registry parameters override the defaults, so their + // absence is not an error. + // + *Value = DefaultValue; + } + + return; +} + + +PWCHAR +EnumRegMultiSz( + IN PWCHAR MszString, + IN ULONG MszStringLength, + IN ULONG StringIndex + ) +/*++ + + Routine Description: + + Parses a REG_MULTI_SZ string and returns the specified substring. + + Arguments: + + MszString - A pointer to the REG_MULTI_SZ string. + + MszStringLength - The length of the REG_MULTI_SZ string, including the + terminating null character. + + StringIndex - Index number of the substring to return. Specifiying + index 0 retrieves the first substring. + + Return Value: + + A pointer to the specified substring. + + Notes: + + This code is called at raised IRQL. It is not pageable. + +--*/ +{ + PWCHAR string = MszString; + + if ( MszStringLength < (2 * sizeof(WCHAR)) ) { + return(NULL); + } + + // + // Find the start of the desired string. + // + while (StringIndex) { + + while (MszStringLength >= sizeof(WCHAR)) { + MszStringLength -= sizeof(WCHAR); + + if (*string++ == UNICODE_NULL) { + break; + } + } + + // + // Check for index out of range. + // + if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) { + return(NULL); + } + + StringIndex--; + } + + if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) { + return(NULL); + } + + return(string); +} + + diff --git a/private/ntos/tdi/tcpip/ip/sources.inc b/private/ntos/tdi/tcpip/ip/sources.inc new file mode 100644 index 000000000..87a099a83 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/sources.inc @@ -0,0 +1,59 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ip + +NTPROFILEINPUT=yes + +TARGETNAME=ip +TARGETPATH=obj +TARGETTYPE=LIBRARY + +TARGETLIBS= + +INCLUDES=..\..\..\..\inc;..\..\..\..\..\inc;..\..\h + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -D_PNP_POWER -DSECFLTR + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\arp.c \ + ..\icmp.c \ + ..\igmp.c \ + ..\info.c \ + ..\init.c \ + ..\iploop.c \ + ..\iprcv.c \ + ..\iproute.c \ + ..\ipstatus.c \ + ..\ipxmit.c \ + ..\ntip.c \ + ..\ntirp.c \ + ..\ntreg.c diff --git a/private/ntos/tdi/tcpip/ip/up/makefile b/private/ntos/tdi/tcpip/ip/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/ip/up/sources b/private/ntos/tdi/tcpip/ip/up/sources new file mode 100644 index 000000000..91036a15a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/up/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +!include ..\sources.inc diff --git a/private/ntos/tdi/tcpip/tcp/addr.c b/private/ntos/tdi/tcpip/tcp/addr.c new file mode 100644 index 000000000..eec1997db --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/addr.c @@ -0,0 +1,2061 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** ADDR.C - TDI address object procedures +// +// This file contains the TDI address object related procedures, +// including TDI open address, TDI close address, etc. +// +// The local address objects are stored in a hash table, protected +// by the AddrObjTableLock. In order to insert or delete from the +// hash table this lock must be held, as well as the address object +// lock. The table lock must always be taken before the object lock. +// + +#include "oscfg.h" +#include "ndis.h" +#include "tdi.h" +#include "tdistat.h" +#include "cxport.h" +#include "ip.h" +#ifdef VXD +#include "tdivxd.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "udp.h" +#include "raw.h" +#ifndef UDP_ONLY +#include "tcp.h" +#include "tcpconn.h" +#else +#include "tcpdeb.h" +#endif +#include "info.h" +#include "tcpinfo.h" +#include "tcpcfg.h" + + +extern IPInfo LocalNetInfo; // Information about the local nets. +EXTERNAL_LOCK(DGSendReqLock) + +#ifndef UDP_ONLY +EXTERNAL_LOCK(ConnTableLock) +#endif + +extern void FreeAORequest(AORequest *Request); + +//* Addess object hash table. +AddrObj *AddrObjTable[AO_TABLE_SIZE]; +AddrObj *LastAO; // one element lookup cache. +DEFINE_LOCK_STRUCTURE(AddrObjTableLock) + +//* AORequest free list. +AORequest *AORequestFree; + +ushort NextUserPort = MIN_USER_PORT; + +#define NUM_AO_REQUEST 5 +DEFINE_LOCK_STRUCTURE(AORequestLock) + +#define AO_HASH(a, p) ((*(uint *)&(a) + (uint)(p)) % AO_TABLE_SIZE) + +#ifdef VXD + +#define DEFAULT_AO_INDEX_SIZE 32 +#define AO_INDEX_INCR 16 // How much to grow by. + +typedef AddrObj *AOIndexTbl[]; + +ushort AOInstance; // Global AO instance count. + +uint AOIndexSize; // # of entries in AOIndex. +uint NextAOIndex; // Next AO index to use. +AOIndexTbl *AOIndex; + +#define AO_INDEX(i) ((i) & 0xffff) +#define AO_INST(i) ((i) >> 16) +#define MAKE_AO_INDEX(s, i) (uint)(((s) << 16) | ((i) & 0xffff)) + +#define MAX_INDEX_SIZE 256 + +#define INVALID_INDEX 0xffffffff + +#endif + + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitAddr(); + +#pragma alloc_text(INIT, InitAddr) + +#endif // ALLOC_PRAGMA +#endif + + +#ifdef VXD + +//* GetIndexedAO - Get an AddrObj from an index. +// +// Called by the UDP routines that access an AO to find the AO by it's +// index. We look it up in the table, and compare the high 16 bits against +// the instance # in the AddrObj. +// +// Input: Index - Index of AddrObj. +// +// Returns: Pointer to AO, or NULL if it's not valid. +// +AddrObj * +GetIndexedAO(uint Index) +{ + AddrObj *AO; + + if (AO_INDEX(Index) < AOIndexSize) { + AO = (*AOIndex)[AO_INDEX(Index)]; + if (AO != NULL && AO->ao_inst == AO_INST(Index)) + return AO; + } + return NULL; + +} + +//* GetAOIndex - Get an index value for an AddrObj. +// +// Called when we're creating an index value for an AddrObj. We go through +// the table, looking for a valid index. If we find one, we'll make an index +// out of it and the AOInstance variable, and bump the instance field. +// Otherwise we may grow the table. +// +// Input: AO - AddrObj to put into table. +// +// Returns: Index to use, or INVALID_INDEX if we can't find one. +// +uint +GetAOIndex(AddrObj *AO) +{ + uint i; // Index variable. + uint CurrentIndex; // Current index being checked. + + for (;;) { + CurrentIndex = NextAOIndex; + for (i = 0; i < AOIndexSize; i++) { + if (CurrentIndex == AOIndexSize) + CurrentIndex = 0; // Set it back to beginning. + + if ((*AOIndex)[CurrentIndex] == NULL) + break; // Found the one we needed. + + CurrentIndex++; + } + + if (i < AOIndexSize) { + uint NewIndex; + + // We came out because we found an empty slot. + AO->ao_inst = AOInstance; + AO->ao_index = (uchar)CurrentIndex; + (*AOIndex)[CurrentIndex] = AO; + NextAOIndex = CurrentIndex + 1; // Bump the next one to look at. + NewIndex = MAKE_AO_INDEX(AOInstance, CurrentIndex); + AOInstance++; + return NewIndex; + } else { + // Couldn't find a slot, so grow the table. + if (AOIndexSize != MAX_INDEX_SIZE) { + // Table isn't already at max size. Try and grow it. + + uint NewIndexSize; + AOIndexTbl *NewIndexTbl, *OldIndexTbl; + + NewIndexSize = MIN(MAX_INDEX_SIZE, AOIndexSize + AO_INDEX_INCR); + NewIndexTbl = CTEAllocMem(sizeof(AddrObj *) * NewIndexSize); + if (NewIndexTbl != NULL) { + // We allocated it. + CTEMemCopy(NewIndexTbl, AOIndex, + AOIndexSize * sizeof(AddrObj *)); + OldIndexTbl = AOIndex; + AOIndex = NewIndexTbl; + AOIndexSize = NewIndexSize; + CTEFreeMem(OldIndexTbl); // Loop around, and try again. + } else + return INVALID_INDEX; + } else + return INVALID_INDEX; + } + } + +} + +#endif + +//* ReadNextAO - Read the next AddrObj in the table. +// +// Called to read the next AddrObj in the table. The needed information +// is derived from the incoming context, which is assumed to be valid. +// We'll copy the information, and then update the context value with +// the next AddrObj to be read. +// +// Input: Context - Poiner to a UDPContext. +// Buffer - Pointer to a UDPEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +ReadNextAO(void *Context, void *Buffer) +{ + UDPContext *UContext = (UDPContext *)Context; + UDPEntry *UEntry = (UDPEntry *)Buffer; + AddrObj *CurrentAO; + uint i; + + CurrentAO = UContext->uc_ao; + CTEStructAssert(CurrentAO, ao); + + UEntry->ue_localaddr = CurrentAO->ao_addr; + UEntry->ue_localport = CurrentAO->ao_port; + + // We've filled it in. Now update the context. + CurrentAO = CurrentAO->ao_next; + if (CurrentAO != NULL && CurrentAO->ao_prot == PROTOCOL_UDP) { + UContext->uc_ao = CurrentAO; + return TRUE; + } else { + // The next AO is NULL, or not a UDP AO. Loop through the AddrObjTable + // looking for a new one. + i = UContext->uc_index; + + for (;;) { + while (CurrentAO != NULL) { + if (CurrentAO->ao_prot == PROTOCOL_UDP) + break; + else + CurrentAO = CurrentAO->ao_next; + } + + if (CurrentAO != NULL) + break; // Get out of for (;;) loop. + + CTEAssert(CurrentAO == NULL); + + // Didn't find one on this chain. Walk down the table, looking + // for the next one. + while (++i < AO_TABLE_SIZE) { + if (AddrObjTable[i] != NULL) { + CurrentAO = AddrObjTable[i]; + break; // Out of while loop. + } + } + + if (i == AO_TABLE_SIZE) + break; // Out of for (;;) loop. + } + + // If we found one, return it. + if (CurrentAO != NULL) { + UContext->uc_ao = CurrentAO; + UContext->uc_index = i; + return TRUE; + } else { + UContext->uc_index = 0; + UContext->uc_ao = NULL; + return FALSE; + } + } + +} + +//* ValidateAOContext - Validate the context for reading the AddrObj table. +// +// Called to start reading the AddrObj table sequentially. We take in +// a context, and if the values are 0 we return information about the +// first AddrObj in the table. Otherwise we make sure that the context value +// is valid, and if it is we return TRUE. +// We assume the caller holds the AddrObjTable lock. +// +// Input: Context - Pointer to a UDPContext. +// Valid - Where to return information about context being +// valid. +// +// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the +// context is valid. +// +uint +ValidateAOContext(void *Context, uint *Valid) +{ + UDPContext *UContext = (UDPContext *)Context; + uint i; + AddrObj *TargetAO; + AddrObj *CurrentAO; + + i = UContext->uc_index; + TargetAO = UContext->uc_ao; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetAO == NULL) { + *Valid = TRUE; + do { + if ((CurrentAO = AddrObjTable[i]) != NULL) { + CTEStructAssert(CurrentAO, ao); + while (CurrentAO != NULL && CurrentAO->ao_prot != PROTOCOL_UDP) + CurrentAO = CurrentAO->ao_next; + + if (CurrentAO != NULL) + break; + } + i++; + } while (i < AO_TABLE_SIZE); + + if (CurrentAO != NULL) { + UContext->uc_index = i; + UContext->uc_ao = CurrentAO; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < AO_TABLE_SIZE) { + CurrentAO = AddrObjTable[i]; + while (CurrentAO != NULL) { + if (CurrentAO == TargetAO) { + if (CurrentAO->ao_prot == PROTOCOL_UDP) { + *Valid = TRUE; + return TRUE; + } + break; + } else { + CurrentAO = CurrentAO->ao_next; + } + } + + } + + // If we get here, we didn't find the matching AddrObj. + *Valid = FALSE; + return FALSE; + + } + +} + + +//** GetAddrObj - Find a local address object. +// +// This is the local address object lookup routine. We take as input the local +// address and port and a pointer to a 'previous' address object. The hash +// table entries in each bucket are sorted in order of increasing address, and +// we skip over any object that has an address lower than the 'previous' +// address. To get the first address object, pass in a previous value of NULL. +// +// We assume that the table lock is held while we're in this routine. We don't +// take each object lock, since the local address and port can't change while +// the entry is in the table and the table lock is held so nothing can be +// inserted or deleted. +// +// Input: LocalAddr - Local IP address of object to find (may be NULL); +// LocalPort - Local port of object to find. +// Protocol - Protocol to find. +// PreviousAO - Pointer to last address object found. +// +// Returns: A pointer to the Address object, or NULL if none. +// +AddrObj * +GetAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol, + AddrObj *PreviousAO) +{ + AddrObj *CurrentAO; // Current address object we're examining. + + +#ifdef DEBUG + if (PreviousAO != NULL) + CTEStructAssert(PreviousAO, ao); +#endif + +#if 0 + // + // Check our 1-element cache for a match + // + if ((PreviousAO == NULL) && (LastAO != NULL)) { + CTEStructAssert(LastAO, ao); + if ( (LastAO->ao_prot == Protocol) && + IP_ADDR_EQUAL(LastAO->ao_addr, LocalAddr) && + (LastAO->ao_port == LocalPort) + ) + { + return LastAO; + } + } +#endif + + // Find the appropriate bucket in the hash table, and search for a match. + // If we don't find one the first time through, we'll try again with a + // wildcard local address. + + for (;;) { + + CurrentAO = AddrObjTable[AO_HASH(LocalAddr, LocalPort)]; + // While we haven't hit the end of the list, examine each element. + + while (CurrentAO != NULL) { + + CTEStructAssert(CurrentAO, ao); + + // If the current one is greater than one we were given, check it. + // + // #62710: Return only valid AO's since we might have stale AO's lying + // around. + // + if ((CurrentAO > PreviousAO) && + (AO_VALID(CurrentAO))) { + if (!(CurrentAO->ao_flags & AO_RAW_FLAG)) { + if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) && + (CurrentAO->ao_port == LocalPort) && + (CurrentAO->ao_prot == Protocol) + ) + { + LastAO = CurrentAO; + return CurrentAO; + } + } + else { + if ( (Protocol != PROTOCOL_UDP) +#ifndef UDP_ONLY + && (Protocol != PROTOCOL_TCP) +#endif + ) + { + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(( + "matching <%u, %lx> ao %lx <%u, %lx>\n", + Protocol, LocalAddr, CurrentAO, + CurrentAO->ao_prot, CurrentAO->ao_addr + )); + + } + + if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) && + ( (CurrentAO->ao_prot == Protocol) || + (CurrentAO->ao_prot == 0) + ) + ) + { + LastAO = CurrentAO; + return CurrentAO; + } + } + } + } + // Either it was less than the previous one, or they didn't match. + CurrentAO = CurrentAO->ao_next; + } + // When we get here, we've hit the end of the list we were examining. + // If we weren't examining a wildcard address, look for a wild card + // address. + if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { + LocalAddr = NULL_IP_ADDR; + PreviousAO = NULL; + } else + return NULL; // We looked for a wildcard and couldn't find + // one, so fail. + } +} + + +//* GetNextAddrObj - Get the next address object in a sequential search. +// +// This is the 'get next' routine, called when we are reading the address +// object table sequentially. We pull the appropriate parameters from the +// search context, call GetAddrObj, and update the search context with what +// we find. This routine assumes the AddrObjTableLock is held by the caller. +// +// Input: SearchContext - Pointer to seach context for search taking place. +// +// Returns: Pointer to AddrObj, or NULL if search failed. +// +AddrObj * +GetNextAddrObj(AOSearchContext *SearchContext) +{ + AddrObj *FoundAO; // Pointer to the address object we found. + + CTEAssert(SearchContext != NULL); + + // Try and find a match. + FoundAO = GetAddrObj(SearchContext->asc_addr, SearchContext->asc_port, + SearchContext->asc_prot, SearchContext->asc_previous); + + // Found a match. Update the search context for next time. + if (FoundAO != NULL) { + SearchContext->asc_previous = FoundAO; + SearchContext->asc_addr = FoundAO->ao_addr; + // Don't bother to update port or protocol, they don't change. + } + return FoundAO; +} + +//* GetFirstAddrObj - Get the first matching address object. +// +// The routine called to start a sequential read of the AddrObj table. We +// initialize the provided search context and then call GetNextAddrObj to do +// the actual read. We assume that the AddrObjTableLock is held by the caller. +// +// Input: LocalAddr - Local IP address of object to be found. +// LocalPort - Local port of AO to be found. +// Protocol - Protocol to be found. +// SearchContext - Pointer to search context to be used during +// search. +// +// Returns: Pointer to AO found, or NULL if we couldn't find any. +// +AddrObj * +GetFirstAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol, + AOSearchContext *SearchContext) +{ + CTEAssert(SearchContext != NULL); + + // Fill in the search context. + SearchContext->asc_previous = NULL; // Haven't found one yet. + SearchContext->asc_addr = LocalAddr; + SearchContext->asc_port = LocalPort; + SearchContext->asc_prot = Protocol; + return GetNextAddrObj(SearchContext); +} + +//* InsertAddrObj - Insert an address object into the AddrObj table. +// +// Called to insert an AO into the table, assuming the table lock is held. We +// hash on the addr and port, and then insert in into the correct place +// (sorted by address of the objects). +// +// Input: NewAO - Pointer to AddrObj to be inserted. +// +// Returns: Nothing. +// +void +InsertAddrObj(AddrObj *NewAO) +{ + AddrObj *PrevAO; // Pointer to previous address object in hash + // chain. + AddrObj *CurrentAO; // Pointer to current AO in table. + + CTEStructAssert(NewAO, ao); + + PrevAO = STRUCT_OF(AddrObj, + &AddrObjTable[AO_HASH(NewAO->ao_addr, NewAO->ao_port)], ao_next); + CurrentAO = PrevAO->ao_next; + + // Loop through the chain until we hit the end or until we find an entry + // whose address is greater than ours. + + while (CurrentAO != NULL) { + + CTEStructAssert(CurrentAO, ao); + CTEAssert(CurrentAO != NewAO); // Debug check to make sure we aren't + // inserting the same entry. + if (NewAO < CurrentAO) + break; + PrevAO = CurrentAO; + CurrentAO = CurrentAO->ao_next; + } + + // At this point, PrevAO points to the AO before the new one. Insert it + // there. + CTEAssert(PrevAO != NULL); + CTEAssert(PrevAO->ao_next == CurrentAO); + + NewAO->ao_next = CurrentAO; + PrevAO->ao_next = NewAO; + if (NewAO->ao_prot == PROTOCOL_UDP) + UStats.us_numaddrs++; +} + +//* RemoveAddrObj - Remove an address object from the table. +// +// Called when we need to remove an address object from the table. We hash on +// the addr and port, then walk the table looking for the object. We assume +// that the table lock is held. +// +// The AddrObj may have already been removed from the table if it was +// invalidated for some reason, so we need to check for the case of not +// finding it. +// +// Input: DeletedAO - AddrObj to delete. +// +// Returns: Nothing. +// +void +RemoveAddrObj(AddrObj *RemovedAO) +{ + AddrObj *PrevAO; // Pointer to previous address object in hash + // chain. + AddrObj *CurrentAO; // Pointer to current AO in table. + + CTEStructAssert(RemovedAO, ao); + + PrevAO = STRUCT_OF(AddrObj, + &AddrObjTable[AO_HASH(RemovedAO->ao_addr, RemovedAO->ao_port)], + ao_next); + CurrentAO = PrevAO->ao_next; + + // Walk the table, looking for a match. + while (CurrentAO != NULL) { + CTEStructAssert(CurrentAO, ao); + + if (CurrentAO == RemovedAO) { + PrevAO->ao_next = CurrentAO->ao_next; + if (CurrentAO->ao_prot == PROTOCOL_UDP) { + UStats.us_numaddrs--; + } + if (CurrentAO == LastAO) { + LastAO = NULL; + } + return; + } else { + PrevAO = CurrentAO; + CurrentAO = CurrentAO->ao_next; + } + } + + // If we get here, we didn't find him. This is OK, but we should say + // something about it. + CTEPrint("RemoveAddrObj: Object not found.\r\n"); +} + +//* FindAnyAddrObj - Find an AO with matching port on any local address. +// +// Called for wildcard address opens. We go through the entire addrobj table, +// and see if anyone has the specified port. We assume that the lock is +// already held on the table. +// +// Input: Port - Port to be looked for. +// Protocol - Protocol on which to look. +// +// Returns: Pointer to AO found, or NULL is noone has it. +// +AddrObj * +FindAnyAddrObj(ushort Port, uchar Protocol) +{ + int i; // Index variable. + AddrObj *CurrentAO; // Current AddrObj being examined. + + for (i = 0; i < AO_TABLE_SIZE; i++) { + CurrentAO = AddrObjTable[i]; + while (CurrentAO != NULL) { + CTEStructAssert(CurrentAO, ao); + + if (CurrentAO->ao_port == Port && CurrentAO->ao_prot == Protocol) + return CurrentAO; + else + CurrentAO = CurrentAO->ao_next; + } + } + + return NULL; + +} + +//* GetAddress - Get an IP address and port from a TDI address structure. +// +// Called when we need to get our addressing information from a TDI +// address structure. We go through the structure, and return what we +// find. +// +// Input: AddrList - Pointer to TRANSPORT_ADDRESS structure to search. +// Addr - Pointer to where to return IP address. +// Port - Pointer to where to return Port. +// +// Return: TRUE if we find an address, FALSE if we don't. +// +uchar +GetAddress(TRANSPORT_ADDRESS UNALIGNED *AddrList, IPAddr *Addr, ushort *Port) +{ + int i; // Index variable. + TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use. + + // First, verify that someplace in Address is an address we can use. + CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address; + + for (i = 0; i < AddrList->TAAddressCount; i++) { + if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) { + if (CurrentAddr->AddressLength >= TDI_ADDRESS_LENGTH_IP) { + TDI_ADDRESS_IP UNALIGNED *ValidAddr = + (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address; + + *Port = ValidAddr->sin_port; + *Addr = ValidAddr->in_addr; + return TRUE; + + } else + return FALSE; // Wrong length for address. + } else + CurrentAddr = (TA_ADDRESS UNALIGNED *)(CurrentAddr->Address + + CurrentAddr->AddressLength); + } + + return FALSE; // Didn't find a match. + + +} + +//* InvalidateAddrs - Invalidate all AOs for a specific address. +// +// Called when we need to invalidate all AOs for a specific address. Walk +// down the table with the lock held, and take the lock on each AddrObj. +// If the address matches, mark it as invalid, pull off all requests, +// and continue. At the end we'll complete all requests with an error. +// +// Input: Addr - Addr to be invalidated. +// +// Returns: Nothing. +// +void +InvalidateAddrs(IPAddr Addr) +{ + Queue SendQ; + Queue RcvQ; + AORequest *ReqList; + CTELockHandle TableHandle, AOHandle; + uint i; + AddrObj *AO; + DGSendReq *SendReq; + DGRcvReq *RcvReq; + AOMCastAddr *MA; + + INITQ(&SendQ); + INITQ(&RcvQ); + ReqList = NULL; + + CTEGetLock(&AddrObjTableLock, &TableHandle); + for (i = 0; i < AO_TABLE_SIZE; i++) { + // Walk down each hash bucket, looking for a match. + AO = AddrObjTable[i]; + while (AO != NULL) { + CTEStructAssert(AO, ao); + + CTEGetLock(&AO->ao_lock, &AOHandle); + if (IP_ADDR_EQUAL(AO->ao_addr, Addr) && AO_VALID(AO)) { + // This one matches. Mark as invalid, then pull his requests. + SET_AO_INVALID(AO); + + // Free any IP options we have. + (*LocalNetInfo.ipi_freeopts)(&AO->ao_opt); + + // If he has a request on him, pull him off. + if (AO->ao_request != NULL) { + AORequest *Temp; + + Temp = STRUCT_OF(AORequest, &AO->ao_request, aor_next); + do { + Temp = Temp->aor_next; + } while (Temp->aor_next != NULL); + + Temp->aor_next = ReqList; + ReqList = AO->ao_request; + AO->ao_request = NULL; + } + + // Go down his send list, pulling things off the send q and + // putting them on our local queue. + while (!EMPTYQ(&AO->ao_sendq)) { + DEQUEUE(&AO->ao_sendq, SendReq, DGSendReq, dsr_q); + CTEStructAssert(SendReq, dsr); + ENQUEUE(&SendQ, &SendReq->dsr_q); + } + + // Do the same for the receive queue. + while (!EMPTYQ(&AO->ao_rcvq)) { + DEQUEUE(&AO->ao_rcvq, RcvReq, DGRcvReq, drr_q); + CTEStructAssert(RcvReq, drr); + ENQUEUE(&RcvQ, &RcvReq->drr_q); + } + + // Free any multicast addresses he may have. IP will have + // deleted them at that level before we get here, so all we need + // to do if free the memory. + MA = AO->ao_mcastlist; + while (MA != NULL) { + AOMCastAddr *Temp; + + Temp = MA; + MA = MA->ama_next; + CTEFreeMem(Temp); + } + AO->ao_mcastlist = NULL; + + } + CTEFreeLock(&AO->ao_lock, AOHandle); + AO = AO->ao_next; // Go to the next one. + } + } + CTEFreeLock(&AddrObjTableLock, TableHandle); + + // OK, now walk what we've collected, complete it, and free it. + while (ReqList != NULL) { + AORequest *Req; + + Req = ReqList; + ReqList = Req->aor_next; + (*Req->aor_rtn)(Req->aor_context, (uint) TDI_ADDR_INVALID, 0); + FreeAORequest(Req); + } + + // Walk down the rcv. q, completing and freeing requests. + while (!EMPTYQ(&RcvQ)) { + + DEQUEUE(&RcvQ, RcvReq, DGRcvReq, drr_q); + CTEStructAssert(RcvReq, drr); + + (*RcvReq->drr_rtn)(RcvReq->drr_context, (uint) TDI_ADDR_INVALID, 0); + + FreeDGRcvReq(RcvReq); + + } + + // Now do the same for sends. + while (!EMPTYQ(&SendQ)) { + + DEQUEUE(&SendQ, SendReq, DGSendReq, dsr_q); + CTEStructAssert(SendReq, dsr); + + (*SendReq->dsr_rtn)(SendReq->dsr_context, (uint) TDI_ADDR_INVALID, 0); + + CTEGetLock(&DGSendReqLock, &TableHandle); + if (SendReq->dsr_header != NULL) + FreeDGHeader(SendReq->dsr_header); + FreeDGSendReq(SendReq); + CTEFreeLock(&DGSendReqLock, TableHandle); + + } +} + +//* RequestEventProc - Handle a deferred request event. +// +// Called when the event scheduled by DelayDerefAO is called. +// We just call ProcessAORequest. +// +// Input: Event - Event that fired. +// Context - Pointer to AddrObj. +// +// Returns: Nothing. +// +void +RequestEventProc(CTEEvent *Event, void *Context) +{ + AddrObj *AO = (AddrObj *)Context; + + CTEStructAssert(AO, ao); + CTEAssert(AO_BUSY(AO)); + + ProcessAORequests(AO); + +} + +//* GetAddrOptions - Get the address options. +// +// Called when we're opening an address. We take in a pointer, and walk +// down it looking for address options we know about. +// +// Input: Ptr - Ptr to search. +// Reuse - Pointer to reuse variable. +// DHCPAddr - Pointer to DHCP addr. +// +// Returns: Nothing. +// +void +GetAddrOptions(void *Ptr, uchar *Reuse, uchar *DHCPAddr) +{ + uchar *OptPtr; + + *Reuse = 0; + *DHCPAddr = 0; + + if (Ptr == NULL) + return; + + OptPtr = (uchar *)Ptr; + + while (*OptPtr != TDI_OPTION_EOL) { + if (*OptPtr == TDI_ADDRESS_OPTION_REUSE) + *Reuse = 1; + else + if (*OptPtr == TDI_ADDRESS_OPTION_DHCP) + *DHCPAddr = 1; + + OptPtr++; + } + +} + +//* TdiOpenAddress - Open a TDI address object. +// +// This is the external interface to open an address. The caller provides a +// TDI_REQUEST structure and a TRANSPORT_ADDRESS structure, as well a pointer +// to a variable identifying whether or not we are to allow reuse of an +// address while it's still open. +// +// Input: Request - Pointer to a TDI request structure for this request. +// AddrList - Pointer to TRANSPORT_ADDRESS structure describing +// address to be opened. +// Protocol - Protocol on which to open the address. Only the +// least significant byte is used. +// Ptr - Pointer to option buffer. +// +// Returns: TDI_STATUS code of attempt. +// +TDI_STATUS +TdiOpenAddress(PTDI_REQUEST Request, TRANSPORT_ADDRESS UNALIGNED *AddrList, + uint Protocol, void *Ptr) +{ + uint i; // Index variable + ushort Port; // Local Port we'll use. + IPAddr LocalAddr; // Actual address we'll use. + AddrObj *NewAO; // New AO we'll use. + AddrObj *ExistingAO; // Pointer to existing AO, if any. + CTELockHandle Handle; + uchar Reuse, DHCPAddr; + + + if (!GetAddress(AddrList, &LocalAddr, &Port)) + return TDI_BAD_ADDR; + + // Find the address options we might need. + GetAddrOptions(Ptr, &Reuse, &DHCPAddr); + + // Allocate the new addr obj now, assuming that + // we need it, so we don't have to do it with locks held later. + NewAO = CTEAllocMem(sizeof(AddrObj)); + + if (NewAO != NULL) { +#ifdef VXD + uint NewAOIndex; +#endif + CTEMemSet(NewAO, 0, sizeof(AddrObj)); + + // Check to make sure IP address is one of our local addresses. This + // is protected with the address table lock, so we can interlock an IP + // address going away through DHCP. + CTEGetLock(&AddrObjTableLock, &Handle); + + if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { // Not a wildcard. + + // Call IP to find out if this is a local address. + + if ((*LocalNetInfo.ipi_getaddrtype)(LocalAddr) != DEST_LOCAL) { + // Not a local address. Fail the request. + CTEFreeLock(&AddrObjTableLock, Handle); + CTEFreeMem(NewAO); + return TDI_BAD_ADDR; + } + } + + // The specified IP address is a valid local address. Now we do + // protocol-specific processing. + + switch (Protocol) { + +#ifndef UDP_ONLY + case PROTOCOL_TCP: +#endif + case PROTOCOL_UDP: + + // If no port is specified we have to assign one. If there is a + // port specified, we need to make sure that the IPAddress/Port + // combo isn't already open (unless Reuse is specified). If the + // input address is a wildcard, we need to make sure the address + // isn't open on any local ip address. + + if (Port == WILDCARD_PORT) { // Have a wildcard port, need to assign an + // address. + Port = NextUserPort; + + for (i = 0; i < NUM_USER_PORTS; i++, Port++) { + ushort NetPort; // Port in net byte order. + + if (Port > MaxUserPort) + Port = MIN_USER_PORT; + + NetPort = net_short(Port); + + if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP + // address. + ExistingAO = FindAnyAddrObj(NetPort, (uchar)Protocol); + else + ExistingAO = GetBestAddrObj(LocalAddr, NetPort, (uchar)Protocol); + + if (ExistingAO == NULL) + break; // Found an unused port. + } + + if (i == NUM_USER_PORTS) { // Couldn't find a free port. + CTEFreeLock(&AddrObjTableLock, Handle); + CTEFreeMem(NewAO); + return TDI_NO_FREE_ADDR; + } + NextUserPort = Port + 1; + Port = net_short(Port); + } else { // Address was specificed + + // Don't check if a DHCP address is specified. + if (!DHCPAddr) { + if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP + ExistingAO = FindAnyAddrObj(Port, (uchar)Protocol); // address. + else + ExistingAO = GetBestAddrObj(LocalAddr, Port, (uchar)Protocol); + + if (ExistingAO != NULL) { // We already have this address open. + // If the caller hasn't asked for Reuse, fail the request. + if (!Reuse) { + CTEFreeLock(&AddrObjTableLock, Handle); + CTEFreeMem(NewAO); + return TDI_ADDR_IN_USE; + } + } + } + } + + // + // We have a new AO. Set up the protocol specific portions + // + if (Protocol == PROTOCOL_UDP) { + NewAO->ao_dgsend = UDPSend; + NewAO->ao_maxdgsize = 0xFFFF - sizeof(UDPHeader); + } + + SET_AO_XSUM(NewAO); // Checksumming defaults to on. + + break; + // end case TCP & UDP + + default: + // + // All other protocols are opened over Raw IP. For now we don't + // do any duplicate checks. + // + + CTEAssert(!DHCPAddr); + + // + // We must set the port to zero. This puts all the raw sockets + // in one hash bucket, which is necessary for GetAddrObj to + // work correctly. It wouldn't be a bad idea to come up with + // a better scheme... + // + Port = 0; + NewAO->ao_dgsend = RawSend; + NewAO->ao_maxdgsize = 0xFFFF; + NewAO->ao_flags |= AO_RAW_FLAG; + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("raw open protocol %u AO %lx\n", Protocol, NewAO)); + } + break; + } + + // When we get here, we know we're creating a brand new address object. + // Port contains the port in question, and NewAO points to the newly + // created AO. + + (*LocalNetInfo.ipi_initopts)(&NewAO->ao_opt); + + (*LocalNetInfo.ipi_initopts)(&NewAO->ao_mcastopt); + + NewAO->ao_mcastopt.ioi_ttl = 1; + NewAO->ao_mcastaddr = NULL_IP_ADDR; + + CTEInitLock(&NewAO->ao_lock); + CTEInitEvent(&NewAO->ao_event, RequestEventProc); + INITQ(&NewAO->ao_sendq); + INITQ(&NewAO->ao_rcvq); + INITQ(&NewAO->ao_activeq); + INITQ(&NewAO->ao_idleq); + INITQ(&NewAO->ao_listenq); + NewAO->ao_port = Port; + NewAO->ao_addr = LocalAddr; + NewAO->ao_prot = (uchar)Protocol; +#ifdef DEBUG + NewAO->ao_sig = ao_signature; +#endif + NewAO->ao_flags |= AO_VALID_FLAG; // AO is valid. + + if (DHCPAddr) + NewAO->ao_flags |= AO_DHCP_FLAG; + +#ifdef VXD + NewAOIndex = GetAOIndex(NewAO); + if (NewAOIndex == INVALID_INDEX) { + CTEFreeLock(&AddrObjTableLock, Handle); + CTEFreeMem(NewAO); + return TDI_NO_RESOURCES; + } +#endif + + InsertAddrObj(NewAO); + CTEFreeLock(&AddrObjTableLock, Handle); +#ifdef VXD + Request->Handle.AddressHandle = (PVOID)NewAOIndex; +#else + Request->Handle.AddressHandle = NewAO; +#endif + return TDI_SUCCESS; + } else { // Couldn't allocate an address object. + return TDI_NO_RESOURCES; + } + + +} + +//* DeleteAO - Delete an address object. +// +// The internal routine to delete an address object. We complete any pending +// requests with errors, and remove and free the address object. +// +// Input: DeletedAO - AddrObj to be deleted. +// +// Returns: Nothing. +// +void +DeleteAO(AddrObj *DeletedAO) +{ + CTELockHandle TableHandle, AOHandle; // Lock handles we'll use here. + CTELockHandle HeaderHandle; +#ifndef UDP_ONLY + CTELockHandle ConnHandle, TCBHandle; + TCB *TCBHead = NULL, *CurrentTCB; + TCPConn *Conn; + Queue *Temp; + Queue *CurrentQ; +#endif + AOMCastAddr *AMA; + + CTEStructAssert(DeletedAO, ao); + CTEAssert(!AO_VALID(DeletedAO)); + CTEAssert(DeletedAO->ao_usecnt == 0); + + CTEGetLock(&AddrObjTableLock, &TableHandle); +#ifndef UDP_ONLY + CTEGetLock(&ConnTableLock, &ConnHandle); +#endif + CTEGetLock(&DGSendReqLock, &HeaderHandle); + CTEGetLock(&DeletedAO->ao_lock, &AOHandle); + + // If he's on an oor queue, remove him. + if (AO_OOR(DeletedAO)) + REMOVEQ(&DeletedAO->ao_pendq); + +#ifdef VXD + (*AOIndex)[DeletedAO->ao_index] = NULL; +#endif + + RemoveAddrObj(DeletedAO); + +#ifndef UDP_ONLY + // Walk down the list of associated connections and zap their AO pointers. + // For each connection, we need to shut down the connection if it's active. + // If the connection isn't already closing, we'll put a reference on it + // so that it can't go away while we're dealing with the AO, and put it + // on a list. On our way out we'll walk down that list and zap each + // connection. + CurrentQ = &DeletedAO->ao_activeq; + + for (;;) { + Temp = QHEAD(CurrentQ); + while (Temp != QEND(CurrentQ)) { + Conn = QSTRUCT(TCPConn, Temp, tc_q); + + CTEStructAssert(Conn, tc); + CurrentTCB = Conn->tc_tcb; + if (CurrentTCB != NULL) { + // We have a TCB. + CTEStructAssert(CurrentTCB, tcb); + CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle); + if (CurrentTCB->tcb_state != TCB_CLOSED && !CLOSING(CurrentTCB)) { + // It's not closing. Put a reference on it and save it on the + // list. + CurrentTCB->tcb_refcnt++; + CurrentTCB->tcb_aonext = TCBHead; + TCBHead = CurrentTCB; + } + CurrentTCB->tcb_conn = NULL; + CurrentTCB->tcb_rcvind = NULL; + CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle); + } + + // Destroy the pointers to the TCB and the AO. + Conn->tc_ao = NULL; + Conn->tc_tcb = NULL; + Temp = QNEXT(Temp); + } + + if (CurrentQ == &DeletedAO->ao_activeq) { + CurrentQ = &DeletedAO->ao_idleq; + } else if (CurrentQ == &DeletedAO->ao_idleq) { + CurrentQ = &DeletedAO->ao_listenq; + } else { + CTEAssert(CurrentQ == &DeletedAO->ao_listenq); + break; + } + } +#endif + + // We've removed him from the queues, and he's marked as invalid. Return + // pending requests with errors. + +#ifndef UDP_ONLY + CTEFreeLock(&DGSendReqLock, AOHandle); + CTEFreeLock(&ConnTableLock, HeaderHandle); + CTEFreeLock(&AddrObjTableLock, ConnHandle); +#else + CTEFreeLock(&DGSendReqLock, AOHandle); + CTEFreeLock(&AddrObjTableLock, HeaderHandle); +#endif + + // We still hold the lock on the AddrObj, although this may not be + // neccessary. + + while (!EMPTYQ(&DeletedAO->ao_rcvq)) { + DGRcvReq *Rcv; + + DEQUEUE(&DeletedAO->ao_rcvq, Rcv, DGRcvReq, drr_q); + CTEStructAssert(Rcv, drr); + + CTEFreeLock(&DeletedAO->ao_lock, TableHandle); + (*Rcv->drr_rtn)(Rcv->drr_context, (uint) TDI_ADDR_DELETED, 0); + + FreeDGRcvReq(Rcv); + + CTEGetLock(&DeletedAO->ao_lock, &TableHandle); + } + + // Now destroy any sends. + while (!EMPTYQ(&DeletedAO->ao_sendq)) { + DGSendReq *Send; + + DEQUEUE(&DeletedAO->ao_sendq, Send, DGSendReq, dsr_q); + CTEStructAssert(Send, dsr); + + CTEFreeLock(&DeletedAO->ao_lock, TableHandle); + (*Send->dsr_rtn)(Send->dsr_context, (uint) TDI_ADDR_DELETED, 0); + + CTEGetLock(&DGSendReqLock, &HeaderHandle); + if (Send->dsr_header != NULL) + FreeDGHeader(Send->dsr_header); + FreeDGSendReq(Send); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + + CTEGetLock(&DeletedAO->ao_lock, &TableHandle); + } + + CTEFreeLock(&DeletedAO->ao_lock, TableHandle); + + // Free any IP options we have. + (*LocalNetInfo.ipi_freeopts)(&DeletedAO->ao_opt); + + // Free any associated multicast addresses. + + AMA = DeletedAO->ao_mcastlist; + while (AMA != NULL) { + AOMCastAddr *Temp; + + (*LocalNetInfo.ipi_setmcastaddr)(AMA->ama_addr, AMA->ama_if, FALSE); + Temp = AMA; + AMA = AMA->ama_next; + CTEFreeMem(Temp); + } + + CTEFreeMem(DeletedAO); + +#ifndef UDP_ONLY + // Now go down the TCB list, and destroy any we need to. + CurrentTCB = TCBHead; + while (CurrentTCB != NULL) { + TCB *NextTCB; + CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle); + CurrentTCB->tcb_refcnt--; + CurrentTCB->tcb_flags |= NEED_RST; // Make sure we send a RST. + NextTCB = CurrentTCB->tcb_aonext; + TryToCloseTCB(CurrentTCB, TCB_CLOSE_ABORTED, TCBHandle); + CurrentTCB = NextTCB; + } +#endif + + +} + +//* GetAORequest - Get an AO request structure. +// +// A routine to allocate a request structure from our free list. +// +// Input: Nothing. +// +// Returns: Pointer to request structure, or NULL if we couldn't get one. +// +AORequest * +GetAORequest() +{ + AORequest *NewRequest; + CTELockHandle Handle; + + CTEGetLock(&AORequestLock, &Handle); + + NewRequest = AORequestFree; + if (NewRequest != NULL) { + AORequestFree = (AORequest *)NewRequest->aor_rtn; + CTEStructAssert(NewRequest, aor); + } + + CTEFreeLock(&AORequestLock, Handle); + return NewRequest; +} + +//* FreeAORequest - Free an AO request structure. +// +// Called to free an AORequest structure. +// +// Input: Request - AORequest structure to be freed. +// +// Returns: Nothing. +// +void +FreeAORequest(AORequest *Request) +{ + CTELockHandle Handle; + + CTEStructAssert(Request, aor); + + CTEGetLock(&AORequestLock, &Handle); + + *(AORequest **)&Request->aor_rtn = AORequestFree; + AORequestFree = Request; + + CTEFreeLock(&AORequestLock, Handle); +} + + + +//* TDICloseAddress - Close an address. +// +// The user API to delete an address. Basically, we destroy the local address +// object if we can. +// +// This routine is interlocked with the AO busy bit - if the busy bit is set, +// we'll just flag the AO for later deletion. +// +// Input: Request - TDI_REQUEST structure for this request. +// +// Returns: Status of attempt to delete the address - either pending or +// success. +// +TDI_STATUS +TdiCloseAddress(PTDI_REQUEST Request) +{ + AddrObj *DeletingAO; + CTELockHandle AOHandle; + +#ifdef VXD + DeletingAO = GetIndexedAO((uint)Request->Handle.AddressHandle); + if (DeletingAO == NULL) + return TDI_ADDR_INVALID; +#else + DeletingAO = Request->Handle.AddressHandle; +#endif + + CTEStructAssert(DeletingAO, ao); + + CTEGetLock(&DeletingAO->ao_lock, &AOHandle); + + if (!AO_BUSY(DeletingAO) && !(DeletingAO->ao_usecnt)) { + SET_AO_BUSY(DeletingAO); + SET_AO_INVALID(DeletingAO); // This address object is + // deleting. + CTEFreeLock(&DeletingAO->ao_lock, AOHandle); + DeleteAO(DeletingAO); + return TDI_SUCCESS; + } else { + + AORequest *NewRequest, *OldRequest; + CTEReqCmpltRtn CmpltRtn; + PVOID ReqContext; + TDI_STATUS Status; + + // Check and see if we already have a delete in progress. If we don't + // allocate and link up a delete request structure. + if (!AO_REQUEST(DeletingAO, AO_DELETE)) { + + OldRequest = DeletingAO->ao_request; + + NewRequest = GetAORequest(); + + if (NewRequest != NULL) { // Got a request. + NewRequest->aor_rtn = Request->RequestNotifyObject; + NewRequest->aor_context = Request->RequestContext; + CLEAR_AO_REQUEST(DeletingAO, AO_OPTIONS); // Clear the option + // request, + // if there is one. + SET_AO_REQUEST(DeletingAO, AO_DELETE); + SET_AO_INVALID(DeletingAO); // This address + // object is + // deleting. + DeletingAO->ao_request = NewRequest; + NewRequest->aor_next = NULL; + CTEFreeLock(&DeletingAO->ao_lock, AOHandle); + + while (OldRequest != NULL) { + AORequest *Temp; + + CmpltRtn = OldRequest->aor_rtn; + ReqContext = OldRequest->aor_context; + + (*CmpltRtn)(ReqContext, (uint) TDI_ADDR_DELETED, 0); + Temp = OldRequest; + OldRequest = OldRequest->aor_next; + FreeAORequest(Temp); + } + + return TDI_PENDING; + } else + Status = TDI_NO_RESOURCES; + } else // Delete already in progress. + Status = TDI_ADDR_INVALID; + + CTEFreeLock(&DeletingAO->ao_lock, AOHandle); + return Status; + } + +} + +//* FindAOMCastAddr - Find a multicast address on an AddrObj. +// +// A utility routine to find a multicast address on an AddrObj. We also return +// a pointer to it's predecessor, for use in deleting. +// +// Input: AO - AddrObj to search. +// Addr - MCast address to search for. +// IF - IPAddress of interface +// PrevAMA - Pointer to where to return predecessor. +// +// Returns: Pointer to matching AMA structure, or NULL if there is none. +// +AOMCastAddr * +FindAOMCastAddr(AddrObj *AO, IPAddr Addr, IPAddr IF, AOMCastAddr **PrevAMA) +{ + AOMCastAddr *FoundAMA, *Temp; + + Temp = STRUCT_OF(AOMCastAddr, &AO->ao_mcastlist, ama_next); + FoundAMA = AO->ao_mcastlist; + + while (FoundAMA != NULL) { + if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr) && + IP_ADDR_EQUAL(IF, FoundAMA->ama_if)) + break; + Temp = FoundAMA; + FoundAMA = FoundAMA->ama_next; + } + + *PrevAMA = Temp; + return FoundAMA; +} + +//* MCastAddrOnAO - Test to see if a multicast address on an AddrObj. +// +// A utility routine to test to see if a multicast address is on an AddrObj. +// +// Input: AO - AddrObj to search. +// Addr - MCast address to search for. +// +// Returns: TRUE is Addr is on AO. +// +uint +MCastAddrOnAO(AddrObj *AO, IPAddr Addr) +{ + AOMCastAddr *FoundAMA; + + FoundAMA = AO->ao_mcastlist; + + while (FoundAMA != NULL) { + if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr)) + return(TRUE); + FoundAMA = FoundAMA->ama_next; + } + return(FALSE); +} + +//* SetAOOptions - Set AddrObj options. +// +// The set options worker routine, called when we've validated the buffer +// and know that the AddrObj isn't busy. +// +// Input: OptionAO - AddrObj for which options are being set. +// Options - AOOption buffer of options. +// +// Returns: TDI_STATUS of attempt. +// +TDI_STATUS +SetAOOptions(AddrObj *OptionAO, uint ID, uint Length, uchar *Options) +{ + IP_STATUS IPStatus; // Status of IP option set request. + CTELockHandle Handle; + TDI_STATUS Status; + AOMCastAddr *AMA, *PrevAMA; + + CTEAssert(AO_BUSY(OptionAO)); + + // First, see if there are IP options. + + if (ID == AO_OPTION_IPOPTIONS) { + IF_TCPDBG(TCP_DEBUG_OPTIONS) { + TCPTRACE(("processing IP_IOTIONS on AO %lx\n", OptionAO)); + } + // These are IP options. Pass them down. + (*LocalNetInfo.ipi_freeopts)(&OptionAO->ao_opt); + + IPStatus = (*LocalNetInfo.ipi_copyopts)(Options, Length, + &OptionAO->ao_opt); + + if (IPStatus == IP_SUCCESS) + return TDI_SUCCESS; + else if (IPStatus == IP_NO_RESOURCES) + return TDI_NO_RESOURCES; + else + return TDI_BAD_OPTION; + } + + // These are UDP/TCP options. + if (Length == 0) + return TDI_BAD_OPTION; + + Status = TDI_SUCCESS; + CTEGetLock(&OptionAO->ao_lock, &Handle); + + switch (ID) { + + case AO_OPTION_XSUM: + if (Options[0]) + SET_AO_XSUM(OptionAO); + else + CLEAR_AO_XSUM(OptionAO); + break; + + case AO_OPTION_IP_DONTFRAGMENT: + IF_TCPDBG(TCP_DEBUG_OPTIONS) { + TCPTRACE(( + "DF opt %u, initial flags %lx on AO %lx\n", + (int) Options[0], OptionAO->ao_opt.ioi_flags, OptionAO + )); + } + + if (Options[0]) + OptionAO->ao_opt.ioi_flags |= IP_FLAG_DF; + else + OptionAO->ao_opt.ioi_flags &= ~IP_FLAG_DF; + + IF_TCPDBG(TCP_DEBUG_OPTIONS) { + TCPTRACE(( + "New flags %lx on AO %lx\n", + OptionAO->ao_opt.ioi_flags, OptionAO + )); + } + + break; + + case AO_OPTION_TTL: + IF_TCPDBG(TCP_DEBUG_OPTIONS) { + TCPTRACE(( + "setting TTL to %d on AO %lx\n", Options[0], OptionAO + )); + } + OptionAO->ao_opt.ioi_ttl = Options[0]; + break; + + case AO_OPTION_TOS: + IF_TCPDBG(TCP_DEBUG_OPTIONS) { + TCPTRACE(( + "setting TOS to %d on AO %lx\n", Options[0], OptionAO + )); + } + OptionAO->ao_opt.ioi_tos = Options[0]; + break; + + case AO_OPTION_MCASTTTL: + OptionAO->ao_mcastopt.ioi_ttl = Options[0]; + break; + + case AO_OPTION_MCASTIF: + if (Length >= sizeof(UDPMCastIFReq)) { + UDPMCastIFReq *Req; + IPAddr Addr; + + Req = (UDPMCastIFReq *)Options; + Addr = Req->umi_addr; + if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) && + (*LocalNetInfo.ipi_getaddrtype)(Addr) != DEST_LOCAL) + { + Status = TDI_BAD_OPTION; + } + else { + OptionAO->ao_mcastaddr = Addr; + } + } else + Status = TDI_BAD_OPTION; + break; + + case AO_OPTION_ADD_MCAST: + case AO_OPTION_DEL_MCAST: + if (Length >= sizeof(UDPMCastReq)) { + UDPMCastReq *Req = (UDPMCastReq *)Options; + + AMA = FindAOMCastAddr(OptionAO, Req->umr_addr, Req->umr_if, + &PrevAMA); + + if (ID == AO_OPTION_ADD_MCAST) { + if (AMA != NULL) { + Status = TDI_BAD_OPTION; + break; + } + AMA = CTEAllocMem(sizeof(AOMCastAddr)); + if (AMA == NULL) { + // Couldn't get the resource we need. + Status = TDI_NO_RESOURCES; + break; + } + + AMA->ama_next = OptionAO->ao_mcastlist; + OptionAO->ao_mcastlist = AMA; + + AMA->ama_addr = Req->umr_addr; + AMA->ama_if = Req->umr_if; + + } else { + // This is a delete request. Fail it if it's not there. + if (AMA == NULL) { + Status = TDI_BAD_OPTION; + break; + } + + PrevAMA->ama_next = AMA->ama_next; + CTEFreeMem(AMA); + } + + IPStatus = (*LocalNetInfo.ipi_setmcastaddr)(Req->umr_addr, + Req->umr_if, ID == AO_OPTION_ADD_MCAST ? TRUE : FALSE); + + if (IPStatus != TDI_SUCCESS) { + // Some problem adding or deleting. If we were adding, we + // need to free the one we just added. + if (ID == AO_OPTION_ADD_MCAST) { + AMA = FindAOMCastAddr(OptionAO, Req->umr_addr, + Req->umr_if, &PrevAMA); + if (AMA != NULL) { + PrevAMA->ama_next = AMA->ama_next; + CTEFreeMem(AMA); + } else + DEBUGCHK; + } + + Status = (IPStatus == IP_NO_RESOURCES ? TDI_NO_RESOURCES : + TDI_BAD_OPTION); + } + + } else + Status = TDI_BAD_OPTION; + break; + + default: + Status = TDI_BAD_OPTION; + break; + } + + CTEFreeLock(&OptionAO->ao_lock, Handle); + + return Status; + +} + +//* SetAddrOptions - Set options on an address object. +// +// Called to set options on an address object. We validate the buffer, +// and if everything is OK we'll check the status of the AddrObj. If +// it's OK then we'll set them, otherwise we'll mark it for later use. +// +// Input: Request - Request describing AddrObj for option set. +// ID - ID for option to be set. +// OptLength - Length of options. +// Options - Pointer to options. +// +// Returns: TDI_STATUS of attempt. +// +TDI_STATUS +SetAddrOptions(PTDI_REQUEST Request, uint ID, uint OptLength, void *Options) +{ + AddrObj *OptionAO; + TDI_STATUS Status; + + CTELockHandle AOHandle; + +#ifdef VXD + OptionAO = GetIndexedAO((uint)Request->Handle.AddressHandle); + if (OptionAO == NULL) + return TDI_ADDR_INVALID; +#else + OptionAO = Request->Handle.AddressHandle; +#endif + + CTEStructAssert(OptionAO, ao); + + CTEGetLock(&OptionAO->ao_lock, &AOHandle); + + if (AO_VALID(OptionAO)) { + if (!AO_BUSY(OptionAO) && OptionAO->ao_usecnt == 0) { + SET_AO_BUSY(OptionAO); + CTEFreeLock(&OptionAO->ao_lock, AOHandle); + + Status = SetAOOptions(OptionAO, ID, OptLength, Options); + + CTEGetLock(&OptionAO->ao_lock, &AOHandle); + if (!AO_PENDING(OptionAO)) { + CLEAR_AO_BUSY(OptionAO); + CTEFreeLock(&OptionAO->ao_lock, AOHandle); + return Status; + } else { + CTEFreeLock(&OptionAO->ao_lock, AOHandle); + ProcessAORequests(OptionAO); + return Status; + } + } else { + AORequest *NewRequest, *OldRequest; + + // The AddrObj is busy somehow. We need to get a request, and link + // him on the request list. + + NewRequest = GetAORequest(); + + if (NewRequest != NULL) { // Got a request. + NewRequest->aor_rtn = Request->RequestNotifyObject; + NewRequest->aor_context = Request->RequestContext; + NewRequest->aor_id = ID; + NewRequest->aor_length = OptLength; + NewRequest->aor_buffer = Options; + SET_AO_REQUEST(OptionAO, AO_OPTIONS); // Set the + // option request, + OldRequest = STRUCT_OF(AORequest, &OptionAO->ao_request, + aor_next); + + while (OldRequest->aor_next != NULL) + OldRequest = OldRequest->aor_next; + + OldRequest->aor_next = NewRequest; + CTEFreeLock(&OptionAO->ao_lock, AOHandle); + + return TDI_PENDING; + } else + Status = TDI_NO_RESOURCES; + + } + } else + Status = TDI_ADDR_INVALID; + + CTEFreeLock(&OptionAO->ao_lock, AOHandle); + return Status; + +} + +//* TDISetEvent - Set a handler for a particular event. +// +// This is the user API to set an event. It's pretty simple, we just +// grab the lock on the AddrObj and fill in the event. +// +// +// Input: Handle - Pointer to address object. +// Type - Event being set. +// Handler - Handler to call for event. +// Context - Context to pass to event. +// +// Returns: TDI_SUCCESS if it works, an error if it doesn't. This routine +// never pends. +// +TDI_STATUS +TdiSetEvent(PVOID Handle, int Type, PVOID Handler, PVOID Context) +{ + AddrObj *EventAO; + CTELockHandle AOHandle; + TDI_STATUS Status; + +#ifdef VXD + EventAO = GetIndexedAO((uint)Handle); + + CTEStructAssert(EventAO, ao); + if (EventAO == NULL || !AO_VALID(EventAO)) + return TDI_ADDR_INVALID; +#else + EventAO = (AddrObj *)Handle; + + CTEStructAssert(EventAO, ao); + if (!AO_VALID(EventAO)) + return TDI_ADDR_INVALID; +#endif + + + CTEGetLock(&EventAO->ao_lock, &AOHandle); + + Status = TDI_SUCCESS; + switch (Type) { + + case TDI_EVENT_CONNECT: + EventAO->ao_connect = Handler; + EventAO->ao_conncontext = Context; + break; + case TDI_EVENT_DISCONNECT: + EventAO->ao_disconnect = Handler; + EventAO->ao_disconncontext = Context; + break; + case TDI_EVENT_ERROR: + EventAO->ao_error = Handler; + EventAO->ao_errcontext = Context; + break; + case TDI_EVENT_RECEIVE: + EventAO->ao_rcv = Handler; + EventAO->ao_rcvcontext = Context; + break; + case TDI_EVENT_RECEIVE_DATAGRAM: + EventAO->ao_rcvdg = Handler; + EventAO->ao_rcvdgcontext = Context; + break; + case TDI_EVENT_RECEIVE_EXPEDITED: + EventAO->ao_exprcv = Handler; + EventAO->ao_exprcvcontext = Context; + break; + default: + Status = TDI_BAD_EVENT_TYPE; + break; + } + + CTEFreeLock(&EventAO->ao_lock, AOHandle); + return Status; + + +} + +//* ProcessAORequests - Process pending requests on an AddrObj. +// +// This is the delayed request processing routine, called when we've +// done something that used the busy bit. We examine the pending +// requests flags, and dispatch the requests appropriately. +// +// Input: RequestAO - AddrObj to be processed. +// +// Returns: Nothing. +// +void +ProcessAORequests(AddrObj *RequestAO) +{ + CTELockHandle AOHandle; + AORequest *Request; + + CTEStructAssert(RequestAO, ao); + CTEAssert(AO_BUSY(RequestAO)); + CTEAssert(RequestAO->ao_usecnt == 0); + + CTEGetLock(&RequestAO->ao_lock, &AOHandle); + + while (AO_PENDING(RequestAO)) { + Request = RequestAO->ao_request; + + if (AO_REQUEST(RequestAO, AO_DELETE)) { + CTEAssert(Request != NULL); + CTEAssert(!AO_REQUEST(RequestAO, AO_OPTIONS)); + CTEFreeLock(&RequestAO->ao_lock, AOHandle); + DeleteAO(RequestAO); + (*Request->aor_rtn)(Request->aor_context, TDI_SUCCESS, 0); + FreeAORequest(Request); + return; // Deleted him, so get out. + } + + // Now handle options request. + while (AO_REQUEST(RequestAO, AO_OPTIONS)) { + TDI_STATUS Status; + + // Have an option request. + Request = RequestAO->ao_request; + RequestAO->ao_request = Request->aor_next; + if (RequestAO->ao_request == NULL) + CLEAR_AO_REQUEST(RequestAO, AO_OPTIONS); + + CTEAssert(Request != NULL); + CTEFreeLock(&RequestAO->ao_lock, AOHandle); + + Status = SetAOOptions(RequestAO, Request->aor_id, + Request->aor_length, Request->aor_buffer); + (*Request->aor_rtn)(Request->aor_context, Status, 0); + FreeAORequest(Request); + + CTEGetLock(&RequestAO->ao_lock, &AOHandle); + } + + // We've done options, now try sends. + if (AO_REQUEST(RequestAO, AO_SEND)) { + DGSendReq *SendReq; + + // Need to send. Clear the busy flag, bump the send count, and + // get the send request. + if (!EMPTYQ(&RequestAO->ao_sendq)) { + DEQUEUE(&RequestAO->ao_sendq, SendReq, DGSendReq, dsr_q); + CLEAR_AO_BUSY(RequestAO); + RequestAO->ao_usecnt++; + CTEFreeLock(&RequestAO->ao_lock, AOHandle); + UDPSend(RequestAO, SendReq); + CTEGetLock(&RequestAO->ao_lock, &AOHandle); + // If there aren't any other pending sends, set the busy bit. + if (!(--RequestAO->ao_usecnt)) + SET_AO_BUSY(RequestAO); + else + break; // Still sending, so get out. + } else { + // Had the send request set, but no send! Odd.... + DEBUGCHK; + CLEAR_AO_REQUEST(RequestAO, AO_SEND); + } + + } + } + + // We're done here. + CLEAR_AO_BUSY(RequestAO); + CTEFreeLock(&RequestAO->ao_lock, AOHandle); + +} + + +//* DelayDerefAO - Derefrence an AddrObj, and schedule an event. +// +// Called when we are done with an address object, and need to +// derefrence it. We dec the usecount, and if it goes to 0 and +// if there are pending actions we'll schedule an event to deal +// with them. +// +// Input: RequestAO - AddrObj to be processed. +// +// Returns: Nothing. +// +void +DelayDerefAO(AddrObj *RequestAO) +{ + CTELockHandle Handle; + + CTEGetLock(&RequestAO->ao_lock, &Handle); + + RequestAO->ao_usecnt--; + + if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) { + if (AO_PENDING(RequestAO)) { + SET_AO_BUSY(RequestAO); + CTEFreeLock(&RequestAO->ao_lock, Handle); + CTEScheduleEvent(&RequestAO->ao_event, RequestAO); + return; + } + } + CTEFreeLock(&RequestAO->ao_lock, Handle); + +} + +//* DerefAO - Derefrence an AddrObj. +// +// Called when we are done with an address object, and need to +// derefrence it. We dec the usecount, and if it goes to 0 and +// if there are pending actions we'll call the process AO handler. +// +// Input: RequestAO - AddrObj to be processed. +// +// Returns: Nothing. +// +void +DerefAO(AddrObj *RequestAO) +{ + CTELockHandle Handle; + + CTEGetLock(&RequestAO->ao_lock, &Handle); + + RequestAO->ao_usecnt--; + + if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) { + if (AO_PENDING(RequestAO)) { + SET_AO_BUSY(RequestAO); + CTEFreeLock(&RequestAO->ao_lock, Handle); + ProcessAORequests(RequestAO); + return; + } + } + + CTEFreeLock(&RequestAO->ao_lock, Handle); + +} + +#pragma BEGIN_INIT + +//* InitAddr - Initialize the address object stuff. +// +// Called during init time to initalize the address object stuff. +// +// Input: Nothing +// +// Returns: True if we succeed, False if we fail. +// +int +InitAddr() +{ + AORequest *RequestPtr; + int i; + + CTEInitLock(&AddrObjTableLock); + CTEInitLock(&AORequestLock); + +#ifdef VXD + AOInstance = 1; + + AOIndexSize = DEFAULT_AO_INDEX_SIZE; + NextAOIndex = 0; + + AOIndex = CTEAllocMem(sizeof(AddrObj *) * DEFAULT_AO_INDEX_SIZE); + if (AOIndex == NULL) + return FALSE; + +#endif + + RequestPtr = CTEAllocMem(sizeof(AORequest)*NUM_AO_REQUEST); + if (RequestPtr == NULL) { +#ifdef VXD + CTEFreeMem(AOIndex); +#endif + return FALSE; + } + + AORequestFree = NULL; + + for (i = 0; i < NUM_AO_REQUEST; i++, RequestPtr++) { +#ifdef DEBUG + RequestPtr->aor_sig = aor_signature; +#endif + FreeAORequest(RequestPtr); + } + + for (i = 0; i < AO_TABLE_SIZE; i++) + AddrObjTable[i] = NULL; + + LastAO = NULL; + + return TRUE; + +} +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/tcp/addr.h b/private/ntos/tdi/tcpip/tcp/addr.h new file mode 100644 index 000000000..78ec23ce3 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/addr.h @@ -0,0 +1,211 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** ADDR.H - TDI address object definitions. +// +// This file contains the definitions of TDI address objects and related +// constants and structures. + +#define ao_signature 0x20204F41 // 'AO ' + +#define AO_TABLE_SIZE 16 // Address object hash table size. + +#define WILDCARD_PORT 0 // 0 means assign a port. + +#define MIN_USER_PORT 1025 // Minimum value for a wildcard port +#define MAX_USER_PORT 5000 // Maximim value for a user port. +#define NUM_USER_PORTS (uint)(MaxUserPort - MIN_USER_PORT + 1) + +#define NETBT_SESSION_PORT 139 + +typedef struct AddrObj AddrObj; + +//* Datagram transport-specific send function. +typedef void (*DGSendProc)(AddrObj *SrcAO, void *SendReq); + +//* Definition of the structure of an address object. Each object represents +// a local address, and the IP portion may be a wildcard. + +struct AddrObj { +#ifdef DEBUG + ulong ao_sig; +#endif + struct AddrObj *ao_next; // Pointer to next address object in chain. + DEFINE_LOCK_STRUCTURE(ao_lock) // Lock for this object. + struct AORequest *ao_request;// Pointer to pending request. + Queue ao_sendq; // Queue of sends waiting for transmission. + Queue ao_pendq; // Linkage for pending queue. + Queue ao_rcvq; // Receive queue. + IPOptInfo ao_opt; // Opt info for this address object. + IPAddr ao_addr; // IP address for this address object. + ushort ao_port; // Local port for this address object. + ushort ao_flags; // Flags for this object. + uchar ao_prot; // Protocol for this AO. + uchar ao_index; // Index into table of this AO. + uchar ao_pad[2]; // PAD PAD PAD + uint ao_listencnt; // Number of listening connections. + ushort ao_usecnt; // Count of 'uses' on AO. + ushort ao_inst; // 'Instance' number of this AO. + IPOptInfo ao_mcastopt;// MCast opt info. + IPAddr ao_mcastaddr; // Source address for MCast from + // this addr object.. + Queue ao_activeq; // Queue of active connections. + Queue ao_idleq; // Queue of inactive (no TCB) connections. + Queue ao_listenq; // Queue of listening connections. + CTEEvent ao_event; // Event to use for this AO. + PConnectEvent ao_connect; // Connect event handle. + PVOID ao_conncontext; // Receive DG context. + PDisconnectEvent ao_disconnect; // Disconnect event routine. + PVOID ao_disconncontext; // Disconnect event context. + PErrorEvent ao_error; // Error event routine. + PVOID ao_errcontext; // Error event context. + PRcvEvent ao_rcv; // Receive event handler + PVOID ao_rcvcontext; // Receive context. + PRcvDGEvent ao_rcvdg; // Receive DG event handler + PVOID ao_rcvdgcontext; // Receive DG context. + PRcvEvent ao_exprcv; // Expedited receive event handler + PVOID ao_exprcvcontext; // Expedited receive context. + struct AOMCastAddr *ao_mcastlist; // List of active multicast + // addresses. + DGSendProc ao_dgsend; // Datagram transport send function. + ushort ao_maxdgsize; // maximum user datagram size. +#ifdef SYN_ATTACK + BOOLEAN ConnLimitReached; //set when there are no + //connections left +#endif +}; /* AddrObj */ + +typedef struct AddrObj AddrObj; + +#define AO_RAW_FLAG 0x0200 // AO is for a raw endpoint. +#define AO_DHCP_FLAG 0x0100 // AO is bound to real 0 address. + +#define AO_VALID_FLAG 0x0080 // AddrObj is valid. +#define AO_BUSY_FLAG 0x0040 // AddrObj is busy (i.e., has it + // exclusive). +#define AO_OOR_FLAG 0x0020 // AddrObj is out of resources, and on + // either the pending or delayed queue. +#define AO_QUEUED_FLAG 0x0010 // AddrObj is on the pending queue. + + +#define AO_XSUM_FLAG 0x0008 // Xsums are used on this AO. +#define AO_SEND_FLAG 0x0004 // Send is pending. +#define AO_OPTIONS_FLAG 0x0002 // Option set pending. +#define AO_DELETE_FLAG 0x0001 // Delete pending. + + +#define AO_VALID(A) ((A)->ao_flags & AO_VALID_FLAG) +#define SET_AO_INVALID(A) (A)->ao_flags &= ~AO_VALID_FLAG + +#define AO_BUSY(A) ((A)->ao_flags & AO_BUSY_FLAG) +#define SET_AO_BUSY(A) (A)->ao_flags |= AO_BUSY_FLAG +#define CLEAR_AO_BUSY(A) (A)->ao_flags &= ~AO_BUSY_FLAG + +#define AO_OOR(A) ((A)->ao_flags & AO_OOR_FLAG) +#define SET_AO_OOR(A) (A)->ao_flags |= AO_OOR_FLAG +#define CLEAR_AO_OOR(A) (A)->ao_flags &= ~AO_OOR_FLAG + +#define AO_QUEUED(A) ((A)->ao_flags & AO_QUEUED_FLAG) +#define SET_AO_QUEUED(A) (A)->ao_flags |= AO_QUEUED_FLAG +#define CLEAR_AO_QUEUED(A) (A)->ao_flags &= ~AO_QUEUED_FLAG + +#define AO_XSUM(A) ((A)->ao_flags & AO_XSUM_FLAG) +#define SET_AO_XSUM(A) (A)->ao_flags |= AO_XSUM_FLAG +#define CLEAR_AO_XSUM(A) (A)->ao_flags &= ~AO_XSUM_FLAG + +#define AO_REQUEST(A, f) ((A)->ao_flags & f##_FLAG) +#define SET_AO_REQUEST(A, f) (A)->ao_flags |= f##_FLAG +#define CLEAR_AO_REQUEST(A, f) (A)->ao_flags &= ~f##_FLAG +#define AO_PENDING(A) ((A)->ao_flags & (AO_DELETE_FLAG | AO_OPTIONS_FLAG | AO_SEND_FLAG)) + +//* Definition of an address object search context. This is a data structure used +// when the address object table is to be read sequentially. + +struct AOSearchContext { + AddrObj *asc_previous; // Previous AO found. + IPAddr asc_addr; // IPAddress to be found. + ushort asc_port; // Port to be found. + uchar asc_prot; // Protocol + uchar asc_pad; // Pad to dword boundary. +}; /* AOSearchContext */ + +typedef struct AOSearchContext AOSearchContext; + +//* Definition of an AO request structure. There structures are used only for +// queuing delete and option set requests. + +#define aor_signature 0x20524F41 + +struct AORequest { +#ifdef DEBUG + ulong aor_sig; +#endif + struct AORequest *aor_next; // Next pointer in chain. + uint aor_id; // ID for the request. + uint aor_length; // Length of buffer. + void *aor_buffer; // Buffer for this request. + CTEReqCmpltRtn aor_rtn; // Request complete routine for this request. + PVOID aor_context; // Request context; +}; /* AORequest */ + +typedef struct AORequest AORequest; + +typedef struct AOMCastAddr { + struct AOMCastAddr *ama_next; // Next in list. + IPAddr ama_addr; // The address. + IPAddr ama_if; // The interface. +} AOMCastAddr; + +//* External declarations for exported functions. + +extern AddrObj *GetAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Prot, + AddrObj *PreviousAO); +extern AddrObj *GetNextAddrObj(AOSearchContext *SearchContext); +extern AddrObj *GetFirstAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Prot, + AOSearchContext *SearchContext); +extern TDI_STATUS TdiOpenAddress(PTDI_REQUEST Request, + TRANSPORT_ADDRESS UNALIGNED *AddrList, uint Protocol, + void *Reuse); +extern TDI_STATUS TdiCloseAddress(PTDI_REQUEST Request); +extern TDI_STATUS SetAddrOptions(PTDI_REQUEST Request, uint ID, uint OptLength, + void *Options); +extern TDI_STATUS TdiSetEvent(PVOID Handle, int Type, PVOID Handler, + PVOID Context); +extern uchar GetAddress(TRANSPORT_ADDRESS UNALIGNED *AddrList, + IPAddr *Addr, ushort *Port); +extern int InitAddr(void); +extern void ProcessAORequests(AddrObj *RequestAO); +extern void DelayDerefAO(AddrObj *RequestAO); +extern void DerefAO(AddrObj *RequestAO); +extern void FreeAORequest(AORequest *FreedRequest); +#ifdef VXD +extern AddrObj *GetIndexedAO(uint Index); +#endif +extern uint ValidateAOContext(void *Context, uint *Valid); +extern uint ReadNextAO(void *Context, void *OutBuf); +extern void InvalidateAddrs(IPAddr Addr); + +extern uint MCastAddrOnAO(AddrObj *AO, IPAddr Addr); + +#define GetBestAddrObj(addr, port, prot) GetAddrObj(addr, port, prot, NULL) + +#define REF_AO(a) (a)->ao_usecnt++ + +#define DELAY_DEREF_AO(a) DelayDerefAO((a)) +#define DEREF_AO(a) DerefAO((a)) +#define LOCKED_DELAY_DEREF_AO(a) (a)->ao_usecnt--; \ +\ + if (!(a)->ao_usecnt && !AO_BUSY((a)) && AO_PENDING((a))) { \ + SET_AO_BUSY((a)); \ + CTEScheduleEvent(&(a)->ao_event, (a)); \ + } + + + + + + + diff --git a/private/ntos/tdi/tcpip/tcp/alpha/sources b/private/ntos/tdi/tcpip/tcp/alpha/sources new file mode 100644 index 000000000..6a4917025 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/alpha/sources @@ -0,0 +1,3 @@ +ALPHA_SOURCES= \ + ..\alpha\xsum.s + diff --git a/private/ntos/tdi/tcpip/tcp/alpha/xsum.s b/private/ntos/tdi/tcpip/tcp/alpha/xsum.s new file mode 100644 index 000000000..032d40747 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/alpha/xsum.s @@ -0,0 +1,271 @@ +// TITLE("Compute Checksum") +//++ +// +// Copyright (c) 1994 Microsoft Corporation +// +// Module Name: +// +// xsum.s +// +// Abstract: +// +// This module implements a function to compute the checksum of a buffer. +// +// Author: +// +// John Vert (jvert) 11-Jul-1994 +// +// Environment: +// +// Revision History: +// +//-- + +#include "ksalpha.h" + + + SBTTL("Compute Checksum") +//++ +// +// ULONG +// tcpxsum ( +// IN ULONG Checksum, +// IN PUSHORT Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function computes the checksum of the specified buffer. +// +// Arguments: +// +// Checksum (a0) - Supplies the initial checksum value. +// +// Source (a1) - Supplies a pointer to the checksum buffer +// +// Length (a2) - Supplies the length of the buffer in words. +// +// Return Value: +// +// The computed checksum is returned as the function value. +// +//-- + + LEAF_ENTRY(tcpxsum) + zap a0, 0xf0, a0 // clear high half of a0 + bis a1, zero, t6 // save initial buffer address + bis zero, zero, v0 // clear accumulated checksum + +// +// Check if the buffer is quadword aligned. +// +// If the buffer is not quadword aligned, then add the leading words to the +// checksum. +// + ldq_u t0, 0(a1) // get containing quadword of first part + blbc a1, 10f // check for word alignment + beq a2, 65f // if zero bytes, don't do anything + extbl t0, a1, t1 // get leading byte + sll t1, 8, v0 // shift it to correct spot for later byte swap + addq a1, 1, a1 // increment buffer to first full word + subq a2, 1, a2 // decrement byte count + +10: + and a1, 6, t2 // check if buffer quadword aligned + beq t2, 20f // if eq, quadword aligned + extql t0, t2, t0 // extract bytes to checksum + and a1, 7, t3 // compute bytes summed + subq zero, t3, t3 + addq t3, 8, t3 + addq a1, 8, a1 // advance buffer address to next qword + bic a1, 7, a1 // + subq a2, t3, t2 + blt t2, 55f // if ltz, too many, jump to residual code + + addq v0, t0, v0 // add bytes to partial checksum + cmpult v0, t0, t1 // generate carry + addq t1, v0, v0 // add carry back into checksum + + bis t2, zero, a2 // reduce count of bytes to checksum + beq t2, 60f // if eq, no more bytes + +20: +// +// Compute the checksum in 64-byte blocks +// + bic a2, 7, t4 // subtract out residual bytes + beq t4, 40f // if eq, no quadwords to checksum + subq zero, t4, t2 // compute negative of byte count + and t2, 15 << 2, t3 // compute bytes in first iteration + ldq t0, 0(a1) // get first quadword to checksum + beq t3, 35f // if eq, full 64-byte block + subq a1, t3, a1 // bias buffer address by offset + bic t4, 64-1, t4 // subtract out bytes in first iteration + lda t2, 30f // get base address of code vector + addl t3, t3, t3 // + addq t3, t2, t2 // compute code vector offset + bis t0, zero, t1 // copy first quadword to checksum + jmp (t2) // dispatch + + +30: +// +// The following code vector computes the checksum of a 64-byte block. +// +.set noreorder + ldq t1, 8(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 16(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 24(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 32(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 40(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 48(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 56(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + addq a1, 64, a1 + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 +.set reorder + + beq t4, 40f // if zero, end of block + +35: + ldq t0, 0(a1) +// +// The following loop is allowed to be reordered by the assembler for +// optimal scheduling. It is never branched into. +// + subq t4, 64, t4 // reduce byte count of longwords + + ldq t1, 8(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 16(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 24(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 32(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 40(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + ldq t0, 48(a1) + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + ldq t1, 56(a1) + addq v0, t0, v0 + cmpult v0, t0, t2 + addq v0, t2, v0 + + addq a1, 64, a1 + addq v0, t1, v0 + cmpult v0, t1, t2 + addq v0, t2, v0 + + bne t4, 35b // if ne zero, not end of block + +40: +// +// Check for any remaining bytes. +// + and a2, 7, a2 // isolate residual bytes + beq a2, 60f // if eq, no residual bytes +50: +// +// Checksum remaining bytes. +// +// The technique we use here is to load the final quadword, then +// zero out the bytes that are not included. +// + ldq t0, 0(a1) // get quadword surrounding remainder +55: + ornot zero, zero, t1 // get FF mask + sll t1, a2, t2 // shift to produce byte mask + zap t0, t2, t0 // zero out bytes past end of buffer + addq v0, t0, v0 // add quadword to partial checksum + cmpult v0, t0, t1 // generate carry + addq t1, v0, v0 // add carry back into checksum +60: +// +// Byte swap the 64-bit checksum if the start of the buffer was not word aligned +// + blbc t6, 65f + zap v0, 0xAA, t0 // isolate even bytes + sll t0, 8, t0 // shift even bytes into odd positions + srl v0, 8, t1 // shift odd bytes into even positions + zap t1, 0xAA, t1 // isolate odd bytes + bis t0, t1, v0 // merge bytes back together + +65: +// +// add computed checksum to original checksum, and fold the 64-bit +// result down to 16 bits. +// + addq v0, a0, v0 // add computed checksum to original + cmpult v0, a0, t0 // generate carry + addq v0, t0, v0 // add carry back into checksum + +// +// swap the longwords in order to sum two longwords and their carry in one add. +// + sll v0, 32, t0 // shift low longword into high + srl v0, 32, t1 // shift high longword into low + bis t1, t0, t5 // merge back together + + addq v0, t5, t0 // produce sum + carry in high longword + srl t0, 32, t1 // shift back down to low half +// +// swap the words in order to sum two words and their carry in one add +// + sll t1, 16, t2 // shift high word into low + srl t1, 16, t3 // shift low word into high + bis t2, t3, t4 // merge back together + addq t4, t1, t2 // produce sum and carry in high word + extwl t2, 2, v0 // extract result. + ret zero, (ra) // return + + .end tcpxsum + diff --git a/private/ntos/tdi/tcpip/tcp/dgram.c b/private/ntos/tdi/tcpip/tcp/dgram.c new file mode 100644 index 000000000..c0ada18c5 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/dgram.c @@ -0,0 +1,990 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** DGRAM.C - Common datagram protocol code. +// +// This file contains the code common to both UDP and Raw IP. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" +#ifdef VXD +#include "tdivxd.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "dgram.h" +#include "tlcommon.h" +#include "info.h" + +#define NO_TCP_DEFS 1 +#include "tcpdeb.h" + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'dPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'dPCT') + +#endif // POOL_TAGGING + +#endif // NT + +#define NUM_DG_HEADERS 5 + +#ifdef NT +#define DG_MAX_HDRS 0xffff +#else +#define DG_MAX_HDRS 100 +#endif + +ulong DGCurrentSendFree = 0; +ulong DGMaxSendFree = DG_MAX_HDRS; + +EXTERNAL_LOCK(AddrObjTableLock) + +DGSendReq *DGSendReqFree; +DEFINE_LOCK_STRUCTURE(DGSendReqLock) + +#ifndef NT +DGRcvReq *DGRcvReqFree; +#else +SLIST_HEADER DGRcvReqFree; +#endif + +DEFINE_LOCK_STRUCTURE(DGRcvReqFreeLock) + +#ifdef DEBUG +uint NumSendReq = 0; +uint NumRcvReq = 0; +#endif + +// Information for maintaining the DG Header structures and +// pending queue. +uint DGHeaderSize; +PNDIS_BUFFER DGHeaderList; +Queue DGHeaderPending; +Queue DGDelayed; + +CTEEvent DGDelayedEvent; + +extern IPInfo LocalNetInfo; + +typedef struct DGHdrBPoolEntry { + struct DGHdrBPoolEntry *uhe_next; + NDIS_HANDLE uhe_handle; + uchar *uhe_buffer; +} DGHdrBPoolEntry; + +DGHdrBPoolEntry *DGHdrBPoolList = NULL; + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitDG(uint MaxHeaderSize); + +#pragma alloc_text(INIT, InitDG) + +#endif // ALLOC_PRAGMA +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + + + +//* GrowDGHeaderList - Try to grow the DG header list. +// +// Called when we run out of buffers on the DG header list, and need +// to grow it. We look to see if we're already at the maximum size, and +// if not we'll allocate the need structures and free them to the list. +// This routine must be called with the SendReq lock held. +// +// Input: Nothing. +// +// Returns: A pointer to a new DG header buffer if we have one, or NULL. +// +PNDIS_BUFFER +GrowDGHeaderList(void) +{ + DGHdrBPoolEntry *NewEntry; + NDIS_STATUS Status; + uint HeaderSize; + uchar *DGSendHP; + uint i; + PNDIS_BUFFER Buffer; + PNDIS_BUFFER ReturnBuffer = NULL; + + if (DGCurrentSendFree < DGMaxSendFree) { + + // Still room to grow the list. + NewEntry = CTEAllocMem(sizeof(DGHdrBPoolEntry)); + + if (NewEntry == NULL) { + // Couldn't get the memory. + return NULL; + } + + NdisAllocateBufferPool(&Status, &NewEntry->uhe_handle, + NUM_DG_HEADERS); + + if (Status != NDIS_STATUS_SUCCESS) { + // Couldn't get a new set of buffers. Fail. + CTEFreeMem(NewEntry); + return NULL; + } + + HeaderSize = DGHeaderSize + LocalNetInfo.ipi_hsize; + + DGSendHP = CTEAllocMem(HeaderSize * NUM_DG_HEADERS); + + if (DGSendHP == NULL) { + NdisFreeBufferPool(NewEntry->uhe_handle); + CTEFreeMem(NewEntry); + return NULL; + } + + NewEntry->uhe_buffer = DGSendHP; + + for (i = 0; i < NUM_DG_HEADERS; i++) { + NdisAllocateBuffer(&Status, &Buffer, NewEntry->uhe_handle, + DGSendHP + (i * HeaderSize), HeaderSize); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(NewEntry->uhe_handle); + CTEFreeMem(NewEntry); + CTEFreeMem(DGSendHP); + return NULL; + } + if (i != 0) + FreeDGHeader(Buffer); + else + ReturnBuffer = Buffer; + } + + DGCurrentSendFree += NUM_DG_HEADERS; + NewEntry->uhe_next = DGHdrBPoolList; + DGHdrBPoolList = NewEntry; + + } else { + // At the limit already. + ReturnBuffer = NULL; + } + + return ReturnBuffer; + + +} +//* GetDGHeader - Get a DG header buffer. +// +// The get header buffer routine. Called with the SendReqLock held. +// +// Input: Nothing. +// +// Output: A pointer to an NDIS buffer, or NULL. +// +_inline PNDIS_BUFFER +GetDGHeader(void) +{ + PNDIS_BUFFER NewBuffer; + + NewBuffer = DGHeaderList; + if (NewBuffer != NULL) + DGHeaderList = NDIS_BUFFER_LINKAGE(NewBuffer); + else + NewBuffer = GrowDGHeaderList(); + + return NewBuffer; +} + +//* FreeDGHeader - Free a DG header buffer. +// +// The free header buffer routine. Called with the SendReqLock held. +// +// Input: Buffer to be freed. +// +// Output: Nothing. +// +void +FreeDGHeader(PNDIS_BUFFER FreedBuffer) +{ + NDIS_BUFFER_LINKAGE(FreedBuffer) = DGHeaderList; + DGHeaderList = FreedBuffer; +} + +//* PutPendingQ - Put an address object on the pending queue. +// +// Called when we've experienced a header buffer out of resources condition, +// and want to queue an AddrObj for later processing. We put the specified +// address object on the DGHeaderPending queue, set the OOR flag and clear +// the 'send request' flag. It is invariant in the system that the send +// request flag and the OOR flag are not set at the same time. +// +// This routine assumes that the caller holds the DGSendReqLock and the +// lock on the particular AddrObj. +// +// Input: QueueingAO - Pointer to address object to be queued. +// +// Returns: Nothing. +// +void +PutPendingQ(AddrObj *QueueingAO) +{ + CTEStructAssert(QueueingAO, ao); + + if (!AO_OOR(QueueingAO)) { + CLEAR_AO_REQUEST(QueueingAO, AO_SEND); + SET_AO_OOR(QueueingAO); + + ENQUEUE(&DGHeaderPending, &QueueingAO->ao_pendq); + } +} + +//* GetDGSendReq - Get a DG send request. +// +// Called when someone wants to allocate a DG send request. We assume +// the send request lock is held when we are called. +// +// Note: This routine and the corresponding free routine might +// be good candidates for inlining. +// +// Input: Nothing. +// +// Returns: Pointer to the SendReq, or NULL if none. +// +DGSendReq * +GetDGSendReq() +{ + DGSendReq *NewReq; + + + NewReq = DGSendReqFree; + if (NewReq != NULL) { + CTEStructAssert(NewReq, dsr); + DGSendReqFree = (DGSendReq *)NewReq->dsr_q.q_next; + } else { + // Couldn't get a request, grow it. This is one area where we'll try + // to allocate memory with a lock held. Because of this, we've + // got to be careful about where we call this routine from. + + NewReq = CTEAllocMem(sizeof(DGSendReq)); + if (NewReq != NULL) { +#ifdef DEBUG + NewReq->dsr_sig = dsr_signature; + NumSendReq++; +#endif + } + } + + return NewReq; +} + +//* FreeDGSendReq - Free a DG send request. +// +// Called when someone wants to free a DG send request. It's assumed +// that the caller holds the SendRequest lock. +// +// Input: SendReq - SendReq to be freed. +// +// Returns: Nothing. +// +void +FreeDGSendReq(DGSendReq *SendReq) +{ + CTEStructAssert(SendReq, dsr); + + *(DGSendReq **)&SendReq->dsr_q.q_next = DGSendReqFree; + DGSendReqFree = SendReq; +} + +//* GetDGRcvReq - Get a DG receive request. +// +// Called when we need to get a DG receive request. +// +// Input: Nothing. +// +// Returns: Pointer to new request, or NULL if none. +// +DGRcvReq * +GetDGRcvReq() +{ + DGRcvReq *NewReq; + +#ifdef VXD + NewReq = DGRcvReqFree; + if (NewReq != NULL) { + CTEStructAssert(NewReq, drr); + DGRcvReqFree = (DGRcvReq *)NewReq->drr_q.q_next; + } else { + // Couldn't get a request, grow it. + NewReq = CTEAllocMem(sizeof(DGRcvReq)); + if (NewReq != NULL) { +#ifdef DEBUG + NewReq->drr_sig = drr_signature; + NumRcvReq++; +#endif + } + } + +#endif // VXD + +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + Queue *QueuePtr; + + BufferLink = ExInterlockedPopEntrySList( + &DGRcvReqFree, + &DGRcvReqFreeLock + ); + + if (BufferLink != NULL) { + QueuePtr = STRUCT_OF(Queue, BufferLink, q_next); + NewReq = STRUCT_OF(DGRcvReq, QueuePtr, drr_q); + CTEStructAssert(NewReq, drr); + } + else { + // Couldn't get a request, grow it. + NewReq = CTEAllocMem(sizeof(DGRcvReq)); + if (NewReq != NULL) { +#ifdef DEBUG + NewReq->drr_sig = drr_signature; + ExInterlockedAddUlong(&NumRcvReq, 1, &DGRcvReqFreeLock); +#endif + } + } + +#endif // NT + + return NewReq; +} + +//* FreeDGRcvReq - Free a DG rcv request. +// +// Called when someone wants to free a DG rcv request. +// +// Input: RcvReq - RcvReq to be freed. +// +// Returns: Nothing. +// +void +FreeDGRcvReq(DGRcvReq *RcvReq) +{ +#ifdef VXD + + CTEStructAssert(RcvReq, drr); + + *(DGRcvReq **)&RcvReq->drr_q.q_next = DGRcvReqFree; + DGRcvReqFree = RcvReq; + +#endif // VXD + +#ifdef NT + + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(RcvReq, drr); + + BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(RcvReq->drr_q.q_next), Next); + ExInterlockedPushEntrySList( + &DGRcvReqFree, + BufferLink, + &DGRcvReqFreeLock + ); + +#endif // NT +} + + +//* DGDelayedEventProc - Handle a delayed event. +// +// This is the delayed event handler, used for out-of-resources conditions +// on AddrObjs. We pull from the delayed queue, and is the addr obj is +// not already busy we'll send the datagram. +// +// Input: Event - Pointer to the event structure. +// Context - Nothing. +// +// Returns: Nothing +// +void +DGDelayedEventProc(CTEEvent *Event, void *Context) +{ + CTELockHandle HeaderHandle, AOHandle; + AddrObj *SendingAO; + DGSendProc SendProc; + + CTEGetLock(&DGSendReqLock, &HeaderHandle); + while (!EMPTYQ(&DGDelayed)) { + DEQUEUE(&DGDelayed, SendingAO, AddrObj, ao_pendq); + CTEStructAssert(SendingAO, ao); + + CTEGetLock(&SendingAO->ao_lock, &AOHandle); + + CLEAR_AO_OOR(SendingAO); + if (!AO_BUSY(SendingAO)) { + DGSendReq *SendReq; + + if (!EMPTYQ(&SendingAO->ao_sendq)) { + DEQUEUE(&SendingAO->ao_sendq, SendReq, DGSendReq, dsr_q); + + CTEStructAssert(SendReq, dsr); + CTEAssert(SendReq->dsr_header != NULL); + + SendingAO->ao_usecnt++; + SendProc = SendingAO->ao_dgsend; + CTEFreeLock(&SendingAO->ao_lock, AOHandle); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + + (*SendProc)(SendingAO, SendReq); + DEREF_AO(SendingAO); + CTEGetLock(&DGSendReqLock, &HeaderHandle); + } else { + CTEAssert(FALSE); + CTEFreeLock(&SendingAO->ao_lock, AOHandle); + } + + } else { + SET_AO_REQUEST(SendingAO, AO_SEND); + CTEFreeLock(&SendingAO->ao_lock, AOHandle); + } + } + + CTEFreeLock(&DGSendReqLock, HeaderHandle); + +} + +//* DGSendComplete - DG send complete handler. +// +// This is the routine called by IP when a send completes. We +// take the context passed back as a pointer to a SendRequest +// structure, and complete the caller's send. +// +// Input: Context - Context we gave on send (really a +// SendRequest structure). +// BufferChain - Chain of buffers sent. +// +// Returns: Nothing. +void +DGSendComplete(void *Context, PNDIS_BUFFER BufferChain) +{ + DGSendReq *FinishedSR = (DGSendReq *)Context; + CTELockHandle HeaderHandle, AOHandle; + CTEReqCmpltRtn Callback; // Completion routine. + PVOID CallbackContext; // User context. + ushort SentSize; + AddrObj *AO; + + CTEStructAssert(FinishedSR, dsr); + CTEGetLock(&DGSendReqLock, &HeaderHandle); + + Callback = FinishedSR->dsr_rtn; + CallbackContext = FinishedSR->dsr_context; + SentSize = FinishedSR->dsr_size; + + // If there's nothing on the header pending queue, just free the + // header buffer. Otherwise pull from the pending queue, give him the + // resource, and schedule an event to deal with him. + if (EMPTYQ(&DGHeaderPending)) { + FreeDGHeader(BufferChain); + } else { + DEQUEUE(&DGHeaderPending, AO, AddrObj, ao_pendq); + CTEStructAssert(AO, ao); + CTEGetLock(&AO->ao_lock, &AOHandle); + if (!EMPTYQ(&AO->ao_sendq)) { + DGSendReq *SendReq; + + PEEKQ(&AO->ao_sendq, SendReq, DGSendReq, dsr_q); + SendReq->dsr_header = BufferChain; // Give him this buffer. + + ENQUEUE(&DGDelayed, &AO->ao_pendq); + CTEFreeLock(&AO->ao_lock, AOHandle); + CTEScheduleEvent(&DGDelayedEvent, NULL); + } else { + // On the pending queue, but no sends! + DEBUGCHK; + CLEAR_AO_OOR(AO); + CTEFreeLock(&AO->ao_lock, AOHandle); + } + + } + + FreeDGSendReq(FinishedSR); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + if (Callback != NULL) + (*Callback)(CallbackContext, TDI_SUCCESS, (uint)SentSize); + +} + + +#ifdef NT +// +// NT supports cancellation of DG send/receive requests. +// + +#define TCP_DEBUG_SEND_DGRAM 0x00000100 +#define TCP_DEBUG_RECEIVE_DGRAM 0x00000200 + +extern ULONG TCPDebug; + + +VOID +TdiCancelSendDatagram( + AddrObj *SrcAO, + PVOID Context + ) +{ + CTELockHandle lockHandle; + DGSendReq *sendReq = NULL; + Queue *qentry; + BOOLEAN found = FALSE; + + + CTEStructAssert(SrcAO, ao); + + CTEGetLock(&SrcAO->ao_lock, &lockHandle); + + // Search the send list for the specified request. + for ( qentry = QNEXT(&(SrcAO->ao_sendq)); + qentry != &(SrcAO->ao_sendq); + qentry = QNEXT(qentry) + ) { + + sendReq = STRUCT_OF(DGSendReq, qentry, dsr_q); + + CTEStructAssert(sendReq, dsr); + + if (sendReq->dsr_context == Context) { + // + // Found it. Dequeue + // + REMOVEQ(qentry); + found = TRUE; + + IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) { + TCPTRACE(( + "TdiCancelSendDatagram: Dequeued item %lx\n", + Context + )); + } + + break; + } + } + + CTEFreeLock(&SrcAO->ao_lock, lockHandle); + + if (found) { + // + // Complete the request and free its resources. + // + (*sendReq->dsr_rtn)(sendReq->dsr_context, (uint) TDI_CANCELLED, 0); + + CTEGetLock(&DGSendReqLock, &lockHandle); + + if (sendReq->dsr_header != NULL) { + FreeDGHeader(sendReq->dsr_header); + } + + FreeDGSendReq(sendReq); + + CTEFreeLock(&DGSendReqLock, lockHandle); + } + +} // TdiCancelSendDatagram + + +VOID +TdiCancelReceiveDatagram( + AddrObj *SrcAO, + PVOID Context + ) +{ + CTELockHandle lockHandle; + DGRcvReq *rcvReq = NULL; + Queue *qentry; + BOOLEAN found = FALSE; + + + CTEStructAssert(SrcAO, ao); + + CTEGetLock(&SrcAO->ao_lock, &lockHandle); + + // Search the send list for the specified request. + for ( qentry = QNEXT(&(SrcAO->ao_rcvq)); + qentry != &(SrcAO->ao_rcvq); + qentry = QNEXT(qentry) + ) { + + rcvReq = STRUCT_OF(DGRcvReq, qentry, drr_q); + + CTEStructAssert(rcvReq, drr); + + if (rcvReq->drr_context == Context) { + // + // Found it. Dequeue + // + REMOVEQ(qentry); + found = TRUE; + + IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) { + TCPTRACE(( + "TdiCancelReceiveDatagram: Dequeued item %lx\n", + Context + )); + } + + break; + } + } + + CTEFreeLock(&SrcAO->ao_lock, lockHandle); + + if (found) { + // + // Complete the request and free its resources. + // + (*rcvReq->drr_rtn)(rcvReq->drr_context, (uint) TDI_CANCELLED, 0); + + FreeDGRcvReq(rcvReq); + } + +} // TdiCancelReceiveDatagram + + +#endif // NT + + +//** TdiSendDatagram - TDI send datagram function. +// +// This is the user interface to the send datagram function. The +// caller specified a request structure, a connection info +// structure containing the address, and data to be sent. +// This routine gets a DG Send request structure to manage the +// send, fills the structure in, and calls DGSend to deal with +// it. +// +// Input: Request - Pointer to request structure. +// ConnInfo - Pointer to ConnInfo structure which points to +// remote address. +// DataSize - Size in bytes of data to be sent. +// BytesSent - Pointer to where to return size sent. +// Buffer - Pointer to buffer chain. +// +// Returns: Status of attempt to send. +// +TDI_STATUS +TdiSendDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo, + uint DataSize, uint *BytesSent, PNDIS_BUFFER Buffer) +{ + AddrObj *SrcAO; // Pointer to AddrObj for src. + DGSendReq *SendReq; // Pointer to send req for this request. + CTELockHandle Handle, SRHandle; // Lock handles for the AO and the + // send request. + TDI_STATUS ReturnValue; + DGSendProc SendProc; + + // First, get a send request. We do this first because of MP issues + // if we port this to NT. We need to take the SendRequest lock before + // we take the AddrObj lock, to prevent deadlock and also because + // GetDGSendReq might yield, and the state of the AddrObj might + // change on us, so we don't want to yield after we've validated + // it. + + CTEGetLock(&DGSendReqLock, &SRHandle); + SendReq = GetDGSendReq(); + + // Now get the lock on the AO, and make sure it's valid. We do this + // to make sure we return the correct error code. + +#ifdef VXD + SrcAO = GetIndexedAO((uint)Request->Handle.AddressHandle); + + if (SrcAO != NULL) { +#else + SrcAO = Request->Handle.AddressHandle; +#endif + + CTEStructAssert(SrcAO, ao); + + CTEGetLock(&SrcAO->ao_lock, &Handle); + + if (AO_VALID(SrcAO)) { + + // Make sure the size is reasonable. + if (DataSize <= SrcAO->ao_maxdgsize) { + + // The AddrObj is valid. Now fill the address into the send request, + // if we've got one. If this works, we'll continue with the + // send. + + if (SendReq != NULL) { // Got a send request. + if (GetAddress(ConnInfo->RemoteAddress, &SendReq->dsr_addr, + &SendReq->dsr_port)) { + + SendReq->dsr_rtn = Request->RequestNotifyObject; + SendReq->dsr_context = Request->RequestContext; + SendReq->dsr_buffer = Buffer; + SendReq->dsr_size = (ushort)DataSize; + + // We've filled in the send request. If the AO isn't + // already busy, try to get a DG header buffer and send + // this. If the AO is busy, or we can't get a buffer, queue + // until later. We try to get the header buffer here, as + // an optimazation to avoid having to retake the lock. + + if (!AO_OOR(SrcAO)) { // AO isn't out of resources + if (!AO_BUSY(SrcAO)) { // or or busy + + if ((SendReq->dsr_header = GetDGHeader()) != NULL) { + REF_AO(SrcAO); // Lock out exclusive + // activities. + SendProc = SrcAO->ao_dgsend; + + CTEFreeLock(&SrcAO->ao_lock, Handle); + CTEFreeLock(&DGSendReqLock, SRHandle); + + // Allright, just send it. + (*SendProc)(SrcAO, SendReq); + + // See if any pending requests occured during + // the send. If so, call the request handler. + DEREF_AO(SrcAO); + + return TDI_PENDING; + } else { + // We couldn't get a header buffer. Put this + // guy on the pending queue, and then fall + // through to the 'queue request' code. + PutPendingQ(SrcAO); + } + } else { + // AO is busy, set request for later + SET_AO_REQUEST(SrcAO, AO_SEND); + } + } + + // AO is busy, or out of resources. Queue the send request + // for later. + SendReq->dsr_header = NULL; + ENQUEUE(&SrcAO->ao_sendq, &SendReq->dsr_q); + SendReq = NULL; + ReturnValue = TDI_PENDING; + } + else { + // The remote address was invalid. + ReturnValue = TDI_BAD_ADDR; + } + } + else { + // Send request was null, return no resources. + ReturnValue = TDI_NO_RESOURCES; + } + } + else { + // Buffer was too big, return an error. + ReturnValue = TDI_BUFFER_TOO_BIG; + } + } + else { + // The addr object is invalid, possibly because it's deleting. + ReturnValue = TDI_ADDR_INVALID; + } + + CTEFreeLock(&SrcAO->ao_lock, Handle); + +#ifdef VXD + } + else { + ReturnValue = TDI_ADDR_INVALID; + } +#endif + + if (SendReq != NULL) + FreeDGSendReq(SendReq); + + CTEFreeLock(&DGSendReqLock, SRHandle); + + return TDI_ADDR_INVALID; +} + +//** TdiReceiveDatagram - TDI receive datagram function. +// +// This is the user interface to the receive datagram function. The +// caller specifies a request structure, a connection info +// structure that acts as a filter on acceptable datagrams, a connection +// info structure to be filled in, and other parameters. We get a DGRcvReq +// structure, fill it in, and hang it on the AddrObj, where it will be removed +// later by incomig datagram handler. +// +// Input: Request - Pointer to request structure. +// ConnInfo - Pointer to ConnInfo structure which points to +// remote address. +// ReturnInfo - Pointer to ConnInfo structure to be filled in. +// RcvSize - Total size in bytes receive buffer. +// BytesRcvd - Pointer to where to return size received. +// Buffer - Pointer to buffer chain. +// +// Returns: Status of attempt to receive. +// +TDI_STATUS +TdiReceiveDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize, uint *BytesRcvd, + PNDIS_BUFFER Buffer) +{ + AddrObj *RcvAO; // AddrObj that is receiving. + DGRcvReq *RcvReq; // Receive request structure. + CTELockHandle AOHandle; + uchar AddrValid; + + RcvReq = GetDGRcvReq(); + +#ifdef VXD + RcvAO = GetIndexedAO((uint)Request->Handle.AddressHandle); + + if (RcvAO != NULL) { + CTEStructAssert(RcvAO, ao); + +#else + RcvAO = Request->Handle.AddressHandle; + CTEStructAssert(RcvAO, ao); +#endif + + CTEGetLock(&RcvAO->ao_lock, &AOHandle); + if (AO_VALID(RcvAO)) { + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("posting receive on AO %lx\n", RcvAO)); + } + + if (RcvReq != NULL) { + if (ConnInfo != NULL && ConnInfo->RemoteAddressLength != 0) + AddrValid = GetAddress(ConnInfo->RemoteAddress, + &RcvReq->drr_addr, &RcvReq->drr_port); + else { + AddrValid = TRUE; + RcvReq->drr_addr = NULL_IP_ADDR; + RcvReq->drr_port = 0; + } + + if (AddrValid) { + + // Everything'd valid. Fill in the receive request and queue it. + RcvReq->drr_conninfo = ReturnInfo; + RcvReq->drr_rtn = Request->RequestNotifyObject; + RcvReq->drr_context = Request->RequestContext; + RcvReq->drr_buffer = Buffer; + RcvReq->drr_size = RcvSize; + ENQUEUE(&RcvAO->ao_rcvq, &RcvReq->drr_q); + CTEFreeLock(&RcvAO->ao_lock, AOHandle); + + return TDI_PENDING; + } else { + // Have an invalid filter address. + CTEFreeLock(&RcvAO->ao_lock, AOHandle); + FreeDGRcvReq(RcvReq); + return TDI_BAD_ADDR; + } + } else { + // Couldn't get a receive request. + CTEFreeLock(&RcvAO->ao_lock, AOHandle); + return TDI_NO_RESOURCES; + } + } else { + // The AddrObj isn't valid. + CTEFreeLock(&RcvAO->ao_lock, AOHandle); + } + +#ifdef VXD + } +#endif + + // The AddrObj is invalid or non-existent. + if (RcvReq != NULL) + FreeDGRcvReq(RcvReq); + + return TDI_ADDR_INVALID; +} + + +#pragma BEGIN_INIT + +//* InitDG - Initialize the DG stuff. +// +// Called during init time to initalize the DG code. We initialize +// our locks and request lists. +// +// Input: MaxHeaderSize - The maximum size of a datagram transport header, +// not including the IP header. +// +// Returns: True if we succeed, False if we fail. +// +int +InitDG(uint MaxHeaderSize) +{ + PNDIS_BUFFER Buffer; + CTELockHandle Handle; + + + DGHeaderSize = MaxHeaderSize; + + CTEInitLock(&DGSendReqLock); + CTEInitLock(&DGRcvReqFreeLock); + + DGSendReqFree = NULL; + +#ifndef NT + DGRcvReqFree = NULL; +#else + ExInitializeSListHead(&DGRcvReqFree); +#endif + + + CTEGetLock(&DGSendReqLock, &Handle); + + Buffer = GrowDGHeaderList(); + + if (Buffer != NULL) { + FreeDGHeader(Buffer); + CTEFreeLock(&DGSendReqLock, Handle); + } else { + CTEFreeLock(&DGSendReqLock, Handle); + return FALSE; + } + + INITQ(&DGHeaderPending); + INITQ(&DGDelayed); + + CTEInitEvent(&DGDelayedEvent, DGDelayedEventProc); + + return TRUE; +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/tcp/dgram.h b/private/ntos/tdi/tcpip/tcp/dgram.h new file mode 100644 index 000000000..a637a75e3 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/dgram.h @@ -0,0 +1,89 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** DGRAM.H - Common datagram protocol definitions. +// +// This file contains definitions for the functions common to +// both UDP and Raw IP. +// + +#ifndef _DGRAM_INCLUDED_ +#define _DGRAM_INCLUDED_ 1 + + +//* Structure used for maintaining DG send requests. + +#define dsr_signature 0x20525338 + +struct DGSendReq { +#ifdef DEBUG + ulong dsr_sig; +#endif + Queue dsr_q; // Queue linkage when pending. + IPAddr dsr_addr; // Remote IPAddr. + PNDIS_BUFFER dsr_buffer; // Buffer of data to send. + PNDIS_BUFFER dsr_header; // Pointer to header buffer. + CTEReqCmpltRtn dsr_rtn; // Completion routine. + PVOID dsr_context; // User context. + ushort dsr_size; // Size of buffer. + ushort dsr_port; // Remote port. +}; /* DGSendReq */ + +typedef struct DGSendReq DGSendReq; + +//* Structure used for maintaining DG receive requests. + +#define drr_signature 0x20525238 + +struct DGRcvReq { +#ifdef DEBUG + ulong drr_sig; +#endif + Queue drr_q; // Queue linkage on AddrObj. + IPAddr drr_addr; // Remote IPAddr acceptable. + PNDIS_BUFFER drr_buffer; // Buffer to be filled in. + PTDI_CONNECTION_INFORMATION drr_conninfo; // Pointer to conn. info. + CTEReqCmpltRtn drr_rtn; // Completion routine. + PVOID drr_context; // User context. + ushort drr_size; // Size of buffer. + ushort drr_port; // Remote port acceptable. +}; /* DGRcvReq */ + +typedef struct DGRcvReq DGRcvReq; + + +//* External definition of exported variables. +EXTERNAL_LOCK(DGSendReqLock) +EXTERNAL_LOCK(DGRcvReqFreeLock) +extern CTEEvent DGDelayedEvent; + + +//* External definition of exported functions. +extern void DGSendComplete(void *Context, PNDIS_BUFFER BufferChain); + +extern TDI_STATUS TdiSendDatagram(PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, uint DataSize, + uint *BytesSent, PNDIS_BUFFER Buffer); + +extern TDI_STATUS TdiReceiveDatagram(PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize, + uint *BytesRcvd, PNDIS_BUFFER Buffer); + +extern IP_STATUS DGRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo); + +extern void FreeDGRcvReq(DGRcvReq *RcvReq); +extern void FreeDGSendReq(DGSendReq *SendReq); +extern int InitDG(uint MaxHeaderSize); +extern _inline PNDIS_BUFFER GetDGHeader(void); +extern void FreeDGHeader(PNDIS_BUFFER FreedBuffer); +extern void PutPendingQ(AddrObj *QueueingAO); + + +#endif // ifndef _DGRAM_INCLUDED_ + diff --git a/private/ntos/tdi/tcpip/tcp/dirs b/private/ntos/tdi/tcpip/tcp/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/tcpip/tcp/i386/sources b/private/ntos/tdi/tcpip/tcp/i386/sources new file mode 100644 index 000000000..9c19718ea --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/i386/sources @@ -0,0 +1,4 @@ +i386_SOURCES= \ + ..\i386\xsum.asm + + diff --git a/private/ntos/tdi/tcpip/tcp/i386/xsum.asm b/private/ntos/tdi/tcpip/tcp/i386/xsum.asm new file mode 100644 index 000000000..7cb03bce2 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/i386/xsum.asm @@ -0,0 +1,259 @@ + title "Compute Checksum" + +;/*++ +; +; Copyright (c) 1992 Microsoft Corporation +; +; Module Name: +; +; cksy.asm +; +; Abstract: +; +; This module implements a function to compute the checksum of a buffer. +; +; Author: +; +; David N. Cutler (davec) 27-Jan-1992 +; +; Revision History: +; +; Who When What +; -------- -------- ---------------------------------------------- +; mikeab 01-22-94 Pentium optimization +; +; Environment: +; +; Any mode. +; +; Revision History: +; +;--*/ + +LOOP_UNROLLING_BITS equ 5 +LOOP_UNROLLING equ (1 SHL LOOP_UNROLLING_BITS) + + .386 + .model small,c + + assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT + assume fs:nothing,gs:nothing + + .xlist + include callconv.inc + include ks386.inc + .list + + .code + +;++ +; +; ULONG +; tcpxsum( +; IN ULONG cksum, +; IN PUCHAR buf, +; IN ULONG len +; ) +; +; Routine Description: +; +; This function computes the checksum of the specified buffer. +; +; Arguments: +; +; cksum - Suppiles the initial checksum value, in 16-bit form, +; with the high word set to 0. +; +; buf - Supplies a pointer to the buffer to the checksum buffer. +; +; len - Supplies the length of the buffer in bytes. +; +; Return Value: +; +; The computed checksum in 32-bit two-partial-accumulators form, added to +; the initial checksum, is returned as the function value. +; +;-- + +cksum equ 12 ; stack offset to initial checksum +buf equ 16 ; stack offset to source address +len equ 20 ; stack offset to length in words + +to_checksum_last_word: + jmp checksum_last_word + +to_checksum_done: + jmp checksum_done + +to_checksum_dword_loop_done: + jmp checksum_dword_loop_done + +cPublicProc tcpxsum,3 + + push ebx ; save nonvolatile register + push esi ; save nonvolatile register + + mov ecx,[esp + len] ; get length in bytes + sub eax,eax ; clear computed checksum + test ecx,ecx ; any bytes to checksum at all? + jz short to_checksum_done ; no bytes to checksum + +; +; if the checksum buffer is not word aligned, then add the first byte of +; the buffer to the input checksum. +; + + mov esi,[esp + buf] ; get source address + sub edx,edx ; set up to load word into EDX below + test esi,1 ; check if buffer word aligned + jz short checksum_word_aligned ; if zf, buffer word aligned + mov ah,[esi] ; get first byte (we know we'll have + ; to swap at the end) + inc esi ; increment buffer address + dec ecx ; decrement number of bytes + jz short to_checksum_done ; if zf set, no more bytes + +; +; If the buffer is not an even number of of bytes, then initialize +; the computed checksum with the last byte of the buffer. +; + +checksum_word_aligned: ; + shr ecx,1 ; convert to word count + jnc short checksum_start ; if nc, even number of bytes + mov al,[esi+ecx*2] ; initialize the computed checksum + jz short to_checksum_done ; if zf set, no more bytes + +; +; Compute checksum in large blocks of dwords, with one partial word up front if +; necessary to get dword alignment, and another partial word at the end if +; needed. +; + +; +; Compute checksum on the leading word, if that's necessary to get dword +; alignment. +; + +checksum_start: ; + test esi,02h ; check if source dword aligned + jz short checksum_dword_aligned ; source is already dword aligned + mov dx,[esi] ; get first word to checksum + add esi,2 ; update source address + add eax,edx ; update partial checksum + ; (no carry is possible, because EAX + ; and EDX are both 16-bit values) + dec ecx ; count off this word (zero case gets + ; picked up below) + +; +; Checksum as many words as possible by processing a dword at a time. +; + +checksum_dword_aligned: + push ecx ; so we can tell if there's a trailing + ; word later + shr ecx,1 ; # of dwords to checksum + jz short to_checksum_last_word ; no dwords to checksum + + mov edx,[esi] ; preload the first dword + add esi,4 ; point to the next dword + dec ecx ; count off the dword we just loaded + jz short to_checksum_dword_loop_done + ; skip the loop if that was the only + ; dword + mov ebx,ecx ; EBX = # of dwords left to checksum + add ecx,LOOP_UNROLLING-1 ; round up loop count + shr ecx,LOOP_UNROLLING_BITS ; convert from word count to unrolled + ; loop count + and ebx,LOOP_UNROLLING-1 ; # of partial dwords to do in first + ; loop + jz short checksum_dword_loop ; special-case when no partial loop, + ; because fixup below doesn't work + ; in that case (carry flag is + ; cleared at this point, as required + ; at loop entry) + lea esi,[esi+ebx*4-(LOOP_UNROLLING*4)] + ; adjust buffer pointer back to + ; compensate for hardwired displacement + ; at loop entry point + ; ***doesn't change carry flag*** + jmp loop_entry[ebx*4] ; enter the loop to do the first, + ; partial iteration, after which we can + ; just do 64-word blocks + ; ***doesn't change carry flag*** + +checksum_dword_loop: + +DEFLAB macro pre,suf +pre&suf: + endm + +TEMP=0 + REPT LOOP_UNROLLING + deflab loop_entry_,%TEMP + adc eax,edx + mov edx,[esi + TEMP] +TEMP=TEMP+4 + ENDM + +checksum_dword_loop_end: + + lea esi,[esi + LOOP_UNROLLING * 4] ; update source address + ; ***doesn't change carry flag*** + dec ecx ; count off unrolled loop iteration + ; ***doesn't change carry flag*** + jnz checksum_dword_loop ; do more blocks + +checksum_dword_loop_done label proc + adc eax,edx ; finish dword checksum + mov edx,0 ; prepare to load trailing word + adc eax,edx + +; +; Compute checksum on the trailing word, if there is one. +; High word of EDX = 0 at this point +; Carry flag set iff there's a trailing word to do at this point +; + +checksum_last_word label proc ; "proc" so not scoped to function + pop ecx ; get back word count + test ecx,1 ; is there a trailing word? + jz short checksum_done ; no trailing word + add ax,[esi] ; add in the trailing word + adc eax,0 ; + +checksum_done label proc ; "proc" so not scoped to function + mov ecx,eax ; fold the checksum to 16 bits + ror ecx,16 + add eax,ecx + mov ebx,[esp + buf] + shr eax,16 + test ebx,1 ; check if buffer word aligned + jz short checksum_combine ; if zf set, buffer word aligned + ror ax,8 ; byte aligned--swap bytes back +checksum_combine label proc ; "proc" so not scoped to function + add ax,word ptr [esp + cksum] ; combine checksums + pop esi ; restore nonvolatile register + adc eax,0 ; + pop ebx ; restore nonvolatile register + stdRET tcpxsum + + +REFLAB macro pre,suf + dd pre&suf + endm + + align 4 +loop_entry label dword + dd 0 +TEMP=LOOP_UNROLLING*4 + REPT LOOP_UNROLLING-1 +TEMP=TEMP-4 + reflab loop_entry_,%TEMP + ENDM + +stdENDP tcpxsum + + end + \ No newline at end of file diff --git a/private/ntos/tdi/tcpip/tcp/info.c b/private/ntos/tdi/tcpip/tcp/info.c new file mode 100644 index 000000000..99fff25bf --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/info.c @@ -0,0 +1,917 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INFO.C - TDI Query/SetInformation routines. +// +// This file contains the code for dealing with TDI Query/Set information +// calls. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tlcommon.h" +#include "info.h" +#include "tdiinfo.h" +#include "tcpcfg.h" +#include "udp.h" +#include "tcpsend.h" + +#ifndef UDP_ONLY +#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_ORDERLY_RELEASE | \ + TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY | \ + TDI_SERVICE_BROADCAST_SUPPORTED | \ + TDI_SERVICE_DELAYED_ACCEPTANCE | \ + TDI_SERVICE_EXPEDITED_DATA | \ + TDI_SERVICE_NO_ZERO_LENGTH) +#else +#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_BROADCAST_SUPPORTED) +#endif + +extern uint StartTime; +EXTERNAL_LOCK(AddrObjTableLock) + +#ifndef UDP_ONLY +TCPStats TStats; +#endif + +UDPStats UStats; + +struct ReadTableStruct { + uint (*rts_validate)(void *Context, uint *Valid); + uint (*rts_readnext)(void *Context, void *OutBuf); +}; + +struct ReadTableStruct ReadAOTable = + {ValidateAOContext, ReadNextAO}; + +#ifndef UDP_ONLY + +struct ReadTableStruct ReadTCBTable = + {ValidateTCBContext, ReadNextTCB}; + +EXTERNAL_LOCK(TCBTableLock) +#endif + +EXTERNAL_LOCK(AddrObjTableLock) + +extern IPInfo LocalNetInfo; + +struct TDIEntityID *EntityList; +uint EntityCount; + +//* TdiQueryInformation - Query Information handler. +// +// The TDI QueryInformation routine. Called when the client wants to +// query information on a connection, the provider as a whole, or to +// get statistics. +// +// Input: Request - The request structure for this command. +// QueryType - The type of query to be performed. +// Buffer - Buffer to place data into. +// BufferSize - Pointer to size in bytes of buffer. On return, +// filled in with bytes copied. +// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE +// if we are querying the address info on +// a connection. +// +// Returns: Status of attempt to query information. +// +TDI_STATUS +TdiQueryInformation(PTDI_REQUEST Request, uint QueryType, PNDIS_BUFFER Buffer, + uint *BufferSize, uint IsConn) +{ + union { + TDI_CONNECTION_INFO ConnInfo; + TDI_ADDRESS_INFO AddrInfo; + TDI_PROVIDER_INFO ProviderInfo; + TDI_PROVIDER_STATISTICS ProviderStats; + } InfoBuf; + + uint InfoSize; + CTELockHandle ConnTableHandle, TCBHandle, AddrHandle, AOHandle; +#ifndef UDP_ONLY + TCPConn *Conn; + TCB *InfoTCB; +#endif + AddrObj *InfoAO; + void *InfoPtr = NULL; + uint Offset; + uint Size; + uint BytesCopied; + + switch (QueryType) { + + case TDI_QUERY_BROADCAST_ADDRESS: + return TDI_INVALID_QUERY; + break; + + case TDI_QUERY_PROVIDER_INFO: + InfoBuf.ProviderInfo.Version = 0x100; +#ifndef UDP_ONLY + InfoBuf.ProviderInfo.MaxSendSize = 0xffffffff; +#else + InfoBuf.ProviderInfo.MaxSendSize = 0; +#endif + InfoBuf.ProviderInfo.MaxConnectionUserData = 0; + InfoBuf.ProviderInfo.MaxDatagramSize = 0xffff - sizeof(UDPHeader); + InfoBuf.ProviderInfo.ServiceFlags = MY_SERVICE_FLAGS; + InfoBuf.ProviderInfo.MinimumLookaheadData = 1; + InfoBuf.ProviderInfo.MaximumLookaheadData = 0xffff; + InfoBuf.ProviderInfo.NumberOfResources = 0; + InfoBuf.ProviderInfo.StartTime.LowPart = StartTime; + InfoBuf.ProviderInfo.StartTime.HighPart = 0; + InfoSize = sizeof(TDI_PROVIDER_INFO); + InfoPtr = &InfoBuf.ProviderInfo; + break; + + case TDI_QUERY_ADDRESS_INFO: + InfoSize = sizeof(TDI_ADDRESS_INFO) - sizeof(TRANSPORT_ADDRESS) + + TCP_TA_SIZE; + CTEMemSet(&InfoBuf.AddrInfo, 0, TCP_TA_SIZE); + InfoBuf.AddrInfo.ActivityCount = 1; // Since noone knows what + // this means, we'll set + // it to one. + + if (IsConn) { +#ifdef UDP_ONLY + return TDI_INVALID_QUERY; +#else + + CTEGetLock(&AddrObjTableLock, &AddrHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + InfoTCB = Conn->tc_tcb; + // If we have a TCB we'll + // return information about that TCB. Otherwise we'll return + // info about the address object. + if (InfoTCB != NULL) { + CTEStructAssert(InfoTCB, tcb); + CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEFreeLock(&AddrObjTableLock, ConnTableHandle); + BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, + InfoTCB->tcb_saddr, InfoTCB->tcb_sport); + CTEFreeLock(&InfoTCB->tcb_lock, AddrHandle); + InfoPtr = &InfoBuf.AddrInfo; + break; + } else { + // No TCB, return info on the AddrObj. + InfoAO = Conn->tc_ao; + if (InfoAO != NULL) { + // We have an AddrObj. + CTEStructAssert(InfoAO, ao); + CTEGetLock(&InfoAO->ao_lock, &AOHandle); + BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, + InfoAO->ao_addr, InfoAO->ao_port); + CTEFreeLock(&InfoAO->ao_lock, AOHandle); + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AddrHandle); + InfoPtr = &InfoBuf.AddrInfo; + break; + } + } + + } + + // Fall through to here when we can't find the connection, or + // the connection isn't associated. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AddrHandle); + return TDI_INVALID_CONNECTION; + break; + +#endif + } else { + // Asking for information on an addr. object. +#ifdef VXD + InfoAO = GetIndexedAO((uint)Request->Handle.AddressHandle); + + if (InfoAO == NULL) + return TDI_ADDR_INVALID; +#else + InfoAO = Request->Handle.AddressHandle; +#endif + + CTEStructAssert(InfoAO, ao); + + CTEGetLock(&InfoAO->ao_lock, &AOHandle); + + if (!AO_VALID(InfoAO)) { + CTEFreeLock(&InfoAO->ao_lock, AOHandle); + return TDI_ADDR_INVALID; + } + + BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address, + InfoAO->ao_addr, InfoAO->ao_port); + CTEFreeLock(&InfoAO->ao_lock, AOHandle); + InfoPtr = &InfoBuf.AddrInfo; + break; + } + + break; + + case TDI_QUERY_CONNECTION_INFO: +#ifndef UDP_ONLY + InfoSize = sizeof(TDI_CONNECTION_INFO); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + InfoTCB = Conn->tc_tcb; + // If we have a TCB we'll return the information. Otherwise + // we'll error out. + if (InfoTCB != NULL) { + + ulong TotalTime; + ulong BPS, PathBPS; + IP_STATUS IPStatus; + CTEULargeInt TempULargeInt; + + CTEStructAssert(InfoTCB, tcb); + CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEMemSet(&InfoBuf.ConnInfo, 0, sizeof(TDI_CONNECTION_INFO)); + InfoBuf.ConnInfo.State = (ulong)InfoTCB->tcb_state; + IPStatus = (*LocalNetInfo.ipi_getpinfo)(InfoTCB->tcb_daddr, + InfoTCB->tcb_saddr, NULL, &PathBPS); + + if (IPStatus != IP_SUCCESS) { + InfoBuf.ConnInfo.Throughput.LowPart = 0xFFFFFFFF; + InfoBuf.ConnInfo.Throughput.HighPart = 0xFFFFFFFF; + } else { + InfoBuf.ConnInfo.Throughput.HighPart = 0; + TotalTime = InfoTCB->tcb_totaltime / + (1000 / MS_PER_TICK); + if (TotalTime != 0) { + TempULargeInt.LowPart = InfoTCB->tcb_bcountlow; + TempULargeInt.HighPart = InfoTCB->tcb_bcounthi; + + BPS = CTEEnlargedUnsignedDivide(TempULargeInt, + TotalTime, NULL); + InfoBuf.ConnInfo.Throughput.LowPart = + MIN(BPS, PathBPS); + } else + InfoBuf.ConnInfo.Throughput.LowPart = PathBPS; + } + + + + // To figure the delay we use the rexmit timeout. Our + // rexmit timeout is roughly the round trip time plus + // some slop, so we use half of that as the one way delay. +#ifdef VXD + InfoBuf.ConnInfo.Delay.LowPart = + (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2; + InfoBuf.ConnInfo.Throughput.HighPart = 0; +#else // VXD + InfoBuf.ConnInfo.Delay.LowPart = + (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2; + InfoBuf.ConnInfo.Delay.HighPart = 0; + // + // Convert milliseconds to 100ns and negate for relative + // time. + // + InfoBuf.ConnInfo.Delay = + RtlExtendedIntegerMultiply( + InfoBuf.ConnInfo.Delay, + 10000 + ); + + CTEAssert(InfoBuf.ConnInfo.Delay.HighPart == 0); + + InfoBuf.ConnInfo.Delay.QuadPart = + -InfoBuf.ConnInfo.Delay.QuadPart; + +#endif // VXD + CTEFreeLock(&InfoTCB->tcb_lock, ConnTableHandle); + InfoPtr = &InfoBuf.ConnInfo; + break; + } + + } + + // Come through here if we can't find the connection or it has + // no TCB. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return TDI_INVALID_CONNECTION; + break; + +#else // UDP_ONLY + return TDI_INVALID_QUERY; + break; +#endif // UDP_ONLY + case TDI_QUERY_PROVIDER_STATISTICS: + CTEMemSet(&InfoBuf.ProviderStats, 0, sizeof(TDI_PROVIDER_STATISTICS)); + InfoBuf.ProviderStats.Version = 0x100; + InfoSize = sizeof(TDI_PROVIDER_STATISTICS); + InfoPtr = &InfoBuf.ProviderStats; + break; + default: + return TDI_INVALID_QUERY; + break; + } + + // When we get here, we've got the pointers set up and the information + // filled in. + + CTEAssert(InfoPtr != NULL); + Offset = 0; + Size = *BufferSize; + (void)CopyFlatToNdis(Buffer, InfoPtr, MIN(InfoSize, Size), &Offset, + &BytesCopied); + if (Size < InfoSize) + return TDI_BUFFER_OVERFLOW; + else { + *BufferSize = InfoSize; + return TDI_SUCCESS; + } +} + +//* TdiSetInformation - Set Information handler. +// +// The TDI SetInformation routine. Currently we don't allow anything to be +// set. +// +// Input: Request - The request structure for this command. +// SetType - The type of set to be performed. +// Buffer - Buffer to set from. +// BufferSize - Size in bytes of buffer. +// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE +// if we are setting the address info on +// a connection. +// +// Returns: Status of attempt to set information. +// +TDI_STATUS +TdiSetInformation(PTDI_REQUEST Request, uint SetType, PNDIS_BUFFER Buffer, + uint BufferSize, uint IsConn) +{ + return TDI_INVALID_REQUEST; +} + +//* TdiAction - Action handler. +// +// The TDI Action routine. Currently we don't support any actions. +// +// Input: Request - The request structure for this command. +// ActionType - The type of action to be performed. +// Buffer - Buffer of action info. +// BufferSize - Size in bytes of buffer. +// +// Returns: Status of attempt to perform action. +// +TDI_STATUS +TdiAction(PTDI_REQUEST Request, uint ActionType, PNDIS_BUFFER Buffer, + uint BufferSize) +{ + return TDI_INVALID_REQUEST; +} + +//* TdiQueryInfoEx - Extended TDI query information. +// +// This is the new TDI query information handler. We take in a TDIObjectID +// structure, a buffer and length, and some context information, and return +// the requested information if possible. +// +// Input: Request - The request structure for this command. +// ID - The object ID +// Buffer - Pointer to buffer to be filled in. +// Size - Pointer to size in bytes of Buffer. On exit, +// filled in with bytes written. +// Context - Pointer to context buffer. +// +// Returns: Status of attempt to get information. +// +TDI_STATUS +TdiQueryInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, + PNDIS_BUFFER Buffer, uint *Size, void *Context) +{ + uint BufferSize = *Size; + uint InfoSize; + void *InfoPtr; + uint Fixed; + CTELockHandle Handle; +#ifndef VXD + CTELock *LockPtr; +#else +#ifdef DEBUG + CTELock *LockPtr; +#endif +#endif + uint Offset = 0; + uchar InfoBuffer[sizeof(TCPConnTableEntry)]; + uint BytesRead; + uint Valid; + uint Entity; + uint BytesCopied; + + // First check to see if he's querying for list of entities. + Entity = ID->toi_entity.tei_entity; + if (Entity == GENERIC_ENTITY) { + *Size = 0; + + if (ID->toi_class != INFO_CLASS_GENERIC || + ID->toi_type != INFO_TYPE_PROVIDER || + ID->toi_id != ENTITY_LIST_ID) { + return TDI_INVALID_PARAMETER; + } + + // Make sure we have room for it the list in the buffer. + InfoSize = EntityCount * sizeof(TDIEntityID); + + if (BufferSize < InfoSize) { + // Not enough room. + return TDI_BUFFER_TOO_SMALL; + } + + *Size = InfoSize; + + // Copy it in, free our temp. buffer, and return success. + (void)CopyFlatToNdis(Buffer, (uchar *)EntityList, InfoSize, &Offset, + &BytesCopied); + return TDI_SUCCESS; + } + + + //* Check the level. If it can't be for us, pass it down. +#ifndef UDP_ONLY + if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) { +#else + if (Entity != CL_TL_ENTITY) { +#endif + + // When we support multiple lower entities at this layer we'll have + // to figure out which one to dispatch to. For now, just pass it + // straight down. + return (*LocalNetInfo.ipi_qinfo)(ID, Buffer, Size, Context); + } + + if (ID->toi_entity.tei_instance != TL_INSTANCE) { + // We only support a single instance. + return TDI_INVALID_REQUEST; + } + + // Zero returned parameters in case of an error below. + *Size = 0; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + // This is a generic request. + if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) { + if (BufferSize >= sizeof(uint)) { + *(uint *)&InfoBuffer[0] = (Entity == CO_TL_ENTITY) ? CO_TL_TCP + : CL_TL_UDP; + (void)CopyFlatToNdis(Buffer, InfoBuffer, sizeof(uint), &Offset, + &BytesCopied); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + // Handle protocol specific class of information. For us, this is + // the MIB-2 stuff or the minimal stuff we do for oob_inline support. + +#ifndef UDP_ONLY + if (ID->toi_type == INFO_TYPE_CONNECTION) { + TCPConn *Conn; + TCB *QueryTCB; + TCPSocketAMInfo *AMInfo; + CTELockHandle TCBHandle; + + if (BufferSize < sizeof(TCPSocketAMInfo) || + ID->toi_id != TCP_SOCKET_ATMARK) + return TDI_INVALID_PARAMETER; + + AMInfo = (TCPSocketAMInfo *)InfoBuffer; + CTEGetLock(&ConnTableLock, &Handle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + QueryTCB = Conn->tc_tcb; + if (QueryTCB != NULL) { + CTEStructAssert(QueryTCB, tcb); + CTEGetLock(&QueryTCB->tcb_lock, &TCBHandle); + if ((QueryTCB->tcb_flags & (URG_INLINE | URG_VALID)) == + (URG_INLINE | URG_VALID)) { + // We're in inline mode, and the urgent data fields are + // valid. + AMInfo->tsa_size = QueryTCB->tcb_urgend - + QueryTCB->tcb_urgstart + 1; + // Rcvnext - pendingcnt is the sequence number of the + // next byte of data that will be delivered to the + // client. Urgend - that value is the offset in the + // data stream of the end of urgent data. + AMInfo->tsa_offset = QueryTCB->tcb_urgend - + (QueryTCB->tcb_rcvnext - QueryTCB->tcb_pendingcnt); + } else { + AMInfo->tsa_size = 0; + AMInfo->tsa_offset = 0; + } + CTEFreeLock(&QueryTCB->tcb_lock, TCBHandle); + *Size = sizeof(TCPSocketAMInfo); + CopyFlatToNdis(Buffer, InfoBuffer, sizeof(TCPSocketAMInfo), + &Offset, &BytesCopied); + return TDI_SUCCESS; + } + } + return TDI_INVALID_PARAMETER; + + } + +#endif + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + switch (ID->toi_id) { + + case UDP_MIB_STAT_ID: +#if UDP_MIB_STAT_ID != TCP_MIB_STAT_ID + case TCP_MIB_STAT_ID: +#endif + Fixed = TRUE; + if (Entity == CL_TL_ENTITY) { + InfoSize = sizeof(UDPStats); + InfoPtr = &UStats; + } else { +#ifndef UDP_ONLY + InfoSize = sizeof(TCPStats); + InfoPtr = &TStats; +#else + return TDI_INVALID_PARAMETER; +#endif + } + break; + case UDP_MIB_TABLE_ID: +#if UDP_MIB_TABLE_ID != TCP_MIB_TABLE_ID + case TCP_MIB_TABLE_ID: +#endif + Fixed = FALSE; + if (Entity == CL_TL_ENTITY) { + InfoSize = sizeof(UDPEntry); + InfoPtr = &ReadAOTable; + CTEGetLock(&AddrObjTableLock, &Handle); +#ifndef VXD + LockPtr = &AddrObjTableLock; +#else +#ifdef DEBUG + LockPtr = &AddrObjTableLock; +#endif +#endif + } else { +#ifndef UDP_ONLY + InfoSize = sizeof(TCPConnTableEntry); + InfoPtr = &ReadTCBTable; + CTEGetLock(&TCBTableLock, &Handle); +#ifndef VXD + LockPtr = &TCBTableLock; +#else +#ifdef DEBUG + LockPtr = &TCBTableLock; +#endif +#endif + +#else + return TDI_INVALID_PARAMETER; +#endif + } + break; + default: + return TDI_INVALID_PARAMETER; + break; + } + + if (Fixed) { + if (BufferSize < InfoSize) + return TDI_BUFFER_TOO_SMALL; + + *Size = InfoSize; + + (void)CopyFlatToNdis(Buffer, InfoPtr, InfoSize, &Offset, + &BytesCopied); + return TDI_SUCCESS; + } else { + struct ReadTableStruct *RTSPtr; + uint ReadStatus; + + // Have a variable length (or mult-instance) structure to copy. + // InfoPtr points to the structure describing the routines to + // call to read the table. + // Loop through up to CountWanted times, calling the routine + // each time. + BytesRead = 0; + + RTSPtr = InfoPtr; + + ReadStatus = (*(RTSPtr->rts_validate))(Context, &Valid); + + // If we successfully read something we'll continue. Otherwise + // we'll bail out. + if (!Valid) { + CTEFreeLock(LockPtr, Handle); + return TDI_INVALID_PARAMETER; + } + + while (ReadStatus) { + // The invariant here is that there is data in the table to + // read. We may or may not have room for it. So ReadStatus + // is TRUE, and BufferSize - BytesRead is the room left + // in the buffer. + if ((int)(BufferSize - BytesRead) >= (int)InfoSize) { + ReadStatus = (*(RTSPtr->rts_readnext))(Context, + InfoBuffer); + BytesRead += InfoSize; + Buffer = CopyFlatToNdis(Buffer, InfoBuffer, InfoSize, + &Offset, &BytesCopied); + } else + break; + + } + + *Size = BytesRead; + CTEFreeLock(LockPtr, Handle); + return (!ReadStatus ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + } + + } + + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + // We want to return implementation specific info. For now, error out. + return TDI_INVALID_PARAMETER; + } + + return TDI_INVALID_PARAMETER; + +} + +//* TdiSetInfoEx - Extended TDI set information. +// +// This is the new TDI set information handler. We take in a TDIObjectID +// structure, a buffer and length. We set the object specifed by the ID +// (and possibly by the Request) to the value specified in the buffer. +// +// Input: Request - The request structure for this command. +// ID - The object ID +// Buffer - Pointer to buffer containing value to set. +// Size - Size in bytes of Buffer. +// +// Returns: Status of attempt to get information. +// +TDI_STATUS +TdiSetInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, void *Buffer, + uint Size) +{ + TCPConnTableEntry *TCPEntry; + CTELockHandle TableHandle, TCBHandle; + TCB *SetTCB; + uint Entity; + TCPConn *Conn; + TDI_STATUS Status; + + + //* Check the level. If it can't be for us, pass it down. + Entity = ID->toi_entity.tei_entity; + + if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) { + // Someday we'll have to figure out how to dispatch. For now, just pass + // it down. + return (*LocalNetInfo.ipi_setinfo)(ID, Buffer, Size); + } + + if (ID->toi_entity.tei_instance != TL_INSTANCE) + return TDI_INVALID_REQUEST; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + // Fill this in when we have generic class defines. + return TDI_INVALID_PARAMETER; + } + + //* Now look at the rest of it. + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + // Handle protocol specific class of information. For us, this is + // the MIB-2 stuff, as well as common sockets options, + // and in particular the setting of the state of a TCP connection. + + if (ID->toi_type == INFO_TYPE_CONNECTION) { + TCPSocketOption *Option; + uint Flag; + uint Value; + +#ifndef UDP_ONLY + // A connection type. Get the connection, and then figure out + // what to do with it. + Status = TDI_INVALID_PARAMETER; + + if (Size < sizeof(TCPSocketOption)) + return Status; + + CTEGetLock(&ConnTableLock, &TableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + Status = TDI_SUCCESS; + + if (ID->toi_id == TCP_SOCKET_WINDOW) { + // This is a funny option, because it doesn't involve + // flags. Handle this specially. + Option = (TCPSocketOption *)Buffer; + + // We don't allow anyone to shrink the window, as this + // gets too weird from a protocol point of view. Also, + // make sure they don't try and set anything too big. + if (Option->tso_value > 0xffff) + Status = TDI_INVALID_PARAMETER; + else if (Option->tso_value > Conn->tc_window || + Conn->tc_tcb == NULL) { + Conn->tc_flags |= CONN_WINSET; + Conn->tc_window = Option->tso_value; + SetTCB = Conn->tc_tcb; + + if (SetTCB != NULL) { + CTEStructAssert(SetTCB, tcb); + CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); + CTEAssert(Option->tso_value > SetTCB->tcb_defaultwin); + if (DATA_RCV_STATE(SetTCB->tcb_state) && + !CLOSING(SetTCB)) { + SetTCB->tcb_flags |= WINDOW_SET; + SetTCB->tcb_defaultwin = Option->tso_value; + SetTCB->tcb_refcnt++; + CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); + SendACK(SetTCB); + CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); + DerefTCB(SetTCB, TCBHandle); + } else { + CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); + } + } + } + CTEFreeLock(&ConnTableLock, TableHandle); + return Status; + } + + Flag = 0; + Option = (TCPSocketOption *)Buffer; + Value = Option->tso_value; + // We have the connection, so figure out which flag to set. + switch (ID->toi_id) { + + case TCP_SOCKET_NODELAY: + Value = !Value; + Flag = NAGLING; + break; + case TCP_SOCKET_KEEPALIVE: + Flag = KEEPALIVE; + break; + case TCP_SOCKET_BSDURGENT: + Flag = BSD_URGENT; + break; + case TCP_SOCKET_OOBINLINE: + Flag = URG_INLINE; + break; + default: + Status = TDI_INVALID_PARAMETER; + break; + } + + if (Status == TDI_SUCCESS) { + if (Value) + Conn->tc_tcbflags |= Flag; + else + Conn->tc_tcbflags &= ~Flag; + + SetTCB = Conn->tc_tcb; + if (SetTCB != NULL) { + CTEStructAssert(SetTCB, tcb); + CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); + if (Value) + SetTCB->tcb_flags |= Flag; + else + SetTCB->tcb_flags &= ~Flag; + + if (ID->toi_id == TCP_SOCKET_KEEPALIVE) { + SetTCB->tcb_alive = TCPTime; + SetTCB->tcb_kacount = 0; + } + + CTEFreeLock(&SetTCB->tcb_lock, TCBHandle); + } + } + } + + CTEFreeLock(&ConnTableLock, TableHandle); + return Status; +#else + return TDI_INVALID_PARAMETER; +#endif + } + + if (ID->toi_type == INFO_TYPE_ADDRESS_OBJECT) { + // We're setting information on an address object. This is + // pretty simple. + + return SetAddrOptions(Request, ID->toi_id, Size, Buffer); + + } + + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + +#ifndef UDP_ONLY + if (ID->toi_id == TCP_MIB_TABLE_ID) { + if (Size != sizeof(TCPConnTableEntry)) + return TDI_INVALID_PARAMETER; + + TCPEntry = (TCPConnTableEntry *)Buffer; + + if (TCPEntry->tct_state != TCP_DELETE_TCB) + return TDI_INVALID_PARAMETER; + + // We have an apparently valid request. Look up the TCB. + CTEGetLock(&TCBTableLock, &TableHandle); + SetTCB = FindTCB(TCPEntry->tct_localaddr, + TCPEntry->tct_remoteaddr, (ushort)TCPEntry->tct_remoteport, + (ushort)TCPEntry->tct_localport); + + // We found him. If he's not closing or closed, close him. + if (SetTCB != NULL) { + CTEGetLock(&SetTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&TCBTableLock, TCBHandle); + + // We've got him. Bump his ref. count, and call TryToCloseTCB + // to mark him as closing. Then notify the upper layer client + // of the disconnect. + SetTCB->tcb_refcnt++; + if (SetTCB->tcb_state != TCB_CLOSED && !CLOSING(SetTCB)) { + SetTCB->tcb_flags |= NEED_RST; + TryToCloseTCB(SetTCB, TCB_CLOSE_ABORTED, TableHandle); + CTEGetLock(&SetTCB->tcb_lock, &TableHandle); + + if (SetTCB->tcb_state != TCB_TIME_WAIT) { + // Remove him from the TCB, and notify the client. + CTEFreeLock(&SetTCB->tcb_lock, TableHandle); + RemoveTCBFromConn(SetTCB); + NotifyOfDisc(SetTCB, NULL, TDI_CONNECTION_RESET); + CTEGetLock(&SetTCB->tcb_lock, &TableHandle); + } + + } + + DerefTCB(SetTCB, TableHandle); + return TDI_SUCCESS; + } else { + CTEFreeLock(&TCBTableLock, TableHandle); + return TDI_INVALID_PARAMETER; + } + } else + return TDI_INVALID_PARAMETER; +#else + return TDI_INVALID_PARAMETER; +#endif + + } + + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + // We want to return implementation specific info. For now, error out. + return TDI_INVALID_REQUEST; + } + + return TDI_INVALID_REQUEST; + + +} diff --git a/private/ntos/tdi/tcpip/tcp/info.h b/private/ntos/tdi/tcpip/tcp/info.h new file mode 100644 index 000000000..f5274160f --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/info.h @@ -0,0 +1,51 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INFO.H - TDI Query/SetInfo and Action definitons. +// +// This file contains definitions for the file info.c. +// + +#include "tcpinfo.h" + +#define TL_INSTANCE 0 + +#ifndef UDP_ONLY +extern TCPStats TStats; + +typedef struct TCPConnContext { + uint tcc_index; + struct TCB *tcc_tcb; +} TCPConnContext; + +#define TCB_STATE_DELTA 1 + +#endif + +typedef struct UDPContext { + uint uc_index; + struct AddrObj *uc_ao; +} UDPContext; + +extern UDPStats UStats; +extern struct TDIEntityID *EntityList; +extern uint EntityCount; + +extern TDI_STATUS TdiQueryInformation(PTDI_REQUEST Request, uint QueryType, + PNDIS_BUFFER Buffer, uint *BufferSize, uint IsConn); + +extern TDI_STATUS TdiSetInformation(PTDI_REQUEST Request, uint SetType, + PNDIS_BUFFER Buffer, uint BufferSize, uint IsConn); + +extern TDI_STATUS TdiAction(PTDI_REQUEST Request, uint ActionType, + PNDIS_BUFFER Buffer, uint BufferSize); + +extern TDI_STATUS TdiQueryInformationEx(PTDI_REQUEST Request, + struct TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context); + +extern TDI_STATUS TdiSetInformationEx(PTDI_REQUEST Request, + struct TDIObjectID *ID, void *Buffer, uint Size); + diff --git a/private/ntos/tdi/tcpip/tcp/init.c b/private/ntos/tdi/tcpip/tcp/init.c new file mode 100644 index 000000000..2a889d88a --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/init.c @@ -0,0 +1,597 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INIT.C - TCP/UDP init code. +// +// This file contain init code for the TCP/UDP driver. Some things +// here are ifdef'ed for building a UDP only version. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef NT +#include +#endif +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "udp.h" +#include "raw.h" +#include "info.h" +#ifndef UDP_ONLY +#include "tcp.h" +#include "tcpsend.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpdeliv.h" +#include "tlcommon.h" + +extern int InitTCPRcv(void); +extern void UnInitTCPRcv(void); +#endif // UDP_ONLY + +#include "tdiinfo.h" +#include "tcpcfg.h" + + +//* Definitions of global variables. +IPInfo LocalNetInfo; + +uint DeadGWDetect; +uint PMTUDiscovery; +uint PMTUBHDetect; +uint KeepAliveTime; +uint KAInterval; +uint DefaultRcvWin; +uint MaxConnections; +uint MaxConnectRexmitCount; +uint MaxConnectResponseRexmitCount; +#ifdef SYN_ATTACK +uint MaxConnectResponseRexmitCountTmp; +#endif +uint MaxDataRexmitCount; +uint BSDUrgent; +uint FinWait2TO; +uint NTWMaxConnectCount; +uint NTWMaxConnectTime; +uint MaxUserPort; + +#ifdef SECFLTR +uint SecurityFilteringEnabled; +#endif // SECFLTR + +#ifdef VXD +uint PreloadCount; +#endif + +#ifdef _PNP_POWER +HANDLE AddressChangeHandle; +#endif + +#ifdef VXD +TDIDispatchTable TLDispatch; + +#ifndef CHICAGO +char TransportName[] = "MSTCP"; +#else +char TransportName[] = TCP_NAME; +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +#endif + +#endif + +uint StartTime; + +extern void *UDPProtInfo; +extern void *RawProtInfo; + +extern int InitTCPConn(void); +extern void UnInitTCPConn(void); +extern IP_STATUS TLGetIPInfo(IPInfo *Buffer, int Size); + + + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int tlinit(); + +#pragma alloc_text(INIT, tlinit) + +#endif // ALLOC_PRAGMA +#endif + +//* Dummy routines for UDP only version. All of these routines return +// 'Invalid Request'. + +#ifdef UDP_ONLY +TDI_STATUS +TdiOpenConnection(PTDI_REQUEST Request, PVOID Context) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS +TdiCloseConnection(PTDI_REQUEST Request) +{ + return TDI_INVALID_REQUEST; +} + +TDI_STATUS +TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS TdiDisAssociateAddress(PTDI_REQUEST Request) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS TdiConnect(PTDI_REQUEST Request, void *Timeout, + PTDI_CONNECTION_INFORMATION RequestAddr, + PTDI_CONNECTION_INFORMATION ReturnAddr) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS TdiListen(PTDI_REQUEST Request, ushort Flags, + PTDI_CONNECTION_INFORMATION AcceptableAddr, + PTDI_CONNECTION_INFORMATION ConnectedAddr) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS TdiAccept(PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION AcceptInfo, + PTDI_CONNECTION_INFORMATION ConnectedInfo) +{ + return TDI_INVALID_REQUEST; +} +TDI_STATUS TdiReceive(PTDI_REQUEST Request, ushort *Flags, + uint *RcvLength, PNDIS_BUFFER Buffer) +{ + return TDI_INVALID_REQUEST; +} + +TDI_STATUS TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength, + PNDIS_BUFFER Buffer) +{ + return TDI_INVALID_REQUEST; +} + +TDI_STATUS TdiDisconnect(PTDI_REQUEST Request, PVOID Timeout, ushort Flags, + PTDI_CONNECTION_INFORMATION DisconnectInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo) +{ + return TDI_INVALID_REQUEST; +} + +#endif + +#ifdef VXD + +extern void *ConvertPtr(void *Ptr); + +//** Routines to handle incoming QueryInformation requests, and dispatch +//** them. + +struct ClientReq { + ushort cr_opcode; + void *cr_ID; + void *cr_buffer; + void *cr_length; + void *cr_context; +}; + +//* VxDQueryInfo - VxD thunk to query information. +// +// The VxD thunk to TdiQueryInformationEx. All we do it convert the pointers +// and call in. +// +// Input: Req - A pointer to a ClientReq structure. +// +// Returns: Status of command. +// +TDI_STATUS +VxDQueryInfo(struct ClientReq *Req) +{ + NDIS_BUFFER Buffer; + TDI_REQUEST Request; + uint *Size; + TDIObjectID *ID; + void *Context; + + CTEAssert(Req->cr_opcode == 0); + + CTEMemSet(&Request, 0, sizeof(TDI_REQUEST)); + + ID = (TDIObjectID *)ConvertPtr(Req->cr_ID); + Size = (uint *)ConvertPtr(Req->cr_length); + +#ifdef DEBUG + Buffer.Signature = BUFFER_SIGN; +#endif + + Buffer.VirtualAddress = ConvertPtr(Req->cr_buffer); + Buffer.Length = *Size; + Buffer.Next = NULL; + + return TdiQueryInformationEx(&Request, ID, &Buffer, Size, + ConvertPtr(Req->cr_context)); + +} + +//* VxDSetInfo - VxD thunk to set information. +// +// The VxD thunk to TdiSetInformationEx. All we do it convert the pointers +// and call in. +// +// Input: Req - A pointer to a ClientReq structure. +// +// Returns: Status of command. +// +TDI_STATUS +VxDSetInfo(struct ClientReq *Req) +{ + TDIObjectID *ID; + uint *Size; + void *Buffer; + TDI_REQUEST Request; + + CTEAssert(Req->cr_opcode == 1); + + CTEMemSet(&Request, 0, sizeof(TDI_REQUEST)); + + ID = (TDIObjectID *)ConvertPtr(Req->cr_ID); + Size = (uint *)ConvertPtr(Req->cr_length); + Buffer = ConvertPtr(Req->cr_buffer); + + return TdiSetInformationEx(&Request, ID, Buffer, *Size); + +} +#endif + +#ifdef CHICAGO +//* AddrChange - Receive notification of an IP address change. +// +// Called by IP when an address comes or goes. We get the address +// and mask, and depending on what's actually happened we may close address +// and connections. +// +// Input: Addr - IP address that's coming or going. +// Mask - Mask for Addr. +// Context - PNP context (unused) +// IPContext - IP context (unused) +// Added - True if the address is coming, False if it's going. +// +// Returns: Nothing. +// +void +AddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + uint Added) +{ + if (Added) { + // He's adding an address. Re-query the entity list now. + EntityList[0].tei_entity = CO_TL_ENTITY; + EntityList[0].tei_instance = 0; + EntityList[1].tei_entity = CL_TL_ENTITY; + EntityList[1].tei_instance = 0; + EntityCount = 2; + + // When we have multiple networks under us, we'll want to loop through + // here calling them all. For now just call the one we have. + (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount); + } else { + // He's deleting an address. + if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) { +#ifndef UDP_ONLY + TCBWalk(DeleteTCBWithSrc, &Addr, NULL, NULL); +#endif + InvalidateAddrs(Addr); + } + + } +} +#endif + +#ifdef NT +#ifdef _PNP_POWER + +//* AddressArrival - Handle an IP address arriving +// +// Called by TDI when an address arrives. All we do is query the +// EntityList. +// +// Input: Addr - IP address that's coming. +// +// Returns: Nothing. +// +void +AddressArrival(PTA_ADDRESS Addr) +{ + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP) { + // He's adding an address. Re-query the entity list now. + EntityList[0].tei_entity = CO_TL_ENTITY; + EntityList[0].tei_instance = 0; + EntityList[1].tei_entity = CL_TL_ENTITY; + EntityList[1].tei_instance = 0; + EntityCount = 2; + + // When we have multiple networks under us, we'll want to loop through + // here calling them all. For now just call the one we have. + (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount); + } +} + +//* AddressDeletion - Handle an IP address going away. +// +// Called by TDI when an address is deleted. If it's an address we +// care about we'll clean up appropriately. +// +// Input: Addr - IP address that's going. +// +// Returns: Nothing. +// +void +AddressDeletion(PTA_ADDRESS Addr) +{ + PTDI_ADDRESS_IP MyAddress; + IPAddr LocalAddress; + + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP) { + // He's deleting an address. + + MyAddress = (PTDI_ADDRESS_IP)Addr->Address; + LocalAddress = MyAddress->in_addr; + + if (!IP_ADDR_EQUAL(LocalAddress, NULL_IP_ADDR)) { +#ifndef UDP_ONLY + TCBWalk(DeleteTCBWithSrc, &LocalAddress, NULL, NULL); +#endif + InvalidateAddrs(LocalAddress); + } + } +} + +#endif // _PNP_POWER +#endif // NT + +#pragma BEGIN_INIT + +extern uchar TCPGetConfigInfo(void); + +extern uchar IPPresent(void); + +//** tlinit - Initialize the transport layer. +// +// The main transport layer initialize routine. We get whatever config +// info we need, initialize some data structures, get information +// from IP, do some more initialization, and finally register our +// protocol values with IP. +// +// Input: Nothing +// +// Returns: True is we succeeded, False if we fail to initialize. +// +int +tlinit() +{ +#ifdef VXD + void *PreloadPtrs[MAX_PRELOAD_COUNT]; + uint i; +#endif + + uint TCBInitialized = 0; + + if (!CTEInitialize()) + return FALSE; + +#ifdef VXD + if (!IPPresent()) + return FALSE; +#endif + + if (!TCPGetConfigInfo()) + return FALSE; + + StartTime = CTESystemUpTime(); + +#ifndef UDP_ONLY + KeepAliveTime = MS_TO_TICKS(KeepAliveTime); + KAInterval = MS_TO_TICKS(KAInterval); + +#endif + + CTERefillMem(); + + // Get net information from IP. + if (TLGetIPInfo(&LocalNetInfo, sizeof(IPInfo)) != IP_SUCCESS) + goto failure; + + if (LocalNetInfo.ipi_version != IP_DRIVER_VERSION) + goto failure; // Wrong version of IP. + +#ifdef CHICAGO + if (!RegisterAddrChangeHndlr(AddrChange, TRUE)) + goto failure; +#endif + +#ifdef NT +#ifdef _PNP_POWER + + (void)TdiRegisterAddressChangeHandler( + AddressArrival, + AddressDeletion, + &AddressChangeHandle + ); + +#endif // _PNP_POWER +#endif // NT + + //* Initialize addr obj management code. + if (!InitAddr()) + goto failure; + + CTERefillMem(); + if (!InitDG(sizeof(UDPHeader))) + goto failure; + +#ifndef UDP_ONLY + MaxConnections = MIN(MaxConnections, INVALID_CONN_INDEX - 1); + CTERefillMem(); + if (!InitTCPConn()) + goto failure; + + CTERefillMem(); + if (!InitTCB()) + goto failure; + + TCBInitialized = 1; + + CTERefillMem(); + if (!InitTCPRcv()) + goto failure; + + CTERefillMem(); + if (!InitTCPSend()) + goto failure; + + CTEMemSet(&TStats, 0, sizeof(TCPStats)); + + TStats.ts_rtoalgorithm = TCP_RTO_VANJ; + TStats.ts_rtomin = MIN_RETRAN_TICKS * MS_PER_TICK; + TStats.ts_rtomax = MAX_REXMIT_TO * MS_PER_TICK; + TStats.ts_maxconn = (ulong) TCP_MAXCONN_DYNAMIC; + +#endif + + CTEMemSet(&UStats,0, sizeof(UDPStats)); + + + // Register our UDP protocol handler. + UDPProtInfo = TLRegisterProtocol(PROTOCOL_UDP, UDPRcv, DGSendComplete, + UDPStatus, NULL); + + if (UDPProtInfo == NULL) + goto failure; // Failed to register! + + // Register the Raw IP (wildcard) protocol handler. + RawProtInfo = TLRegisterProtocol(PROTOCOL_ANY, RawRcv, DGSendComplete, + RawStatus, NULL); + + if (RawProtInfo == NULL) { + CTEPrint(("failed to register raw prot with IP\n")); + goto failure; // Failed to register! + } + +#ifdef VXD + TLDispatch.TdiOpenAddressEntry = TdiOpenAddress; + TLDispatch.TdiCloseAddressEntry = TdiCloseAddress; + TLDispatch.TdiSendDatagramEntry = TdiSendDatagram; + TLDispatch.TdiReceiveDatagramEntry = TdiReceiveDatagram; + TLDispatch.TdiSetEventEntry = TdiSetEvent; + + TLDispatch.TdiOpenConnectionEntry = TdiOpenConnection; + TLDispatch.TdiCloseConnectionEntry = TdiCloseConnection; + TLDispatch.TdiAssociateAddressEntry = TdiAssociateAddress; + TLDispatch.TdiDisAssociateAddressEntry = TdiDisAssociateAddress; + TLDispatch.TdiConnectEntry = TdiConnect; + TLDispatch.TdiDisconnectEntry = TdiDisconnect; + TLDispatch.TdiListenEntry = TdiListen; + TLDispatch.TdiAcceptEntry = TdiAccept; + TLDispatch.TdiReceiveEntry = TdiReceive; + TLDispatch.TdiSendEntry = TdiSend; + TLDispatch.TdiQueryInformationEntry = TdiQueryInformation; + TLDispatch.TdiSetInformationEntry = TdiSetInformation; + TLDispatch.TdiActionEntry = TdiAction; + TLDispatch.TdiQueryInformationExEntry = TdiQueryInformationEx; + TLDispatch.TdiSetInformationExEntry = TdiSetInformationEx; + + if (!TLRegisterDispatch(TransportName, &TLDispatch)) + goto failure; + +#endif + + CTERefillMem(); + + // Now query the lower layer entities, and save the information. + EntityList = CTEAllocMem(sizeof(TDIEntityID) * MAX_TDI_ENTITIES); + if (EntityList == NULL) + goto failure; + + EntityList[0].tei_entity = CO_TL_ENTITY; + EntityList[0].tei_instance = 0; + EntityList[1].tei_entity = CL_TL_ENTITY; + EntityList[1].tei_instance = 0; + EntityCount = 2; + + // When we have multiple networks under us, we'll want to loop through + // here calling them all. For now just call the one we have. + (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount); + + CTERefillMem(); + +#ifdef VXD + // Allocate memory as needed to satisfy the heap preload requirements. We'll + // allocate a bunch of memory from the IFSMgr and then free it, so + // hopefully it'll be there later when we need it. + PreloadCount = MIN(PreloadCount, MAX_PRELOAD_COUNT); + for (i = 0; i < PreloadCount; i++) { + void *Temp; + + Temp = CTEAllocMem(PRELOAD_BLOCK_SIZE); + if (Temp != NULL) + PreloadPtrs[i] = Temp; + else + break; + CTERefillMem(); + } + + PreloadCount = i; + for (i = 0; i < PreloadCount; i++) { + CTEFreeMem(PreloadPtrs[i]); + } + +#endif + + return TRUE; + + // Come here to handle all failure cases. +failure: + + // If we've registered Raw IP, unregister it now. + if (RawProtInfo != NULL) + TLRegisterProtocol(PROTOCOL_ANY, NULL, NULL, NULL, NULL); + + // If we've registered UDP, unregister it now. + if (UDPProtInfo != NULL) + TLRegisterProtocol(PROTOCOL_UDP, NULL, NULL, NULL, NULL); +#ifndef UDP_ONLY + UnInitTCPSend(); + UnInitTCPRcv(); + if (TCBInitialized) { + UnInitTCB(); + } + UnInitTCPConn(); +#endif + + CTERefillMem(); + return FALSE; +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/tcp/mips/sources b/private/ntos/tdi/tcpip/tcp/mips/sources new file mode 100644 index 000000000..e4f0fd9fa --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/mips/sources @@ -0,0 +1,4 @@ +MIPS_SOURCES= \ + ..\mips\xsum.s + + diff --git a/private/ntos/tdi/tcpip/tcp/mips/xsum.s b/private/ntos/tdi/tcpip/tcp/mips/xsum.s new file mode 100644 index 000000000..1463a897b --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/mips/xsum.s @@ -0,0 +1,243 @@ +// TITLE("Compute Checksum") +//++ +// +// Copyright (c) 1992-1994 Microsoft Corporation +// +// Module Name: +// +// tcpxsum.s +// +// Abstract: +// +// This module implement a function to compute the checksum of a buffer. +// +// Author: +// +// David N. Cutler (davec) 27-Jan-1992 +// +// Environment: +// +// User mode. +// +// Revision History: +// +//-- + +#include "ksmips.h" + + SBTTL("Compute Checksum") +//++ +// +// ULONG +// tcpxsum ( +// IN ULONG Checksum, +// IN PUCHAR Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function computes the checksum of the specified buffer. +// +// Arguments: +// +// Checksum (a0) - Supplies the initial checksum value. +// +// Source (a1) - Supplies a pointer to the checksum buffer. +// +// Length (a2) - Supplies the length of the buffer in bytes. +// +// Return Value: +// +// The computed checksum is returned as the function value. +// +//-- + + LEAF_ENTRY(tcpxsum) + +// +// Clear the computed checksum and check if the buffer is word aligned. +// + + move a3,zero // clear computed checksum + and v1,a1,1 // check if buffer word aligned + beq zero,a2,90f // if eq, no bytes to checksum + and t1,a2,1 // check if length is even + beq zero,v1,10f // if eq, buffer word aligned + +// +// Initialize the checksum to the first byte shifted up by a byte. +// + + lbu t2,0(a1) // get first byte of buffer + addu a1,a1,1 // advance buffer address + subu a2,a2,1 // reduce count of bytes to checksum + dsll a3,t2,8 // shift byte up in computed checksum + beq zero,a2,90f // if eq, no more bytes in buffer + and t1,a2,1 // check if length is even + +// +// Check if the length of the buffer if an even number of bytes. +// +// If the buffer is not an even number of bytes, then add the last byte +// to the computed checksum. +// + +10: and t3,a1,2 // check if buffer long aligned + beq zero,t1,20f // if eq, even number of bytes + addu t0,a1,a2 // compute address of ending byte + 1 + lbu t2,-1(t0) // get last byte of buffer + subu a2,a2,1 // reduce count of bytes to checksum + daddu a3,a3,t2 // add last byte to computed checksum + beq zero,a2,90f // if eq, no more bytes in buffer + +// +// Check if the buffer is long aligned. +// +// If the buffer is not long aligned, then long align the buffer. +// + +20: and t0,a2,8 - 1 // compute residual bytes + beq zero,t3,30f // if eq, buffer long aligned + lhu t2,0(a1) // get next word of buffer + addu a1,a1,2 // advance buffer address + subu a2,a2,2 // reduce count of bytes to checksum + daddu a3,a3,t2 // add next word to computed checksum + beq zero,a2,90f // if eq, no more bytes in buffer + and t0,a2,8 - 1 // compute residual bytes + +// +// Compute checksum. +// + + .set noreorder + .set at +30: subu t9,a2,t0 // subtract out residual bytes + beq zero,t9,70f // if eq, no large blocks + addu t8,a1,t9 // compute ending block address + move a2,t0 // set residual number of bytes + and v0,t9,1 << 3 // check for initial 8-byte block + beq zero,v0,40f // if eq, no 8-byte block + and v0,t9,1 << 4 // check for initial 16-byte block + lwu t0,0(a1) // load 8-byte block + lwu t1,4(a1) // + addu a1,a1,8 // advance source address + daddu a3,a3,t0 // compute 8-byte checksum + beq t8,a1,70f // if eq, end of block + daddu a3,a3,t1 // +40: beq zero,v0,50f // if eq, no 16-byte block + and v0,t9,1 << 5 // check for initial 32-byte block + lwu t0,0(a1) // load 16-byte data block + lwu t1,4(a1) // + lwu t2,8(a1) // + lwu t3,12(a1) // + addu a1,a1,16 // advance source address + daddu a3,a3,t0 // compute 16-byte block checksum + daddu a3,a3,t1 // + daddu a3,a3,t2 // + beq t8,a1,70f // if eq, end of block + daddu a3,a3,t3 // +50: beq zero,v0,60f // if eq, no 32-byte block + lwu t0,0(a1) // load 32-byte data block + lwu t1,4(a1) // + lwu t2,8(a1) // + lwu t3,12(a1) // + lwu t4,16(a1) // + lwu t5,20(a1) // + lwu t6,24(a1) // + lwu t7,28(a1) // + addu a1,a1,32 // advance source address + daddu a3,a3,t0 // compute 32-byte block checksum + daddu a3,a3,t1 // + daddu a3,a3,t2 // + daddu a3,a3,t3 // + daddu a3,a3,t4 // + daddu a3,a3,t5 // + daddu a3,a3,t6 // + beq t8,a1,70f // if eq, end of block + daddu a3,a3,t7 // +55: lwu t0,0(a1) // load 32-byte data block +60: lwu t1,4(a1) // + lwu t2,8(a1) // + lwu t3,12(a1) // + lwu t4,16(a1) // + lwu t5,20(a1) // + lwu t6,24(a1) // + lwu t7,28(a1) // + daddu a3,a3,t0 // compute 32-byte block checksum + daddu a3,a3,t1 // + daddu a3,a3,t2 // + daddu a3,a3,t3 // + daddu a3,a3,t4 // + daddu a3,a3,t5 // + daddu a3,a3,t6 // + daddu a3,a3,t7 // + lwu t0,32(a1) // load 32-byte data block + lwu t1,36(a1) // + lwu t2,40(a1) // + lwu t3,44(a1) // + lwu t4,48(a1) // + lwu t5,52(a1) // + lwu t6,56(a1) // + lwu t7,60(a1) // + addu a1,a1,64 // advance source address + daddu a3,a3,t0 // compute 32-byte block checksum + daddu a3,a3,t1 // + daddu a3,a3,t2 // + daddu a3,a3,t3 // + daddu a3,a3,t4 // + daddu a3,a3,t5 // + daddu a3,a3,t6 // + bne t8,a1,55b // if ne, not end of block + daddu a3,a3,t7 // + .set at + .set reorder + +// +// Compute the checksum of in 2-byte blocks. +// + +70: addu t8,a1,a2 // compute ending block address + beq zero,a2,90f // if eq, no bytes to checksum + + .set noreorder + .set noat +80: lhu t0,0(a1) // compute checksum of 2-byte block + addu a1,a1,2 // advance source address + bne t8,a1,80b // if ne, more 2-byte blocks + daddu a3,a3,t0 // + .set at + .set reorder + +// +// Combine input checksum and paritial checksum. +// +// If the input buffer was byte aligned, then word swap bytes in computed +// checksum before combination with the input checksum. +// + +90: beq zero,v1,100f // if eq, buffer word aligned + li t6,0xff00ff // get byte swap mask + dsll t7,t6,32 // + or t6,t6,t7 // + and t3,a3,t6 // isolate bytes 0, 2, 4, and 6 + dsll t3,t3,8 // shift bytes 0, 2, 4, and 6 into position + dsrl t4,a3,8 // shift bytes 1, 3, 5, and 7 into position + and t4,t4,t6 // isolate bytes 1, 3, 5, and 7 + or a3,t4,t3 // merge checksum bytes +100: dsll a0,a0,32 // make sure upper 32 bits are clear + dsrl a0,a0,32 // + daddu v0,a0,a3 // combine checksums + dsrl t0,v0,32 // swap checksum longs + dsll t1,v0,32 // + or t0,t0,t1 // + daddu v0,v0,t0 // compute 32-bit checksum with carry + dsrl v0,v0,32 // + srl t0,v0,16 // swap checksum words + sll t1,v0,16 // + or t0,t0,t1 // + addu v0,v0,t0 // add words with carry into high word + srl v0,v0,16 // extract final checksum + j ra // return + + .end tcpxsum diff --git a/private/ntos/tdi/tcpip/tcp/mp/makefile b/private/ntos/tdi/tcpip/tcp/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/tcp/mp/sources b/private/ntos/tdi/tcpip/tcp/mp/sources new file mode 100644 index 000000000..dfff2b249 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/mp/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +LINKLIBS=..\..\ip\mp\obj\*\ip.lib +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf b/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf new file mode 100644 index 000000000..4f03cef97 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf @@ -0,0 +1,297 @@ +TdiQueryInformation@20 +TCPDispatchInternalDeviceControl@8 +DerefTCB@8 +FreeARPBuffer@8 +ProcessTCBDelayQ@0 +ARPTransmit@16 +TCPRcv@48 +IPRcv@28 +IPRcvComplete@0 +GetARPBuffer@12 +FreeIPPacket@4 +TCBTimeout@8 +ARPTimeout@8 +ICMPTimer@4 +IPTimeout@8 +IGMPTimer@4 +IndicateData@16 +TCPSendComplete@8 +GetSendReq@0 +FreeSendReq@4 +FillTCPHeader@8 +RcvWin@4 +IPTransmit@36 +GetRcvReq@0 +GetTCPHeader@0 +FindTCB@16 +TCPSendData@8 +TdiSend@16 +TCPSend@8 +ACKData@8 +FreeRBChain@4 +FreeRcvReq@4 +TCPDataRequestComplete@12 +FindUserRcv@4 +TCPRcvComplete@0 +GetLocalNTE@8 +GetConnFromConnID@4 +ARPSendData@16 +XsumRcvBuf@8 +FreeTCPHeader@4 +ARPRcv@28 +XsumSendChain@8 +CTEStartTimer@16 +DeliverToUser@32 +GetIPPacket@0 +ARPRcvComplete@4 +CTESystemUpTime@0 +ARPSendComplete@12 +IPSendComplete@12 +DelayAction@8 +TCPPrepareIrpForCancel@12 +BufferData@16 +SendACK@4 +CompleteRcvs@4 +FreePartialRB@8 +GetAddrObj@16 +AddrOnIF@8 +FindRTE@20 +TdiCopyBufferToMdl@24 +TCPGetMdlChainByteCount@4 +IPHash@4 +LookupRTE@12 +OpenRCE@24 +TCPQueryInformation@8 +CopyFlatToNdis@20 +FindListenConn@16 +DummyDone@8 +GetAddrType@4 +InitTCBFromConn@16 +tcpxsum@12 +FindMSS@4 +DelayDerefAO@4 +FindRCE@12 +TryToCloseTCB@12 +InitRCE@4 +NotifyOfDisc@12 +UpdateConnInfo@16 +GoToEstab@4 +TCPDisconnect@8 +FreeConnReq@4 +TdiDisconnect@20 +IPGetLocalMTU@8 +CloseRCE@4 +IPInitOptions@4 +AdjustRcvWin@4 +BuildTDIAddress@12 +SendSYN@8 +AllocTCB@0 +ProcessUserOptions@8 +FreeTCB@4 +RemoveTCBFromConn@4 +InitSendState@4 +CompleteConnReq@12 +IPFreeOptions@4 +TCPRequestComplete@12 +InvalidSourceAddress@4 +AcceptConn@8 +RemoveTCB@4 +GetConnReq@0 +CloseTCB@8 +RemoveConnFromTCB@4 +FinishRemoveTCBFromConn@4 +IsBCastOnNTE@8 +InsertTCB@4 +ResetSendNext@8 +ARPLookup@12 +BCastRcv@40 +IPGetAddrType@4 +UDPRcv@48 +GetFirstAddrObj@16 +IPForward@32 +GetNextAddrObj@4 +UDPDeliver@32 +LockedDerefIF@4 +BestNTEForIF@8 +ARPInvalidate@8 +IsBCastOnIF@8 +ARPSendBCast@16 +UDPSendDatagram@8 +DGSendComplete@8 +TdiSendDatagram@20 +DerefAO@4 +FreeDGSendReq@4 +GetDGSendReq@0 +FreeDGHeader@4 +UDPSend@8 +GetAddress@12 +ARPRemoveRCE@8 +HandleARPPacket@24 +IsLocalAddr@8 +ARPLocalAddr@8 +IPRouteTimeout@8 +GetConnID@4 +TdiAssociateAddress@8 +FindEA@12 +TCPCreate@12 +TCPAssociateAddress@8 +TdiOpenConnection@8 +TCPDispatch@8 +TdiMapUserRequest@12 +IPGetPInfo@16 +SendARPPacket@40 +RemoveARPTableEntry@8 +CreateARPTableEntry@12 +SendARPRequest@20 +GrowARPHeaders@4 +GrowTCPHeaderList@0 +TdiCloseConnection@4 +RemoveConnFromAO@8 +TCPCloseObjectComplete@12 +TCPClose@8 +CloseDone@8 +TCPCleanup@12 +FreeConnID@4 +DummyCmplt@12 +LoopAddAddr@16 +FindIGMPAddr@12 +CTEInitEvent@8 +TdiSetEvent@16 +ARPSetMCastList@4 +IPGetInfo@8 +IGMPAddrChange@12 +ICMPInit@4 +IGMPInit@0 +ARPRequestComplete@12 +FreeAORequest@4 +TCPQueryInformationEx@8 +ARPFindMCast@12 +InitAdapter@4 +CTESignal@8 +CTEBlock@4 +CTEInitTimer@4 +InsertAddrObj@4 +TdiQueryInformationEx@20 +ARPAddAddr@16 +TdiOpenAddress@16 +InitNTERouting@12 +ARPAddMCast@8 +InitIGMPForNTE@4 +FindAnyAddrObj@8 +GrowDGHeaderList@0 +FindSpecificRTE@20 +InitNTE@4 +CopyToNdis@16 +IPRegisterProtocol@20 +GetAddrOptions@12 +InitInterface@8 +TdiSetInformationEx@16 +TCPConnect@8 +TCPQueryInformationExComplete@12 +TCPSetInformationEx@8 +LockedAddRoute@36 +TCPAcdBind@0 +AddRoute@36 +TdiConnect@16 +LoopXmit@16 +AddValueSecurityFilter@12 +InitGateway@4 +ARPOAComplete@12 +CTEScheduleEvent@8 +GetHashMask@8 +InitLoopback@4 +RawStatus@28 +FindInterfaceEntry@4 +FindProtocolEntry@8 +AddProtocolSecurityFilter@12 +TdiRegisterNetAddress@8 +UDPStatus@28 +ARPDynRegister@36 +FindInsertPoint@4 +IPAddInterface@20 +NotifyAddrChange@28 +TCPStatus@28 +TdiInitialize@0 +TdiRegisterDeviceObject@8 +TdiRegisterAddressChangeHandler@12 +CTEInitialize@0 +ARPBindAdapter@20 +ICMPStatus@28 +IPQueryInfo@16 +DeleteProtocolValueEntries@4 +ModifyProtocolEntry@12 +ModifyInterfaceEntry@16 +ModifySecurityFilter@16 +DoNDISRequest@24 +LoopGetEList@12 +TCPDisassociateAddress@8 +TdiDisAssociateAddress@4 +EnumRegMultiSz@12 +AddressArrival@4 +ARPGetEList@12 +GrowIPPacketList@0 +IPGetEList@8 +RTValidateContext@8 +RTReadNext@8 +GetCurrentRouteTable@4 +AddNTERoutes@4 +GetSecurityFilterList@12 +LoopXmitRtn@8 +InitDG@4 +IPGetConfig@0 +InitTimestamp@0 +IPProcessAdapterSection@8 +TCPInitializeParameter@12 +TLGetIPInfo@8 +InitializeSecurityFilters@0 +IPFreeConfig@4 +InitTCPRcv@0 +InitAddr@0 +IPInit@0 +GetTime@0 +ARPInit@0 +InitTCPConn@0 +IPProcessConfiguration@0 +IPDriverEntry@8 +tlinit@0 +InitTCPSend@0 +InitTCB@0 +GetIFAddrList@8 +ARPOpen@4 +TCPDispatchDeviceControl@8 +TCPSetEventHandler@8 +ARPRegister@12 +GetRegStringValue@16 +IsDHCPZeroAddress@4 +EnumSecurityFilterValue@12 +CloseIFConfig@4 +UseEtherSNAP@4 +OpenIFConfig@8 +IPConvertStringToAddress@8 +SetRegDWORDValue@12 +GetArpCacheLife@0 +InitRegDWORDParameter@16 +GetRegMultiSZValue@12 +OpenRegKey@8 +GetGeneralIFConfig@8 +GetAlwaysSourceRoute@0 +GetRegSZValue@16 +GetRegDWORDValue@12 +IsLLInterfaceValueNull@4 +DerefIF@4 +ARPQueryInfo@20 +DriverEntry@8 +TCPGetConfigInfo@0 +SendIPPacket@28 +GetIPHdrBuffer@0 +GrowHdrBufList@0 +FreeIPHdrBuffer@4 +InitRouting@4 +TLRegisterProtocol@20 +LookupNextHopWithBuffer@28 +SetPersistentRoutesForNTE@12 +SendARPReply@28 +GetGMTDelta@0 +SendRSTFromHeader@20 +InvalidateRCEChain@4 +LoopQInfo@20 diff --git a/private/ntos/tdi/tcpip/tcp/ntautodl.c b/private/ntos/tdi/tcpip/tcp/ntautodl.c new file mode 100644 index 000000000..0c244aff7 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/ntautodl.c @@ -0,0 +1,258 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntautodl.c + +Abstract: + + NT specific routines for interfacing with the + RAS AutoDial driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) Aug 30, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + adiscolo 08-30-95 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "udp.h" +#include "tlcommon.h" +#include + +// +// Macro for calculating +// an IP address component. +// +#define UC(pIpAddr, i) ((ULONG)(((PCHAR)(pIpAddr))[i]) & 0xff) + +// +// Global variables +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Tcp '; + + + +VOID +TCPNoteNewConnection( + IN TCB *pTCB, + IN CTELockHandle Handle + ) +{ + ACD_ADDR addr; + ACD_ADAPTER adapter; + + // + // If there is a NULL source + // or destination IP address, then return. + // + if (!pTCB->tcb_saddr || !pTCB->tcb_daddr) { + CTEFreeLock(&pTCB->tcb_lock, Handle); + return; + } + // + // We also know we aren't interested in + // any connections on the 127 network. + // + if (UC(&pTCB->tcb_daddr, 0) == 127) { + CTEFreeLock(&pTCB->tcb_lock, Handle); + return; + } + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_IP; + addr.ulIpaddr = pTCB->tcb_daddr; + adapter.fType = ACD_ADAPTER_IP; + adapter.ulIpaddr = pTCB->tcb_saddr; + // + // Release the TCB lock handle before + // calling out of this driver. + // + CTEFreeLock(&pTCB->tcb_lock, Handle); + // + // Inform the automatic connection driver + // of the new connection. + // + (*AcdDriverG.lpfnNewConnection)(&addr, &adapter); +} // TCPNoteNewConnection + + + +VOID +TCPAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // TCPAcdBind + + + +VOID +TCPAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // TCPAcdUnbind + + diff --git a/private/ntos/tdi/tcpip/tcp/ntdisp.c b/private/ntos/tdi/tcpip/tcp/ntdisp.c new file mode 100644 index 000000000..15c6f10cc --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/ntdisp.c @@ -0,0 +1,4063 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntdisp.c + +Abstract: + + NT specific routines for dispatching and handling IRPs. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "udp.h" +#include "raw.h" +#include +#include +#include "tcpcfg.h" +#include "secfltr.h" +#include "tcpconn.h" + +// +// Macros +// +//++ +// +// LARGE_INTEGER +// CTEConvert100nsToMilliseconds( +// IN LARGE_INTEGER HnsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// HnsTime - Time in hundreds of nanoseconds. +// +// Return Value: +// +// Time in milliseconds. +// +//-- + +#define SHIFT10000 13 +static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; + +#define CTEConvert100nsToMilliseconds(HnsTime) \ + RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) + + +// +// Global variables +// +extern PDEVICE_OBJECT TCPDeviceObject, UDPDeviceObject; +extern PDEVICE_OBJECT IPDeviceObject; +extern PDEVICE_OBJECT RawIPDeviceObject; + + +// +// Local types +// +typedef struct { + PIRP Irp; + PMDL InputMdl; + PMDL OutputMdl; + TCP_REQUEST_QUERY_INFORMATION_EX QueryInformation; +} TCP_QUERY_CONTEXT, *PTCP_QUERY_CONTEXT; + + + +// +// General external function prototypes +// +extern +NTSTATUS +IPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// External TDI function prototypes +// +extern TDI_STATUS +TdiOpenAddress( + PTDI_REQUEST Request, + TRANSPORT_ADDRESS UNALIGNED *AddrList, + uint protocol, + void *Reuse + ); + +extern TDI_STATUS +TdiCloseAddress( + PTDI_REQUEST Request + ); + +extern TDI_STATUS +TdiOpenConnection( + PTDI_REQUEST Request, + PVOID Context + ); + +extern TDI_STATUS +TdiCloseConnection( + PTDI_REQUEST Request + ); + +extern TDI_STATUS +TdiAssociateAddress( + PTDI_REQUEST Request, + HANDLE AddrHandle + ); + +extern TDI_STATUS +TdiCancelDisAssociateAddress( + PTDI_REQUEST Request + ); + +extern TDI_STATUS +TdiDisAssociateAddress( + PTDI_REQUEST Request + ); + +extern TDI_STATUS +TdiConnect( + PTDI_REQUEST Request, + void *Timeout, + PTDI_CONNECTION_INFORMATION RequestAddr, + PTDI_CONNECTION_INFORMATION ReturnAddr + ); + +extern TDI_STATUS +TdiListen( + PTDI_REQUEST Request, + ushort Flags, + PTDI_CONNECTION_INFORMATION AcceptableAddr, + PTDI_CONNECTION_INFORMATION ConnectedAddr + ); + +extern TDI_STATUS +TdiAccept( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION AcceptInfo, + PTDI_CONNECTION_INFORMATION ConnectedInfo + ); + +extern TDI_STATUS +TdiDisconnect( + PTDI_REQUEST Request, + void *TO, + ushort Flags, + PTDI_CONNECTION_INFORMATION DiscConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo + ); + +extern TDI_STATUS +TdiSend( + PTDI_REQUEST Request, + ushort Flags, + uint SendLength, + PNDIS_BUFFER SendBuffer + ); + +extern TDI_STATUS +TdiReceive( + PTDI_REQUEST Request, + ushort *Flags, + uint *RcvLength, + PNDIS_BUFFER Buffer + ); + +extern TDI_STATUS +TdiSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + uint DataSize, + uint *BytesSent, + PNDIS_BUFFER Buffer + ); + +VOID +TdiCancelSendDatagram( + AddrObj *SrcAO, + PVOID Context + ); + +extern TDI_STATUS +TdiReceiveDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo, + uint RcvSize, + uint *BytesRcvd, + PNDIS_BUFFER Buffer + ); + +VOID +TdiCancelReceiveDatagram( + AddrObj *SrcAO, + PVOID Context + ); + +extern TDI_STATUS +TdiSetEvent( + PVOID Handle, + int Type, + PVOID Handler, + PVOID Context + ); + +extern TDI_STATUS +TdiQueryInformation( + PTDI_REQUEST Request, + uint QueryType, + PNDIS_BUFFER Buffer, + uint *BytesReturned, + uint IsConn + ); + +extern TDI_STATUS +TdiSetInformation( + PTDI_REQUEST Request, + uint SetType, + PNDIS_BUFFER Buffer, + uint BufferSize, + uint IsConn + ); + +extern TDI_STATUS +TdiQueryInformationEx( + PTDI_REQUEST Request, + struct TDIObjectID *ID, + PNDIS_BUFFER Buffer, + uint *Size, + void *Context + ); + +extern TDI_STATUS +TdiSetInformationEx( + PTDI_REQUEST Request, + struct TDIObjectID *ID, + void *Buffer, + uint Size + ); + +extern TDI_STATUS +TdiAction( + PTDI_REQUEST Request, + uint ActionType, + PNDIS_BUFFER Buffer, + uint BufferSize + ); + +// +// Other external functions +// +void +TCPAbortAndIndicateDisconnect( + uint ConnnectionContext + ); + + +// +// Local pageable function prototypes +// +NTSTATUS +TCPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPCreate( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPAssociateAddress( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPSetEventHandler( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPQueryInformation( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +FILE_FULL_EA_INFORMATION UNALIGNED * +FindEA( + PFILE_FULL_EA_INFORMATION StartEA, + CHAR *TargetName, + USHORT TargetNameLength + ); + +BOOLEAN +IsDHCPZeroAddress( + TRANSPORT_ADDRESS UNALIGNED *AddrList + ); + +ULONG +RawExtractProtocolNumber( + IN PUNICODE_STRING FileName + ); + +#ifdef SECFLTR + +NTSTATUS +TCPControlSecurityFilter( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPProcessSecurityFilterRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +TCPEnumerateSecurityFilter( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +#endif // SECFLTR + +NTSTATUS +TCPEnumerateConnectionList( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +// +// Local helper routine prototypes. +// +ULONG +TCPGetMdlChainByteCount( + PMDL Mdl + ); + + +// +// All of this code is pageable. +// +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, TCPDispatchDeviceControl) +#pragma alloc_text(PAGE, TCPCreate) +#pragma alloc_text(PAGE, TCPAssociateAddress) +#pragma alloc_text(PAGE, TCPSetEventHandler) +#pragma alloc_text(PAGE, FindEA) +#pragma alloc_text(PAGE, IsDHCPZeroAddress) +#pragma alloc_text(PAGE, RawExtractProtocolNumber) + +#ifdef SECFLTR + +#pragma alloc_text(PAGE, TCPControlSecurityFilter) +#pragma alloc_text(PAGE, TCPProcessSecurityFilterRequest) +#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter) + +#endif // SECFLTR + +#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter) + +#endif // ALLOC_PRAGMA + + + +// +// Generic Irp completion and cancellation routines. +// + +void +TCPDataRequestComplete( + void *Context, + unsigned int Status, + unsigned int ByteCount + ) + +/*++ + +Routine Description: + + Completes a UDP/TCP send/receive request. + +Arguments: + + Context - A pointer to the IRP for this request. + Status - The final TDI status of the request. + ByteCount - Bytes sent/received information. + +Return Value: + + None. + +Notes: + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PTCP_CONTEXT tcpContext; + PIRP item = NULL; + + + irp = (PIRP) Context; + irpSp = IoGetCurrentIrpStackLocation(irp); + tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; + + IoAcquireCancelSpinLock(&oldIrql); + +#if DBG + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + + PLIST_ENTRY entry, listHead; + PIRP item = NULL; + + if (irp->Cancel) { + CTEAssert(irp->CancelRoutine == NULL); + listHead = &(tcpContext->CancelledIrpList); + } + else { + listHead = &(tcpContext->PendingIrpList); + } + + // + // Verify that the Irp is on the appropriate list + // + for ( entry = listHead->Flink; + entry != listHead; + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + if (item == irp) { + RemoveEntryList(&(irp->Tail.Overlay.ListEntry)); + break; + } + } + + CTEAssert(item == irp); + } + +#endif + + IoSetCancelRoutine(irp, NULL); + + CTEAssert(tcpContext->ReferenceCount > 0); + + if (--(tcpContext->ReferenceCount) == 0) { + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); + CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); + } + + // + // Set the cleanup event. + // + KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); + } + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPDataRequestComplete: Irp %lx fileobj %lx refcnt dec to %u\n", + irp, + irpSp->FileObject, + tcpContext->ReferenceCount + )); + } + + if (irp->Cancel || tcpContext->CancelIrps) { + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(("TCPDataRequestComplete: Irp %lx was cancelled\n", irp)); + } + + Status = (unsigned int) STATUS_CANCELLED; + ByteCount = 0; + } + + IoReleaseCancelSpinLock(oldIrql); + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPDataRequestComplete: completing irp %lx, status %lx, byte count %lx\n", + irp, + Status, + ByteCount + )); + } + + irp->IoStatus.Status = (NTSTATUS) Status; + irp->IoStatus.Information = ByteCount; + + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + + return; + +} // TCPDataRequestComplete + + +void +TCPRequestComplete( + void *Context, + unsigned int Status, + unsigned int UnUsed + ) + +/*++ + +Routine Description: + + Completes a cancellable TDI request which returns no data by + calling TCPDataRequestComplete with a ByteCount of zero. + +Arguments: + + Context - A pointer to the IRP for this request. + Status - The final TDI status of the request. + UnUsed - An unused parameter + +Return Value: + + None. + +Notes: + +--*/ + +{ + UNREFERENCED_PARAMETER(UnUsed); + + TCPDataRequestComplete(Context, Status, 0); + +} // TCPRequestComplete + + + +void +TCPNonCancellableRequestComplete( + void *Context, + unsigned int Status, + unsigned int UnUsed + ) + +/*++ + +Routine Description: + + Completes a TDI request which cannot be cancelled. + +Arguments: + + Context - A pointer to the IRP for this request. + Status - The final TDI status of the request. + UnUsed - An unused parameter + +Return Value: + + None. + +Notes: + +--*/ + +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + + + UNREFERENCED_PARAMETER(UnUsed); + + irp = (PIRP) Context; + irpSp = IoGetCurrentIrpStackLocation(irp); + + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(( + "TCPNonCancellableRequestComplete: irp %lx status %lx\n", + irp, + Status + )); + } + + // + // Complete the IRP + // + irp->IoStatus.Status = (NTSTATUS) Status; + irp->IoStatus.Information = 0; + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + + return; + +} // TCPNonCancellableRequestComplete + + + +void +TCPCancelComplete( + void *Context, + unsigned int Unused1, + unsigned int Unused2 + ) +{ + PFILE_OBJECT fileObject = (PFILE_OBJECT) Context; + PTCP_CONTEXT tcpContext = (PTCP_CONTEXT) fileObject->FsContext; + KIRQL oldIrql; + + + UNREFERENCED_PARAMETER(Unused1); + UNREFERENCED_PARAMETER(Unused2); + + IoAcquireCancelSpinLock(&oldIrql); + + // + // Remove the reference placed on the endpoint by the cancel routine. + // The cancelled Irp will be completed by the completion routine for the + // request. + // + if (--(tcpContext->ReferenceCount) == 0) { + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); + CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); + } + + // + // Set the cleanup event. + // + KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); + } + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPCancelComplete: fileobj %lx refcnt dec to %u\n", + fileObject, + tcpContext->ReferenceCount + )); + } + + IoReleaseCancelSpinLock(oldIrql); + + return; + +} // TCPCancelComplete + + +VOID +TCPCancelRequest( + PDEVICE_OBJECT Device, + PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels an outstanding Irp. + +Arguments: + + Device - Pointer to the device object for this request. + Irp - Pointer to I/O request packet + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PTCP_CONTEXT tcpContext; + NTSTATUS status = STATUS_SUCCESS; + PFILE_OBJECT fileObject; + UCHAR minorFunction; + TDI_REQUEST request; + + + irpSp = IoGetCurrentIrpStackLocation(Irp); + fileObject = irpSp->FileObject; + tcpContext = (PTCP_CONTEXT) fileObject->FsContext; + minorFunction = irpSp->MinorFunction; + + CTEAssert(Irp->Cancel); + IoSetCancelRoutine(Irp, NULL); + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPCancelRequest: cancelling irp %lx, file object %lx\n", + Irp, + fileObject + )); + } + +#if DBG + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + // + // Verify that the Irp is on the pending list + // + PLIST_ENTRY entry; + PIRP item = NULL; + + + for ( entry = tcpContext->PendingIrpList.Flink; + entry != &(tcpContext->PendingIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + if (item == Irp) { + RemoveEntryList( &(Irp->Tail.Overlay.ListEntry)); + break; + } + } + + CTEAssert(item == Irp); + + InsertTailList( + &(tcpContext->CancelledIrpList), + &(Irp->Tail.Overlay.ListEntry) + ); + } + +#endif // DBG + + // + // Add a reference so the object can't be closed while the cancel routine + // is executing. + // + CTEAssert(tcpContext->ReferenceCount > 0); + tcpContext->ReferenceCount++; + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPCancelRequest: Irp %lx fileobj %lx refcnt inc to %u\n", + Irp, + fileObject, + tcpContext->ReferenceCount + )); + } + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // Try to cancel the request. + // + switch(minorFunction) { + + case TDI_SEND: + case TDI_RECEIVE: + + CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE); + + TCPAbortAndIndicateDisconnect( + (uint) tcpContext->Handle.ConnectionContext + ); + break; + + case TDI_SEND_DATAGRAM: + + CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); + + TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + + CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); + + TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + + CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE); + // + // This pends but is not cancellable. We put it thru the cancel code + // anyway so a reference is made for it and so it can be tracked in + // a debug build. + // + break; + + default: + + // + // Initiate a disconnect to cancel the request. + // + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPCancelComplete; + request.RequestContext = fileObject; + + status = TdiDisconnect( + &request, + NULL, + TDI_DISCONNECT_ABORT, + NULL, + NULL + ); + break; + } + + if (status != TDI_PENDING) { + TCPCancelComplete(fileObject, 0, 0); + } + + return; + +} // TCPCancelRequest + + + +NTSTATUS +TCPPrepareIrpForCancel( + PTCP_CONTEXT TcpContext, + PIRP Irp, + PDRIVER_CANCEL CancelRoutine + ) +{ + KIRQL oldIrql; + + // + // Set up for cancellation + // + IoAcquireCancelSpinLock(&oldIrql); + + CTEAssert(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + + IoMarkIrpPending(Irp); + IoSetCancelRoutine(Irp, CancelRoutine); + TcpContext->ReferenceCount++; + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPPrepareIrpForCancel: irp %lx fileobj %lx refcnt inc to %u\n", + Irp, + (IoGetCurrentIrpStackLocation(Irp))->FileObject, + TcpContext->ReferenceCount + )); + } + +#if DBG + IF_TCPDBG(TCP_DEBUG_CANCEL) { + PLIST_ENTRY entry; + PIRP item = NULL; + + // + // Verify that the Irp has not already been submitted. + // + for ( entry = TcpContext->PendingIrpList.Flink; + entry != &(TcpContext->PendingIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + for ( entry = TcpContext->CancelledIrpList.Flink; + entry != &(TcpContext->CancelledIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + InsertTailList( + &(TcpContext->PendingIrpList), + &(Irp->Tail.Overlay.ListEntry) + ); + } +#endif // DBG + + IoReleaseCancelSpinLock(oldIrql); + + return(STATUS_SUCCESS); + } + + // + // The IRP has already been cancelled. Complete it now. + // + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(("TCP: irp %lx already cancelled, completing.\n", Irp)); + } + + IoReleaseCancelSpinLock(oldIrql); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(STATUS_CANCELLED); + +} // TCPPrepareIrpForCancel + + + +// +// TDI functions +// +NTSTATUS +TCPAssociateAddress( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Associate Address IRP into a call to TdiAssociateAddress. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + NTSTATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_ASSOCIATE associateInformation; + PFILE_OBJECT fileObject; + + + PAGED_CODE(); + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + associateInformation = (PTDI_REQUEST_KERNEL_ASSOCIATE) &(IrpSp->Parameters); + + // + // Get the file object for the address. Then extract the Address Handle + // from the TCP_CONTEXT associated with it. + // + status = ObReferenceObjectByHandle( + associateInformation->AddressHandle, + 0, + NULL, + KernelMode, + &fileObject, + NULL + ); + + if (NT_SUCCESS(status)) { + + if ( (fileObject->DeviceObject == TCPDeviceObject) && + (((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) + ) { + + tcpContext = (PTCP_CONTEXT) fileObject->FsContext; + + status = TdiAssociateAddress( + &request, + tcpContext->Handle.AddressHandle + ); + + CTEAssert(status != STATUS_PENDING); + + ObDereferenceObject(fileObject); + + IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { + TCPTRACE(( + "TCPAssociateAddress complete on file object %lx\n", + IrpSp->FileObject + )); + } + } + else { + ObDereferenceObject(fileObject); + status = STATUS_INVALID_HANDLE; + + IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { + TCPTRACE(( + "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n", + associateInformation->AddressHandle, + status + )); + } + } + } + else { + IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { + TCPTRACE(( + "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n", + associateInformation->AddressHandle, + status + )); + } + } + + return(status); +} + + +NTSTATUS +TCPDisassociateAddress( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Associate Address IRP into a call to TdiAssociateAddress. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +--*/ + +{ + NTSTATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + + IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { + TCPTRACE(("TCP disassociating address\n")); + } + + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPRequestComplete; + request.RequestContext = Irp; + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiDisAssociateAddress(&request); + + if (status != TDI_PENDING) { + TCPRequestComplete(Irp, status, 0); + } + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(TDI_PENDING); + } + + return(status); + +} // TCPDisassociateAddress + + + +NTSTATUS +TCPConnect( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Connect IRP into a call to TdiConnect. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PTCP_CONTEXT tcpContext; + TDI_REQUEST request; + PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; + PTDI_REQUEST_KERNEL_CONNECT connectRequest; + LARGE_INTEGER millisecondTimeout; + PLARGE_INTEGER requestTimeout; + + + IF_TCPDBG(TCP_DEBUG_CONNECT) { + TCPTRACE(( + "TCPConnect irp %lx, file object %lx\n", + Irp, + IrpSp->FileObject + )); + } + + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); + + connectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); + requestInformation = connectRequest->RequestConnectionInformation; + returnInformation = connectRequest->ReturnConnectionInformation; + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPRequestComplete; + request.RequestContext = Irp; + + requestTimeout = (PLARGE_INTEGER) connectRequest->RequestSpecific; + + if (requestTimeout != NULL) { + // + // NT relative timeouts are negative. Negate first to get a positive + // value to pass to the transport. + // + millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart); + millisecondTimeout = CTEConvert100nsToMilliseconds( + millisecondTimeout + ); + } + else { + millisecondTimeout.LowPart = 0; + millisecondTimeout.HighPart = 0; + } + + + CTEAssert(millisecondTimeout.HighPart == 0); + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiConnect( + &request, + ((millisecondTimeout.LowPart != 0) ? + &(millisecondTimeout.LowPart) : NULL), + requestInformation, + returnInformation + ); + + if (status != STATUS_PENDING) { + TCPRequestComplete(Irp, status, 0); + } + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(STATUS_PENDING); + } + + return(status); + +} // TCPConnect + + +NTSTATUS +TCPDisconnect( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Disconnect IRP into a call to TdiDisconnect. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +Notes: + + Abortive disconnects may pend, but cannot be cancelled. + +--*/ + +{ + NTSTATUS status; + PTCP_CONTEXT tcpContext; + TDI_REQUEST request; + PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; + PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest; + LARGE_INTEGER millisecondTimeout; + PLARGE_INTEGER requestTimeout; + BOOLEAN abortive = FALSE; + + + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); + + disconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); + requestInformation = disconnectRequest->RequestConnectionInformation; + returnInformation = disconnectRequest->ReturnConnectionInformation; + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestContext = Irp; + + // + // Set up the timeout value. + // + if (disconnectRequest->RequestSpecific != NULL) { + requestTimeout = (PLARGE_INTEGER) disconnectRequest->RequestSpecific; + + if ((requestTimeout->LowPart == -1) && (requestTimeout->HighPart == -1)) { + millisecondTimeout.LowPart = requestTimeout->LowPart; + millisecondTimeout.HighPart = 0; + } + else { + // + // NT relative timeouts are negative. Negate first to get a + // positive value to pass to the transport. + // + millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart); + millisecondTimeout = CTEConvert100nsToMilliseconds( + millisecondTimeout + ); + } + } + else { + millisecondTimeout.LowPart = 0; + millisecondTimeout.HighPart = 0; + } + + CTEAssert(millisecondTimeout.HighPart == 0); + + if (disconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) { + // + // Abortive disconnects cannot be cancelled and must use + // a specific completion routine. + // + abortive = TRUE; + IoMarkIrpPending(Irp); + request.RequestNotifyObject = TCPNonCancellableRequestComplete; + status = STATUS_SUCCESS; + } + else { + // + // Non-abortive disconnects can use the generic cancellation and + // completion routines. + // + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + request.RequestNotifyObject = TCPRequestComplete; + } + + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(( + "TCPDisconnect irp %lx, flags %lx, fileobj %lx, abortive = %d\n", + Irp, + disconnectRequest->RequestFlags, + IrpSp->FileObject, + abortive + )); + } + + if (NT_SUCCESS(status)) { + status = TdiDisconnect( + &request, + ((millisecondTimeout.LowPart != 0) ? + &(millisecondTimeout.LowPart) : NULL), + (ushort) disconnectRequest->RequestFlags, + requestInformation, + returnInformation + ); + + if (status != STATUS_PENDING) { + if (abortive) { + TCPNonCancellableRequestComplete(Irp, status, 0); + } + else { + TCPRequestComplete(Irp, status, 0); + } + } + else { + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(("TCPDisconnect pending irp %lx\n", Irp)); + } + } + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(STATUS_PENDING); + } + + return(status); + +} // TCPDisconnect + + +NTSTATUS +TCPListen( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Listen IRP into a call to TdiListen. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +--*/ + +{ + NTSTATUS status; + PTCP_CONTEXT tcpContext; + TDI_REQUEST request; + PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; + PTDI_REQUEST_KERNEL_LISTEN listenRequest; + + + IF_TCPDBG(TCP_DEBUG_CONNECT) { + TCPTRACE(( + "TCPListen irp %lx on file object %lx\n", + Irp, + IrpSp->FileObject + )); + } + + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); + + listenRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); + requestInformation = listenRequest->RequestConnectionInformation; + returnInformation = listenRequest->ReturnConnectionInformation; + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPRequestComplete; + request.RequestContext = Irp; + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiListen( + &request, + (ushort) listenRequest->RequestFlags, + requestInformation, + returnInformation + ); + + if (status != TDI_PENDING) { + TCPRequestComplete(Irp, status, 0); + } + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(TDI_PENDING); + } + + return(status); + +} // TCPListen + + +NTSTATUS +TCPAccept( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Accept IRP into a call to TdiAccept. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PTCP_CONTEXT tcpContext; + TDI_REQUEST request; + PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; + PTDI_REQUEST_KERNEL_ACCEPT acceptRequest; + + + IF_TCPDBG(TCP_DEBUG_CONNECT) { + TCPTRACE(( + "TCPAccept irp %lx on file object %lx\n", Irp, + IrpSp->FileObject + )); + } + + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); + + acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT) &(IrpSp->Parameters); + requestInformation = acceptRequest->RequestConnectionInformation; + returnInformation = acceptRequest->ReturnConnectionInformation; + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPRequestComplete; + request.RequestContext = Irp; + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiAccept( + &request, + requestInformation, + returnInformation + ); + + if (status != TDI_PENDING) { + TCPRequestComplete(Irp, status, 0); + } + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(TDI_PENDING); + } + + return(status); + +} // TCPAccept + + + +NTSTATUS +TCPSendData( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Send IRP into a call to TdiSend. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +--*/ + +{ + TDI_STATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_SEND requestInformation; + KIRQL oldIrql; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); + requestInformation = (PTDI_REQUEST_KERNEL_SEND) &(IrpSp->Parameters); + + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + IoAcquireCancelSpinLock(&oldIrql); + + CTEAssert(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + // + // Set up for cancellation + // + + IoMarkIrpPending(Irp); + IoSetCancelRoutine(Irp, TCPCancelRequest); + + tcpContext->ReferenceCount++; + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPSendData: irp %lx fileobj %lx refcnt inc to %u\n", + Irp, + IrpSp, + tcpContext->ReferenceCount + )); + } + +#if DBG + IF_TCPDBG(TCP_DEBUG_CANCEL) { + PLIST_ENTRY entry; + PIRP item = NULL; + + // + // Verify that the Irp has not already been submitted. + // + for ( entry = tcpContext->PendingIrpList.Flink; + entry != &(tcpContext->PendingIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + for ( entry = tcpContext->CancelledIrpList.Flink; + entry != &(tcpContext->CancelledIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + InsertTailList( + &(tcpContext->PendingIrpList), + &(Irp->Tail.Overlay.ListEntry) + ); + } +#endif // DBG + + IoReleaseCancelSpinLock(oldIrql); + + IF_TCPDBG(TCP_DEBUG_SEND) { + TCPTRACE(( + "TCPSendData irp %lx sending %d bytes, flags %lx, fileobj %lx\n", + Irp, + requestInformation->SendLength, + requestInformation->SendFlags, + IrpSp->FileObject + )); + } + + status = TdiSend( + &request, + (ushort) requestInformation->SendFlags, + requestInformation->SendLength, + (PNDIS_BUFFER) Irp->MdlAddress + ); + + if (status == TDI_PENDING) { + IF_TCPDBG(TCP_DEBUG_SEND) { + TCPTRACE(("TCPSendData pending irp %lx\n", Irp)); + } + + return(status); + } + + // + // The status is not pending. We reset the pending bit + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + + if (status == TDI_SUCCESS) { + CTEAssert(requestInformation->SendLength == 0); + + TCPDataRequestComplete(Irp, status, requestInformation->SendLength); + } + else { + + IF_TCPDBG(TCP_DEBUG_SEND) { + TCPTRACE(( + "TCPSendData - irp %lx send failed, status %lx\n", + Irp, + status + )); + } + + TCPDataRequestComplete(Irp, status, 0); + } + + + } + else { + // + // Irp was cancelled previously. + // + IoReleaseCancelSpinLock(oldIrql); + + IF_TCPDBG(TCP_DEBUG_SEND) { + TCPTRACE(( + "TCPSendData: Irp %lx on fileobj %lx was cancelled\n", + Irp, + IrpSp->FileObject + )); + } + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + status = STATUS_CANCELLED; + } + + return(status); + +} // TCPSendData + + + +NTSTATUS +TCPReceiveData( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI Receive IRP into a call to TdiReceive. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +--*/ + +{ + TDI_STATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_RECEIVE requestInformation; + KIRQL oldIrql; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); + requestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) &(IrpSp->Parameters); + + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + IoAcquireCancelSpinLock(&oldIrql); + + CTEAssert(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + // + // Set up for cancellation + // + + IoMarkIrpPending(Irp); + IoSetCancelRoutine(Irp, TCPCancelRequest); + + tcpContext->ReferenceCount++; + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPReceiveData: irp %lx fileobj %lx refcnt inc to %u\n", + Irp, + IrpSp->FileObject, + tcpContext->ReferenceCount + )); + } + +#if DBG + IF_TCPDBG(TCP_DEBUG_CANCEL) { + PLIST_ENTRY entry; + PIRP item = NULL; + + // + // Verify that the Irp has not already been submitted. + // + for ( entry = tcpContext->PendingIrpList.Flink; + entry != &(tcpContext->PendingIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + for ( entry = tcpContext->CancelledIrpList.Flink; + entry != &(tcpContext->CancelledIrpList); + entry = entry->Flink + ) { + + item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + CTEAssert(item != Irp); + } + + InsertTailList( + &(tcpContext->PendingIrpList), + &(Irp->Tail.Overlay.ListEntry) + ); + } +#endif // DBG + + IoReleaseCancelSpinLock(oldIrql); + + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(( + "TCPReceiveData irp %lx receiving %d bytes flags %lx filobj %lx\n", + Irp, + requestInformation->ReceiveLength, + requestInformation->ReceiveFlags, + IrpSp->FileObject + )); + } + + status = TdiReceive( + &request, + (ushort *) &(requestInformation->ReceiveFlags), + &(requestInformation->ReceiveLength), + (PNDIS_BUFFER) Irp->MdlAddress + ); + + if (status == TDI_PENDING) { + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(("TCPReceiveData: pending irp %lx\n", Irp)); + } + + return(status); + } + + // + // The status is not pending. We reset the pending bit + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + + CTEAssert(status != TDI_SUCCESS); + + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(( + "TCPReceiveData - irp %lx failed, status %lx\n", + Irp, + status + )); + } + + TCPDataRequestComplete(Irp, status, 0); + } + else { + // + // Irp was cancelled previously. + // + IoReleaseCancelSpinLock(oldIrql); + + IF_TCPDBG(TCP_DEBUG_SEND) { + TCPTRACE(( + "TCPReceiveData: Irp %lx on fileobj %lx was cancelled\n", + Irp, + IrpSp->FileObject + )); + } + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + status = STATUS_CANCELLED; + } + + return status; + +} // TCPReceiveData + + + +NTSTATUS +UDPSendDatagram( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + TDI_STATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_SENDDG datagramInformation; + ULONG bytesSent = 0; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + datagramInformation = (PTDI_REQUEST_KERNEL_SENDDG) &(IrpSp->Parameters); + CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); + + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) { + TCPTRACE(( + "UDPSendDatagram irp %lx sending %d bytes\n", + Irp, + datagramInformation->SendLength + )); + } + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiSendDatagram( + &request, + datagramInformation->SendDatagramInformation, + datagramInformation->SendLength, + &bytesSent, + (PNDIS_BUFFER) Irp->MdlAddress + ); + + if (status == TDI_PENDING) { + return(status); + } + + CTEAssert(status != TDI_SUCCESS); + CTEAssert(bytesSent == 0); + + TCPTRACE(( + "UDPSendDatagram - irp %lx send failed, status %lx\n", + Irp, + status + )); + + TCPDataRequestComplete(Irp, status, bytesSent); + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(TDI_PENDING); + } + + return status; + +} // UDPSendDatagram + + + +NTSTATUS +UDPReceiveDatagram( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +--*/ + +{ + TDI_STATUS status; + TDI_REQUEST request; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_RECEIVEDG datagramInformation; + uint bytesReceived = 0; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + datagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) &(IrpSp->Parameters); + CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); + + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + IF_TCPDBG(TCP_DEBUG_RECEIVE_DGRAM) { + TCPTRACE(( + "UDPReceiveDatagram: irp %lx receiveing %d bytes\n", + Irp, + datagramInformation->ReceiveLength + )); + } + + status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); + + if (NT_SUCCESS(status)) { + + status = TdiReceiveDatagram( + &request, + datagramInformation->ReceiveDatagramInformation, + datagramInformation->ReturnDatagramInformation, + datagramInformation->ReceiveLength, + &bytesReceived, + Irp->MdlAddress + ); + + if (status == TDI_PENDING) { + return(status); + } + + CTEAssert(status != TDI_SUCCESS); + CTEAssert(bytesReceived == 0); + + TCPTRACE(( + "UDPReceiveDatagram: irp %lx send failed, status %lx\n", + Irp, + status + )); + + TCPDataRequestComplete(Irp, status, bytesReceived); + // + // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING + // + return(TDI_PENDING); + } + + return status; + +} // UDPReceiveDatagram + + + +NTSTATUS +TCPSetEventHandler( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_SET_EVENT event; + PTCP_CONTEXT tcpContext; + + PAGED_CODE(); + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + event = (PTDI_REQUEST_KERNEL_SET_EVENT) &(IrpSp->Parameters); + + IF_TCPDBG(TCP_DEBUG_EVENT_HANDLER) { + TCPTRACE(( + "TCPSetEventHandler: irp %lx event %lx handler %lx context %lx\n", + Irp, + event->EventType, + event->EventHandler, + event->EventContext + )); + } + + status = TdiSetEvent( + tcpContext->Handle.AddressHandle, + event->EventType, + event->EventHandler, + event->EventContext + ); + + CTEAssert(status != TDI_PENDING); + + return(status); + +} // TCPSetEventHandler + + + +NTSTATUS +TCPQueryInformation( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI QueryInformation IRP into a call to TdiQueryInformation. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + +--*/ + +{ + TDI_REQUEST request; + TDI_STATUS status = STATUS_SUCCESS; + PTCP_CONTEXT tcpContext; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryInformation; + uint isConn = FALSE; + uint dataSize = 0; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + queryInformation = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION) + &(IrpSp->Parameters); + + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + switch(queryInformation->QueryType) { + + case TDI_QUERY_BROADCAST_ADDRESS: + CTEAssert( ((int) IrpSp->FileObject->FsContext2) == + TDI_CONTROL_CHANNEL_FILE + ); + request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; + break; + + case TDI_QUERY_PROVIDER_INFO: +// +// NetBT does this. Reinstate the CTEAssert when it is fixed. +// +// CTEAssert( ((int) IrpSp->FileObject->FsContext2) == +// TDI_CONTROL_CHANNEL_FILE +// ); + request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; + break; + + case TDI_QUERY_ADDRESS_INFO: + if (((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) { + // + // This is a TCP connection object. + // + isConn = TRUE; + request.Handle.ConnectionContext = + tcpContext->Handle.ConnectionContext; + } + else { + // + // This is an address object + // + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + } + break; + + case TDI_QUERY_CONNECTION_INFO: + CTEAssert(((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); + isConn = TRUE; + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + CTEAssert( ((int) IrpSp->FileObject->FsContext2) == + TDI_CONTROL_CHANNEL_FILE + ); + request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; + break; + + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (NT_SUCCESS(status)) { + // + // This request isn't cancellable, but we put it through + // the cancel path because it handles some checks for us + // and tracks the irp. + // + status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); + + if (NT_SUCCESS(status)) { + dataSize = TCPGetMdlChainByteCount(Irp->MdlAddress); + + status = TdiQueryInformation( + &request, + queryInformation->QueryType, + Irp->MdlAddress, + &dataSize, + isConn + ); + + if (status != TDI_PENDING) { + TCPDataRequestComplete(Irp, status, dataSize); + return(status); + + } + + return(STATUS_PENDING); + } + + return(status); + } + + Irp->IoStatus.Status = (NTSTATUS) status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); + +} // TCPQueryInformation + + + +void +TCPQueryInformationExComplete( + void *Context, + unsigned int Status, + unsigned int ByteCount + ) + +/*++ + +Routine Description: + + Completes a TdiQueryInformationEx request. + +Arguments: + + Context - A pointer to the IRP for this request. + Status - The final TDI status of the request. + ByteCount - Bytes returned in output buffer. + +Return Value: + + None. + +Notes: + +--*/ +{ + PTCP_QUERY_CONTEXT queryContext = (PTCP_QUERY_CONTEXT) Context; + ULONG bytesCopied; + + + if (NT_SUCCESS(Status)) { + // + // Copy the returned context to the input buffer. + // + TdiCopyBufferToMdl( + &(queryContext->QueryInformation.Context), + 0, + CONTEXT_SIZE, + queryContext->InputMdl, + FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context), + &bytesCopied + ); + + CTEAssert(bytesCopied == CONTEXT_SIZE); + } + + // + // Unlock the user's buffers and free the MDLs describing them. + // + MmUnlockPages(queryContext->InputMdl); + IoFreeMdl(queryContext->InputMdl); + MmUnlockPages(queryContext->OutputMdl); + IoFreeMdl(queryContext->OutputMdl); + + // + // Complete the request + // + TCPDataRequestComplete(queryContext->Irp, Status, ByteCount); + + CTEFreeMem(queryContext); + + return; +} + + + +NTSTATUS +TCPQueryInformationEx( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + +--*/ + +{ + TDI_REQUEST request; + TDI_STATUS status = STATUS_SUCCESS; + PTCP_CONTEXT tcpContext; + uint size; + PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer; + PVOID OutputBuffer; + PMDL inputMdl = NULL; + PMDL outputMdl = NULL; + ULONG InputBufferLength, + OutputBufferLength; + PTCP_QUERY_CONTEXT queryContext; + BOOLEAN inputLocked = FALSE; + BOOLEAN outputLocked = FALSE; + + + PAGED_CODE(); + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "QueryInformationEx starting - irp %lx fileobj %lx\n", + Irp, + IrpSp->FileObject + )); + } + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + + switch ((int) IrpSp->FileObject->FsContext2) { + + case TDI_TRANSPORT_ADDRESS_FILE: + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + break; + + case TDI_CONNECTION_FILE: + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + break; + + case TDI_CONTROL_CHANNEL_FILE: + request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; + break; + + default: + CTEAssert(0); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(STATUS_INVALID_PARAMETER); + } + + InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + // + // Validate the input parameters + // + if ( (InputBufferLength == sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)) && + (OutputBufferLength != 0) + ) + { + OutputBuffer = Irp->UserBuffer; + InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX) + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + queryContext = CTEAllocMem(sizeof(TCP_QUERY_CONTEXT)); + + if (queryContext != NULL) { + status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); + + if (!NT_SUCCESS(status)) { + CTEFreeMem(queryContext); + return(status); + } + + // + // Allocate Mdls to describe the input and output buffers. + // Probe and lock the buffers. + // + try { + inputMdl = IoAllocateMdl( + InputBuffer, + sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), + FALSE, + TRUE, + NULL + ); + + outputMdl = IoAllocateMdl( + OutputBuffer, + OutputBufferLength, + FALSE, + TRUE, + NULL + ); + + if ((inputMdl != NULL) && (outputMdl != NULL)) { + + MmProbeAndLockPages( + inputMdl, + Irp->RequestorMode, + IoModifyAccess + ); + + inputLocked = TRUE; + + MmProbeAndLockPages( + outputMdl, + Irp->RequestorMode, + IoWriteAccess + ); + + outputLocked = TRUE; + + // + // Copy the input parameter to our pool block so + // TdiQueryInformationEx can manipulate it directly. + // + RtlCopyMemory( + &(queryContext->QueryInformation), + InputBuffer, + sizeof(TCP_REQUEST_QUERY_INFORMATION_EX) + ); + } + else { + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(("QueryInfoEx: Couldn't allocate MDL\n")); + } + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "QueryInfoEx: exception copying input params %lx\n", + GetExceptionCode() + )); + } + + status = GetExceptionCode(); + } + + if (NT_SUCCESS(status)) { + // + // It's finally time to do this thing. + // + size = TCPGetMdlChainByteCount(outputMdl); + + queryContext->Irp = Irp; + queryContext->InputMdl = inputMdl; + queryContext->OutputMdl = outputMdl; + + request.RequestNotifyObject = TCPQueryInformationExComplete; + request.RequestContext = queryContext; + + status = TdiQueryInformationEx( + &request, + &(queryContext->QueryInformation.ID), + outputMdl, + &size, + &(queryContext->QueryInformation.Context) + ); + + if (status != TDI_PENDING) { + TCPQueryInformationExComplete( + queryContext, + status, + size + ); + + return(status); + } + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "QueryInformationEx - pending irp %lx fileobj %lx\n", + Irp, + IrpSp->FileObject + )); + } + + return(STATUS_PENDING); + } + + // + // If we get here, something failed. Clean up. + // + if (inputMdl != NULL) { + if (inputLocked) { + MmUnlockPages(inputMdl); + } + + IoFreeMdl(inputMdl); + } + + if (outputMdl != NULL) { + if (outputLocked) { + MmUnlockPages(outputMdl); + } + + IoFreeMdl(outputMdl); + } + + CTEFreeMem(queryContext); + + TCPDataRequestComplete(Irp, status, 0); + + return(status); + + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(("QueryInfoEx: Unable to allocate query context\n")); + } + } + } + else { + status = STATUS_INVALID_PARAMETER; + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n", + OutputBufferLength, InputBufferLength + )); + } + } + + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "QueryInformationEx complete - irp %lx, status %lx\n", + Irp, + status + )); + } + + Irp->IoStatus.Status = (NTSTATUS) status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + + return(status); +} + + + +NTSTATUS +TCPSetInformationEx( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + TDI_REQUEST request; + TDI_STATUS status; + PTCP_CONTEXT tcpContext; + PTCP_REQUEST_SET_INFORMATION_EX setInformation; + + + PAGED_CODE(); + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "SetInformationEx - irp %lx fileobj %lx\n", + Irp, + IrpSp->FileObject + )); + } + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + setInformation = (PTCP_REQUEST_SET_INFORMATION_EX) + Irp->AssociatedIrp.SystemBuffer; + + switch ((int) IrpSp->FileObject->FsContext2) { + + case TDI_TRANSPORT_ADDRESS_FILE: + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + break; + + case TDI_CONNECTION_FILE: + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + break; + + case TDI_CONTROL_CHANNEL_FILE: + request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; + break; + + default: + CTEAssert(0); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(STATUS_INVALID_PARAMETER); + } + + status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); + + if (NT_SUCCESS(status)) { + request.RequestNotifyObject = TCPDataRequestComplete; + request.RequestContext = Irp; + + status = TdiSetInformationEx( + &request, + &(setInformation->ID), + &(setInformation->Buffer[0]), + setInformation->BufferSize + ); + + if (status != TDI_PENDING) { + TCPDataRequestComplete( + Irp, + status, + 0 + ); + + return(status); + } + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "SetInformationEx - pending irp %lx fileobj %lx\n", + Irp, + IrpSp->FileObject + )); + } + + return(STATUS_PENDING); + } + + IF_TCPDBG(TCP_DEBUG_INFO) { + TCPTRACE(( + "SetInformationEx complete - irp %lx\n", + Irp + )); + } + + // + // The irp has already been completed. + // + return(status); +} + + +#ifdef SECFLTR + + + +NTSTATUS +TCPControlSecurityFilter( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes a request to query or set the status of security filtering. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + + PTCP_SECURITY_FILTER_STATUS request; + ULONG requestLength; + ULONG requestCode; + TDI_STATUS status = STATUS_SUCCESS; + + + PAGED_CODE(); + + Irp->IoStatus.Information = 0; + + request = (PTCP_SECURITY_FILTER_STATUS) Irp->AssociatedIrp.SystemBuffer; + requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; + + if (requestCode == IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS) { + requestLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) { + status = STATUS_INVALID_PARAMETER; + } + else { + request->FilteringEnabled = IsSecurityFilteringEnabled(); + Irp->IoStatus.Information = sizeof(TCP_SECURITY_FILTER_STATUS); + } + } + else { + CTEAssert(requestCode == IOCTL_TCP_SET_SECURITY_FILTER_STATUS); + + requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) { + status = STATUS_INVALID_PARAMETER; + } + else { + ControlSecurityFiltering(request->FilteringEnabled); + } + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); +} + + + +NTSTATUS +TCPProcessSecurityFilterRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes a request to add or delete a transport security filter. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + TCPSecurityFilterEntry *request; + ULONG requestLength; + ULONG i; + ULONG requestCode; + NTSTATUS status = STATUS_SUCCESS; + + + PAGED_CODE(); + + Irp->IoStatus.Information = 0; + + request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer; + requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; + + if (requestLength < sizeof(TCPSecurityFilterEntry)) { + status = STATUS_INVALID_PARAMETER; + } + else { + if (requestCode == IOCTL_TCP_ADD_SECURITY_FILTER) { + status = AddValueSecurityFilter( + net_long(request->tsf_address), + request->tsf_protocol, + request->tsf_value + ); + } + else { + CTEAssert(requestCode == IOCTL_TCP_DELETE_SECURITY_FILTER); + status = DeleteValueSecurityFilter( + net_long(request->tsf_address), + request->tsf_protocol, + request->tsf_value + ); + } + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); +} + + + +NTSTATUS +TCPEnumerateSecurityFilter( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes a request to enumerate a transport security filter list. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + + TCPSecurityFilterEntry *request; + TCPSecurityFilterEnum *response; + ULONG requestLength, responseLength; + NTSTATUS status; + + + PAGED_CODE(); + + request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer; + response = (TCPSecurityFilterEnum *) request; + requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (requestLength < sizeof(TCPSecurityFilterEntry)) { + status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + } + else if (responseLength < sizeof(TCPSecurityFilterEnum)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + } + else { + EnumerateSecurityFilters( + net_long(request->tsf_address), + request->tsf_protocol, + request->tsf_value, + (uchar *) (response + 1), + responseLength - sizeof(TCPSecurityFilterEnum), + &(response->tfe_entries_returned), + &(response->tfe_entries_available) + ); + + status = TDI_SUCCESS; + Irp->IoStatus.Information = + sizeof(TCPSecurityFilterEnum) + + (response->tfe_entries_returned * sizeof(TCPSecurityFilterEntry)); + + + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); +} + +#endif // SECFLTR + + +NTSTATUS +TCPEnumerateConnectionList( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes a request to enumerate the workstation connection list. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successful. + +Notes: + + This routine does not pend. + +--*/ + +{ + + TCPConnectionListEntry *request; + TCPConnectionListEnum *response; + ULONG requestLength, responseLength; + NTSTATUS status; + + + PAGED_CODE(); + + request = (TCPConnectionListEntry *) Irp->AssociatedIrp.SystemBuffer; + response = (TCPConnectionListEnum *) request; + requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (responseLength < sizeof(TCPConnectionListEnum)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + } + else { + EnumerateConnectionList( + (uchar *) (response + 1), + responseLength - sizeof(TCPConnectionListEnum), + &(response->tce_entries_returned), + &(response->tce_entries_available) + ); + + status = TDI_SUCCESS; + Irp->IoStatus.Information = + sizeof(TCPConnectionListEnum) + + (response->tce_entries_returned * sizeof(TCPConnectionListEntry)); + + + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); +} + + + +NTSTATUS +TCPCreate( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + DeviceObject - Pointer to the device object for this request. + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + TDI_REQUEST Request; + NTSTATUS status; + FILE_FULL_EA_INFORMATION *ea; + FILE_FULL_EA_INFORMATION UNALIGNED *targetEA; + PTCP_CONTEXT tcpContext; + uint protocol; + + + PAGED_CODE(); + + tcpContext = ExAllocatePool(NonPagedPool, sizeof(TCP_CONTEXT)); + + if (tcpContext == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + +#if DBG + InitializeListHead(&(tcpContext->PendingIrpList)); + InitializeListHead(&(tcpContext->CancelledIrpList)); +#endif + + tcpContext->ReferenceCount = 1; // put initial reference on open object + tcpContext->CancelIrps = FALSE; + KeInitializeEvent(&(tcpContext->CleanupEvent), SynchronizationEvent, FALSE); + + ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + // + // See if this is a Control Channel open. + // + if (!ea) { + IF_TCPDBG(TCP_DEBUG_OPEN) { + TCPTRACE(( + "TCPCreate: Opening control channel for file object %lx\n", + IrpSp->FileObject + )); + } + + tcpContext->Handle.ControlChannel = NULL; + IrpSp->FileObject->FsContext = tcpContext; + IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE; + + return(STATUS_SUCCESS); + } + + // + // See if this is an Address Object open. + // + targetEA = FindEA( + ea, + TdiTransportAddress, + TDI_TRANSPORT_ADDRESS_LENGTH + ); + + if (targetEA != NULL) { + UCHAR optionsBuffer[3]; + PUCHAR optionsPointer = optionsBuffer; + + + if (DeviceObject == TCPDeviceObject) { + protocol = PROTOCOL_TCP; + } + else if (DeviceObject == UDPDeviceObject) { + protocol = PROTOCOL_UDP; + + CTEAssert(optionsPointer - optionsBuffer <= 3); + + if (IsDHCPZeroAddress( + (TRANSPORT_ADDRESS UNALIGNED *) + &(targetEA->EaName[targetEA->EaNameLength + 1]) + )) { + *optionsPointer = TDI_ADDRESS_OPTION_DHCP; + optionsPointer++; + } + + CTEAssert(optionsPointer - optionsBuffer <= 3); + } + else { + // + // This is a raw ip open + // + protocol = RawExtractProtocolNumber( + &(IrpSp->FileObject->FileName) + ); + + if (protocol == 0xFFFFFFFF) { + ExFreePool(tcpContext); + return(STATUS_INVALID_PARAMETER); + } + } + + if ( (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE) + ) { + *optionsPointer = TDI_ADDRESS_OPTION_REUSE; + optionsPointer++; + } + + *optionsPointer = TDI_OPTION_EOL; + + IF_TCPDBG(TCP_DEBUG_OPEN) { + TCPTRACE(( + "TCPCreate: Opening address for file object %lx\n", + IrpSp->FileObject + )); + } + + status = TdiOpenAddress( + &Request, + (TRANSPORT_ADDRESS UNALIGNED *) + &(targetEA->EaName[targetEA->EaNameLength + 1]), + protocol, + optionsBuffer + ); + + if (NT_SUCCESS(status)) { + // + // Save off the handle to the AO passed back. + // + tcpContext->Handle.AddressHandle = Request.Handle.AddressHandle; + IrpSp->FileObject->FsContext = tcpContext; + IrpSp->FileObject->FsContext2 = + (PVOID) TDI_TRANSPORT_ADDRESS_FILE; + } + else { + ExFreePool(tcpContext); + TCPTRACE(("TdiOpenAddress failed, status %lx\n", status)); + if (status == STATUS_ADDRESS_ALREADY_EXISTS) { + status = STATUS_SHARING_VIOLATION; + } + } + + CTEAssert(status != TDI_PENDING); + + return(status); + } + + // + // See if this is a Connection Object open. + // + targetEA = FindEA( + ea, + TdiConnectionContext, + TDI_CONNECTION_CONTEXT_LENGTH + ); + + if (targetEA != NULL) { + // + // This is an open of a Connection Object. + // + + if (DeviceObject == TCPDeviceObject) { + + IF_TCPDBG(TCP_DEBUG_OPEN) { + TCPTRACE(( + "TCPCreate: Opening connection for file object %lx\n", + IrpSp->FileObject + )); + } + + status = TdiOpenConnection( + &Request, + *((CONNECTION_CONTEXT UNALIGNED *) + &(targetEA->EaName[targetEA->EaNameLength + 1])) + ); + + if (NT_SUCCESS(status)) { + // + // Save off the Connection Context passed back. + // + tcpContext->Handle.ConnectionContext = + Request.Handle.ConnectionContext; + IrpSp->FileObject->FsContext = tcpContext; + IrpSp->FileObject->FsContext2 = + (PVOID) TDI_CONNECTION_FILE; + } + else { + ExFreePool(tcpContext); + TCPTRACE(( + "TdiOpenConnection failed, status %lx\n", + status + )); + } + } + else { + TCPTRACE(( + "TCP: TdiOpenConnection issued on UDP device!\n" + )); + status = STATUS_INVALID_DEVICE_REQUEST; + ExFreePool(tcpContext); + } + + CTEAssert(status != TDI_PENDING); + + return(status); + } + + TCPTRACE(("TCPCreate: didn't find any useful ea's\n")); + status = STATUS_INVALID_EA_NAME; + ExFreePool(tcpContext); + + + CTEAssert(status != TDI_PENDING); + + return(status); + +} // TCPCreate + + + +void +TCPCloseObjectComplete( + void *Context, + unsigned int Status, + unsigned int UnUsed + ) + +/*++ + +Routine Description: + + Completes a TdiCloseConnectoin or TdiCloseAddress request. + +Arguments: + + Context - A pointer to the IRP for this request. + Status - The final status of the operation. + UnUsed - An unused parameter + +Return Value: + + None. + +Notes: + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PTCP_CONTEXT tcpContext; + + + UNREFERENCED_PARAMETER(UnUsed); + + irp = (PIRP) Context; + irpSp = IoGetCurrentIrpStackLocation(irp); + tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; + irp->IoStatus.Status = Status; + + IF_TCPDBG(TCP_DEBUG_CLEANUP) { + TCPTRACE(( + "TCPCloseObjectComplete on file object %lx\n", + irpSp->FileObject + )); + } + + IoAcquireCancelSpinLock(&oldIrql); + + CTEAssert(tcpContext->ReferenceCount > 0); + CTEAssert(tcpContext->CancelIrps); + + // + // Remove the initial reference that was put on by TCPCreate. + // + CTEAssert(tcpContext->ReferenceCount > 0); + + if (--(tcpContext->ReferenceCount) == 0) { + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); + CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); + } + + KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); + } + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPCloseObjectComplete: irp %lx fileobj %lx refcnt dec to %u\n", + irp, + irpSp, + tcpContext->ReferenceCount + )); + } + + IoReleaseCancelSpinLock(oldIrql); + + return; + +} // TCPCleanupComplete + + + +NTSTATUS +TCPCleanup( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Cancels all outstanding Irps on a TDI object by calling the close + routine for the object. It then waits for them to be completed + before returning. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +Notes: + + This routine blocks, but does not pend. + +--*/ + +{ + KIRQL oldIrql; + PIRP cancelIrp = NULL; + PTCP_CONTEXT tcpContext; + NTSTATUS status; + TDI_REQUEST request; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + + IoAcquireCancelSpinLock(&oldIrql); + + tcpContext->CancelIrps = TRUE; + KeResetEvent(&(tcpContext->CleanupEvent)); + + IoReleaseCancelSpinLock(oldIrql); + + // + // Now call the TDI close routine for this object to force all of its Irps + // to complete. + // + request.RequestNotifyObject = TCPCloseObjectComplete; + request.RequestContext = Irp; + + switch ((int) IrpSp->FileObject->FsContext2) { + + case TDI_TRANSPORT_ADDRESS_FILE: + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(( + "TCPCleanup: Closing address object on file object %lx\n", + IrpSp->FileObject + )); + } + request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; + status = TdiCloseAddress(&request); + break; + + case TDI_CONNECTION_FILE: + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(( + "TCPCleanup: Closing Connection object on file object %lx\n", + IrpSp->FileObject + )); + } + request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; + status = TdiCloseConnection(&request); + break; + + case TDI_CONTROL_CHANNEL_FILE: + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(( + "TCPCleanup: Closing Control Channel object on file object %lx\n", + IrpSp->FileObject + )); + } + status = STATUS_SUCCESS; + break; + + default: + // + // This should never happen. + // + CTEAssert(FALSE); + + IoAcquireCancelSpinLock(&oldIrql); + tcpContext->CancelIrps = FALSE; + IoReleaseCancelSpinLock(oldIrql); + + return(STATUS_INVALID_PARAMETER); + } + + if (status != TDI_PENDING) { + TCPCloseObjectComplete(Irp, status, 0); + } + + IF_TCPDBG(TCP_DEBUG_CLEANUP) { + TCPTRACE(( + "TCPCleanup: waiting for completion of Irps on file object %lx\n", + IrpSp->FileObject + )); + } + + status = KeWaitForSingleObject( + &(tcpContext->CleanupEvent), + UserRequest, + KernelMode, + FALSE, + NULL + ); + + CTEAssert(NT_SUCCESS(status)); + + IF_TCPDBG(TCP_DEBUG_CLEANUP) { + TCPTRACE(( + "TCPCleanup: Wait on file object %lx finished\n", + IrpSp->FileObject + )); + } + + // + // The cleanup Irp will be completed by the dispatch routine. + // + + return(Irp->IoStatus.Status); + +} // TCPCleanup + + +NTSTATUS +TCPClose( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the + open endpoint. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +Notes: + + This request does not pend. + +--*/ + +{ + PTCP_CONTEXT tcpContext; + + + tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; + +#if DBG + + IF_TCPDBG(TCP_DEBUG_CANCEL) { + + KIRQL oldIrql; + + IoAcquireCancelSpinLock(&oldIrql); + + CTEAssert(tcpContext->ReferenceCount == 0); + CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); + CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); + + IoReleaseCancelSpinLock(oldIrql); + } +#endif // DBG + + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(("TCPClose on file object %lx\n", IrpSp->FileObject)); + } + + ExFreePool(tcpContext); + + return(STATUS_SUCCESS); + +} // TCPClose + + + +NTSTATUS +TCPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + + + PAGED_CODE(); + + // + // Set this in advance. Any IOCTL dispatch routine that cares about it + // will modify it itself. + // + Irp->IoStatus.Information = 0; + + switch(IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TCP_QUERY_INFORMATION_EX: + return(TCPQueryInformationEx(Irp, IrpSp)); + break; + + case IOCTL_TCP_SET_INFORMATION_EX: + return(TCPSetInformationEx(Irp, IrpSp)); + break; + +#ifdef SECFLTR + + case IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS: + case IOCTL_TCP_SET_SECURITY_FILTER_STATUS: + return(TCPControlSecurityFilter(Irp, IrpSp)); + break; + + case IOCTL_TCP_ADD_SECURITY_FILTER: + case IOCTL_TCP_DELETE_SECURITY_FILTER: + return(TCPProcessSecurityFilterRequest(Irp, IrpSp)); + break; + + case IOCTL_TCP_ENUMERATE_SECURITY_FILTER: + return(TCPEnumerateSecurityFilter(Irp, IrpSp)); + break; + +#endif // SECFLTR + + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return status; + +} // TCPDispatchDeviceControl + + + +NTSTATUS +TCPDispatchInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for Internal Device Control IRPs. + This is the hot path for kernel-mode clients. + +Arguments: + + DeviceObject - Pointer to device object for target device + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + NTSTATUS status; + + + if (DeviceObject != IPDeviceObject) { + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + if (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) { + // + // Send and receive are the performance path, so check for them + // right away. + // + if (irpSp->MinorFunction == TDI_SEND) { + return(TCPSendData(Irp, irpSp)); + } + + if (irpSp->MinorFunction == TDI_RECEIVE) { + return(TCPReceiveData(Irp, irpSp)); + } + + switch(irpSp->MinorFunction) { + + case TDI_ASSOCIATE_ADDRESS: + status = TCPAssociateAddress(Irp, irpSp); + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); + + case TDI_DISASSOCIATE_ADDRESS: + return(TCPDisassociateAddress(Irp, irpSp)); + + case TDI_CONNECT: + return(TCPConnect(Irp, irpSp)); + + case TDI_DISCONNECT: + return(TCPDisconnect(Irp, irpSp)); + + case TDI_LISTEN: + return(TCPListen(Irp, irpSp)); + + case TDI_ACCEPT: + return(TCPAccept(Irp, irpSp)); + + default: + break; + } + + // + // Fall through. + // + } + else if ( ((int)irpSp->FileObject->FsContext2) == + TDI_TRANSPORT_ADDRESS_FILE + ) + { + if (irpSp->MinorFunction == TDI_SEND_DATAGRAM) { + return(UDPSendDatagram(Irp, irpSp)); + } + + if (irpSp->MinorFunction == TDI_RECEIVE_DATAGRAM) { + return(UDPReceiveDatagram(Irp, irpSp)); + } + + if (irpSp->MinorFunction == TDI_SET_EVENT_HANDLER) { + status = TCPSetEventHandler(Irp, irpSp); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); + } + + // + // Fall through. + // + } + + CTEAssert( + (((int)irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) + || + (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) + || + (((int)irpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE) + ); + + // + // These functions are common to all endpoint types. + // + switch(irpSp->MinorFunction) { + + case TDI_QUERY_INFORMATION: + return(TCPQueryInformation(Irp, irpSp)); + + case TDI_SET_INFORMATION: + case TDI_ACTION: + TCPTRACE(( + "TCP: Call to unimplemented TDI function 0x%x\n", + irpSp->MinorFunction + )); + status = STATUS_NOT_IMPLEMENTED; + break; + + default: + TCPTRACE(( + "TCP: call to invalid TDI function 0x%x\n", + irpSp->MinorFunction + )); + status = STATUS_INVALID_DEVICE_REQUEST; + } + + CTEAssert(status != TDI_PENDING); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return status; + } + + return(IPDispatch(DeviceObject, Irp)); +} + + + +NTSTATUS +TCPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the generic dispatch routine for TCP/UDP/RawIP. + +Arguments: + + DeviceObject - Pointer to device object for target device + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + NTSTATUS status; + + + if (DeviceObject != IPDeviceObject) { + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + CTEAssert(irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL); + + switch (irpSp->MajorFunction) { + + case IRP_MJ_CREATE: + status = TCPCreate(DeviceObject, Irp, irpSp); + break; + + case IRP_MJ_CLEANUP: + status = TCPCleanup(DeviceObject, Irp, irpSp); + break; + + case IRP_MJ_CLOSE: + status = TCPClose(Irp, irpSp); + break; + + case IRP_MJ_DEVICE_CONTROL: + status = TdiMapUserRequest(DeviceObject, Irp, irpSp); + + if (status == STATUS_SUCCESS) { + return(TCPDispatchInternalDeviceControl(DeviceObject, Irp)); + } + + return(TCPDispatchDeviceControl( + Irp, + IoGetCurrentIrpStackLocation(Irp) + )); + break; + + case IRP_MJ_QUERY_SECURITY: + // + // This is generated on Raw endpoints. We don't do anything + // for it. + // + status = STATUS_INVALID_DEVICE_REQUEST; + break; + + case IRP_MJ_WRITE: + case IRP_MJ_READ: + + default: + TCPTRACE(( + "TCPDispatch: Irp %lx unsupported major function 0x%lx\n", + irpSp, + irpSp->MajorFunction + )); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + CTEAssert(status != TDI_PENDING); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return status; + + } + + return(IPDispatch(DeviceObject, Irp)); + + +} // TCPDispatch + + + +// +// Private utility functions +// +FILE_FULL_EA_INFORMATION UNALIGNED * +FindEA( + PFILE_FULL_EA_INFORMATION StartEA, + CHAR *TargetName, + USHORT TargetNameLength + ) + +/*++ + +Routine Description: + + Parses and extended attribute list for a given target attribute. + +Arguments: + + StartEA - the first extended attribute in the list. + TargetName - the name of the target attribute. + TargetNameLength - the length of the name of the target attribute. + +Return Value: + + A pointer to the requested attribute or NULL if the target wasn't found. + +--*/ + +{ + USHORT i; + BOOLEAN found; + FILE_FULL_EA_INFORMATION UNALIGNED *CurrentEA; + + + PAGED_CODE(); + + do { + found = TRUE; + + CurrentEA = StartEA; + StartEA += CurrentEA->NextEntryOffset; + + if (CurrentEA->EaNameLength != TargetNameLength) { + continue; + } + + for (i=0; i < CurrentEA->EaNameLength; i++) { + if (CurrentEA->EaName[i] == TargetName[i]) { + continue; + } + found = FALSE; + break; + } + + if (found) { + return(CurrentEA); + } + + } while(CurrentEA->NextEntryOffset != 0); + + return(NULL); +} + + + +BOOLEAN +IsDHCPZeroAddress( + TRANSPORT_ADDRESS UNALIGNED *AddrList + ) + +/*++ + +Routine Description: + + Checks a TDI IP address list for an address from DHCP binding + to the IP address zero. Normally, binding to zero means wildcard. + For DHCP, it really means bind to an interface with an address of + zero. This semantic is flagged by a special value in an unused + portion of the address structure (ie. this is a kludge). + +Arguments: + + AddrList - The TDI transport address list passed in the create IRP. + +Return Value: + + TRUE if the first IP address found had the flag set. FALSE otherwise. + +--*/ + +{ + int i; // Index variable. + TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use. + + + // First, verify that someplace in Address is an address we can use. + CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address; + + for (i = 0; i < AddrList->TAAddressCount; i++) { + if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) { + if (CurrentAddr->AddressLength == TDI_ADDRESS_LENGTH_IP) { + TDI_ADDRESS_IP UNALIGNED *ValidAddr; + + ValidAddr = (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address; + + if (*((ULONG UNALIGNED *) ValidAddr->sin_zero) == 0x12345678) { + return TRUE; + } + + } else { + return FALSE; // Wrong length for address. + } + } else { + CurrentAddr = (TA_ADDRESS UNALIGNED *) + (CurrentAddr->Address + CurrentAddr->AddressLength); + } + } + + return FALSE; // Didn't find a match. +} + + + +ULONG +TCPGetMdlChainByteCount( + PMDL Mdl + ) + +/*++ + +Routine Description: + + Sums the byte counts of each MDL in a chain. + +Arguments: + + Mdl - Pointer to the MDL chain to sum. + +Return Value: + + The byte count of the MDL chain. + +--*/ + +{ + ULONG count = 0; + + while (Mdl != NULL) { + count += MmGetMdlByteCount(Mdl); + Mdl = Mdl->Next; + } + + return(count); +} + + + + +ULONG +RawExtractProtocolNumber( + IN PUNICODE_STRING FileName + ) + +/*++ + +Routine Description: + + Extracts the protocol number from the file object name. + +Arguments: + + FileName - The unicode file name. + +Return Value: + + The protocol number or 0xFFFFFFFF on error. + +--*/ + +{ + PWSTR name; + UNICODE_STRING unicodeString; + USHORT length; + ULONG protocol; + NTSTATUS status; + + + PAGED_CODE(); + + name = FileName->Buffer; + + if ( FileName->Length < + (sizeof(OBJ_NAME_PATH_SEPARATOR) + sizeof(WCHAR)) + ) + { + return(0xFFFFFFFF); + } + + // + // Step over separator + // + if (*name++ != OBJ_NAME_PATH_SEPARATOR) { + return(0xFFFFFFFF); + } + + if (*name == UNICODE_NULL) { + return(0xFFFFFFFF); + } + + // + // Convert the remaining name into a number. + // + RtlInitUnicodeString(&unicodeString, name); + + status = RtlUnicodeStringToInteger( + &unicodeString, + 10, + &protocol + ); + + if (!NT_SUCCESS(status)) { + return(0xFFFFFFFF); + } + + if (protocol > 255) { + return(0xFFFFFFFF); + } + + return(protocol); + +} + diff --git a/private/ntos/tdi/tcpip/tcp/ntinit.c b/private/ntos/tdi/tcpip/tcp/ntinit.c new file mode 100644 index 000000000..e1fdbd7f7 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/ntinit.c @@ -0,0 +1,1038 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntinit.c + +Abstract: + + NT specific routines for loading and configuring the TCP/UDP driver. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "udp.h" +#include "raw.h" +#include "tcpconn.h" +#include "tcpcfg.h" +#include +#include "secfltr.h" + +// +// Global variables. +// +PDRIVER_OBJECT TCPDriverObject = NULL; +PDEVICE_OBJECT TCPDeviceObject = NULL; +PDEVICE_OBJECT UDPDeviceObject = NULL; +PDEVICE_OBJECT RawIPDeviceObject = NULL; +extern PDEVICE_OBJECT IPDeviceObject; + +// +//Place holder for Maximum duplicate acks we would like +//to see before we do fast retransmit +// +#if FAST_RETRANSMIT +uint MaxDupAcks; +#endif + + +#ifdef _PNP_POWER + +HANDLE TCPRegistrationHandle; +HANDLE UDPRegistrationHandle; +HANDLE IPRegistrationHandle; + +#endif + +#ifdef SYN_ATTACK +BOOLEAN SynAttackProtect; //if TRUE, syn-att protection checks + // are made +uint TCPPortsExhausted; //# of ports exhausted +uint TCPMaxPortsExhausted; //Max # of ports that must be exhausted + // for syn-att protection to kick in +uint TCPMaxPortsExhaustedLW; //Low-watermark of # of ports exhausted + //When reached, we revert to normal + // count for syn-ack retries +uint TCPMaxHalfOpen; //Max # of half-open connections allowed + // before we dec. the syn-ack retries +uint TCPMaxHalfOpenRetried; //Max # of half-open conn. that have + // been retried at least 1 time +uint TCPMaxHalfOpenRetriedLW; //Low-watermark of the above. When + // go down to it, we revert to normal + // # of retries for syn-acks +uint TCPHalfOpen; //# of half-open connections +uint TCPHalfOpenRetried; //# of half-open conn. that have been + //retried at least once +#endif +// +// External function prototypes +// + +int +tlinit( + void + ); + +NTSTATUS +TCPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TCPDispatchInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IPDriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ); + +PWCHAR +EnumRegMultiSz( + IN PWCHAR MszString, + IN ULONG MszStringLength, + IN ULONG StringIndex + ); + +// +// Local funcion prototypes +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +void * +TLRegisterProtocol( + uchar Protocol, + void *RcvHandler, + void *XmitHandler, + void *StatusHandler, + void *RcvCmpltHandler + ); + +IP_STATUS +TLGetIPInfo( + IPInfo *Buffer, + int Size + ); + +uchar +TCPGetConfigInfo( + void + ); + +NTSTATUS +TCPInitializeParameter( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG Value + ); + +#ifdef SECFLTR + +uint +EnumSecurityFilterValue( + PNDIS_STRING FilterList, + ulong Index, + ulong *FilterValue + ); + +#endif // SECFLTR + +#ifdef RASAUTODIAL +VOID +TCPAcdBind(); +#endif // RASAUTODIAL + + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, TLRegisterProtocol) +#pragma alloc_text(INIT, TLGetIPInfo) +#pragma alloc_text(INIT, TCPGetConfigInfo) +#pragma alloc_text(INIT, TCPInitializeParameter) + +#ifdef SECFLTR +#pragma alloc_text(PAGE, EnumSecurityFilterValue) +#endif // SECFLTR + +#ifdef RASAUTODIAL +#pragma alloc_text(INIT, TCPAcdBind) +#endif // RASAUTODIAL + +#endif // ALLOC_PRAGMA + + +// +// Function definitions +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + Initialization routine for the TCP/UDP driver. + +Arguments: + + DriverObject - Pointer to the TCP driver object created by the system. + DeviceDescription - The name of TCP's node in the registry. + +Return Value: + + The final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + USHORT i; + int initStatus; + + +#ifdef _PNP_POWER + TdiInitialize(); +#endif + +#ifdef SECFLTR + // + // IP calls the security filter code, so initialize it first. + // + InitializeSecurityFilters(); + +#endif // SECFLTR + + // + // Initialize IP + // + status = IPDriverEntry(DriverObject, RegistryPath); + + if (!NT_SUCCESS(status)) { + TCPTRACE(("Tcpip: IP initialization failed, status %lx\n", status)); + return(status); + } + + // + // Initialize TCP, UDP, and RawIP + // + TCPDriverObject = DriverObject; + + // + // Create the device objects. IoCreateDevice zeroes the memory + // occupied by the object. + // + + RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_NETWORK, + 0, + FALSE, + &TCPDeviceObject + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + DriverObject, + EVENT_TCPIP_CREATE_DEVICE_FAILED, + 1, + 1, + &deviceName.Buffer, + 0, + NULL + ); + + TCPTRACE(( + "TCP: Failed to create TCP device object, status %lx\n", + status + )); + goto init_failed; + } + + RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_NETWORK, + 0, + FALSE, + &UDPDeviceObject + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + DriverObject, + EVENT_TCPIP_CREATE_DEVICE_FAILED, + 1, + 1, + &deviceName.Buffer, + 0, + NULL + ); + + TCPTRACE(( + "TCP: Failed to create UDP device object, status %lx\n", + status + )); + goto init_failed; + } + + RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_NETWORK, + 0, + FALSE, + &RawIPDeviceObject + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + DriverObject, + EVENT_TCPIP_CREATE_DEVICE_FAILED, + 1, + 1, + &deviceName.Buffer, + 0, + NULL + ); + + TCPTRACE(( + "TCP: Failed to create Raw IP device object, status %lx\n", + status + )); + goto init_failed; + } + + // + // Initialize the driver object + // + DriverObject->DriverUnload = NULL; + DriverObject->FastIoDispatch = NULL; + for (i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + DriverObject->MajorFunction[i] = TCPDispatch; + } + + // + // We special case Internal Device Controls because they are the + // hot path for kernel-mode clients. + // + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + TCPDispatchInternalDeviceControl; + + // + // Intialize the device objects. + // + TCPDeviceObject->Flags |= DO_DIRECT_IO; + UDPDeviceObject->Flags |= DO_DIRECT_IO; + RawIPDeviceObject->Flags |= DO_DIRECT_IO; + + // + // Finally, initialize the stack. + // + initStatus = tlinit(); + + if (initStatus == TRUE) { +#ifdef RASAUTODIAL + // + // Get the automatic connection driver + // entry points. + // + TCPAcdBind(); +#endif // RASAUTODIAL + + +#ifdef _PNP_POWER + + RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME); + (void)TdiRegisterDeviceObject(&deviceName,&TCPRegistrationHandle); + + RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME); + (void)TdiRegisterDeviceObject(&deviceName,&UDPRegistrationHandle); + + RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME); + (void)TdiRegisterDeviceObject(&deviceName,&IPRegistrationHandle); + +#endif + return(STATUS_SUCCESS); + } + + TCPTRACE(( + "Tcpip: TCP/UDP initialization failed, but IP will be available.\n" + )); + + CTELogEvent( + DriverObject, + EVENT_TCPIP_TCP_INIT_FAILED, + 1, + 0, + NULL, + 0, + NULL + ); + status = STATUS_UNSUCCESSFUL; + + +init_failed: + + // + // IP has successfully started, but TCP & UDP failed. Set the + // Dispatch routine to point to IP only, since the TCP and UDP + // devices don't exist. + // + + if (TCPDeviceObject != NULL) { + IoDeleteDevice(TCPDeviceObject); + } + + if (UDPDeviceObject != NULL) { + IoDeleteDevice(UDPDeviceObject); + } + + if (RawIPDeviceObject != NULL) { + IoDeleteDevice(RawIPDeviceObject); + } + + for (i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + DriverObject->MajorFunction[i] = IPDispatch; + } + + return(STATUS_SUCCESS); +} + + +IP_STATUS +TLGetIPInfo( + IPInfo *Buffer, + int Size + ) + +/*++ + +Routine Description: + + Returns information necessary for TCP to call into IP. + +Arguments: + + Buffer - A pointer to the IP information structure. + + Size - The size of Buffer. + +Return Value: + + The IP status of the operation. + +--*/ + +{ + return(IPGetInfo(Buffer, Size)); +} + + +void * +TLRegisterProtocol( + uchar Protocol, + void *RcvHandler, + void *XmitHandler, + void *StatusHandler, + void *RcvCmpltHandler + ) + +/*++ + +Routine Description: + + Calls the IP driver's protocol registration function. + +Arguments: + + Protocol - The protocol number to register. + + RcvHandler - Transport's packet receive handler. + + XmitHandler - Transport's packet transmit complete handler. + + StatusHandler - Transport's status update handler. + + RcvCmpltHandler - Transport's receive complete handler + +Return Value: + + A context value for the protocol to pass to IP when transmitting. + +--*/ + +{ + return(IPRegisterProtocol( + Protocol, + RcvHandler, + XmitHandler, + StatusHandler, + RcvCmpltHandler + ) + ); +} + + +// +// Interval in milliseconds between keepalive transmissions until a +// response is received. +// +#define DEFAULT_KEEPALIVE_INTERVAL 1000 + +// +// time to first keepalive transmission. 2 hours == 7,200,000 milliseconds +// +#define DEFAULT_KEEPALIVE_TIME 7200000 + +#ifdef SYN_ATTACK +#define MIN_THRESHOLD_MAX_HO 100 +#define MIN_THRESHOLD_MAX_HO_RETRIED 80 +#endif + +uchar +TCPGetConfigInfo( + void + ) + +/*++ + +Routine Description: + + Initializes TCP global configuration parameters. + +Arguments: + + None. + +Return Value: + + Zero on failure, nonzero on success. + +--*/ + +{ + HANDLE keyHandle; + NTSTATUS status; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING UKeyName; + ULONG maxConnectRexmits = 0; + ULONG maxConnectResponseRexmits = 0; + ULONG maxDataRexmits = 0; + ULONG pptpmaxDataRexmits = 0; + ULONG useRFC1122UrgentPointer = 0; + BOOLEAN AsSystem; + + + // + // Initialize to the defaults in case an error occurs somewhere. + // + KAInterval = DEFAULT_KEEPALIVE_INTERVAL; + KeepAliveTime = DEFAULT_KEEPALIVE_TIME; + PMTUDiscovery = TRUE; + PMTUBHDetect = FALSE; + DeadGWDetect = TRUE; + DefaultRcvWin = 0; // Automagically pick a reasonable one. + MaxConnections = DEFAULT_MAX_CONNECTIONS; + maxConnectRexmits = MAX_CONNECT_REXMIT_CNT; + maxConnectResponseRexmits = MAX_CONNECT_RESPONSE_REXMIT_CNT; + pptpmaxDataRexmits = maxDataRexmits = MAX_REXMIT_CNT; + BSDUrgent = TRUE; + FinWait2TO = FIN_WAIT2_TO; + NTWMaxConnectCount = NTW_MAX_CONNECT_COUNT; + NTWMaxConnectTime = NTW_MAX_CONNECT_TIME; + MaxUserPort = MAX_USER_PORT; + + +#if FAST_RETRANSMIT +// Default number of duplicate acks + MaxDupAcks = 2; +#endif + + +#ifdef SYN_ATTACK + SynAttackProtect = FALSE; //by default it is always off + if (MmIsThisAnNtAsSystem()) { + TCPMaxPortsExhausted = 5; + TCPMaxHalfOpen = 100; + TCPMaxHalfOpenRetried = 80; + } + else { + TCPMaxPortsExhausted = 5; + TCPMaxHalfOpen = 500; + TCPMaxHalfOpenRetried = 400; + } +#endif + +#ifdef SECFLTR + SecurityFilteringEnabled = FALSE; +#endif // SECFLTR + + + // + // Read the TCP optional (hidden) registry parameters. + // + RtlInitUnicodeString( + &UKeyName, + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters" + ); + + memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes( + &objectAttributes, + &UKeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = ZwOpenKey( + &keyHandle, + KEY_READ, + &objectAttributes + ); + + if (NT_SUCCESS(status)) { + + TCPInitializeParameter( + keyHandle, + L"KeepAliveInterval", + &KAInterval + ); + + TCPInitializeParameter( + keyHandle, + L"KeepAliveTime", + &KeepAliveTime + ); + + TCPInitializeParameter( + keyHandle, + L"EnablePMTUBHDetect", + &PMTUBHDetect + ); + + TCPInitializeParameter( + keyHandle, + L"TcpWindowSize", + &DefaultRcvWin + ); + + TCPInitializeParameter( + keyHandle, + L"TcpNumConnections", + &MaxConnections + ); + + TCPInitializeParameter( + keyHandle, + L"TcpMaxConnectRetransmissions", + &maxConnectRexmits + ); + + if (maxConnectRexmits > 255) { + maxConnectRexmits = 255; + } + + TCPInitializeParameter( + keyHandle, + L"TcpMaxConnectResponseRetransmissions", + &maxConnectResponseRexmits + ); + + if (maxConnectResponseRexmits > 255) { + maxConnectResponseRexmits = 255; + } + + TCPInitializeParameter( + keyHandle, + L"TcpMaxDataRetransmissions", + &maxDataRexmits + ); + + if (maxDataRexmits > 255) { + maxDataRexmits = 255; + } + + +#if FAST_RETRANSMIT + // Limit the MaxDupAcks to 3 + + TCPInitializeParameter( + keyHandle, + L"TcpMaxDupAcks", + &MaxDupAcks + ); + + if (MaxDupAcks > 3) { + MaxDupAcks = 3; + } + if (MaxDupAcks < 0) { + MaxDupAcks = 1; + } +#endif + + + +#ifdef SYN_ATTACK + + TCPInitializeParameter( + keyHandle, + L"SynAttackProtect", + (unsigned long *)&SynAttackProtect + ); + + + if (SynAttackProtect) { + + // + // We don't want syn-attack protection to kick in if the user + // has set the MaxConnectResponseRetransmissions to lower than + // a certain threshold. + // + if (maxConnectResponseRexmits >= MAX_CONNECT_RESPONSE_REXMIT_CNT){ + + TCPInitializeParameter( + keyHandle, + L"TCPMaxPortsExhausted", + &TCPMaxPortsExhausted + ); + + TCPMaxPortsExhaustedLW = MAX((TCPMaxPortsExhausted >> 1) + (TCPMaxPortsExhausted >> 2), 1); + + TCPInitializeParameter( + keyHandle, + L"TCPMaxHalfOpen", + &TCPMaxHalfOpen + ); + + if (TCPMaxHalfOpen < MIN_THRESHOLD_MAX_HO) { + TCPMaxHalfOpen = MIN_THRESHOLD_MAX_HO; + } + + TCPInitializeParameter( + keyHandle, + L"TCPMaxHalfOpenRetried", + &TCPMaxHalfOpenRetried + ); + + if ( + (TCPMaxHalfOpenRetried > TCPMaxHalfOpen) || + (TCPMaxHalfOpenRetried < MIN_THRESHOLD_MAX_HO_RETRIED) + ) + + { + TCPMaxHalfOpenRetried = MIN_THRESHOLD_MAX_HO_RETRIED; + } + + TCPMaxHalfOpenRetriedLW = (TCPMaxHalfOpenRetried >> 1) + + (TCPMaxHalfOpenRetried >> 2); + } + else { + SynAttackProtect = FALSE; + } + + } +#endif + + // + // If we fail, then set to same value as maxDataRexmit so that the + // max(pptpmaxDataRexmit,maxDataRexmit) is a decent value + // Need this since TCPInitializeParameter no longer "initializes" + // to a default value + // + + if(TCPInitializeParameter(keyHandle, + L"PPTPTcpMaxDataRetransmissions", + &pptpmaxDataRexmits) != STATUS_SUCCESS) + { + pptpmaxDataRexmits = maxDataRexmits; + } + + if (pptpmaxDataRexmits > 255) { + pptpmaxDataRexmits = 255; + } + + TCPInitializeParameter( + keyHandle, + L"TcpUseRFC1122UrgentPointer", + &useRFC1122UrgentPointer + ); + + if (useRFC1122UrgentPointer) { + BSDUrgent = FALSE; + } + + TCPInitializeParameter( + keyHandle, + L"TcpTimedWaitDelay", + &FinWait2TO + ); + + if (FinWait2TO < 30) { + FinWait2TO = 30; + } + if (FinWait2TO > 300) { + FinWait2TO = 300; + } + FinWait2TO = MS_TO_TICKS(FinWait2TO*1000); + + NTWMaxConnectTime = MS_TO_TICKS(NTWMaxConnectTime*1000); + + TCPInitializeParameter( + keyHandle, + L"MaxUserPort", + &MaxUserPort + ); + + if (MaxUserPort < 5000) { + MaxUserPort = 5000; + } + if (MaxUserPort > 65534) { + MaxUserPort = 65534; + } + + // + // Read a few IP optional (hidden) registry parameters that TCP + // cares about. + // + TCPInitializeParameter( + keyHandle, + L"EnablePMTUDiscovery", + &PMTUDiscovery + ); + + TCPInitializeParameter( + keyHandle, + L"EnableDeadGWDetect", + &DeadGWDetect + ); + +#ifdef SECFLTR + TCPInitializeParameter( + keyHandle, + L"EnableSecurityFilters", + &SecurityFilteringEnabled + ); +#endif // SECFLTR + + ZwClose(keyHandle); + } + + MaxConnectRexmitCount = maxConnectRexmits; + MaxConnectResponseRexmitCount = maxConnectResponseRexmits; +#ifdef SYN_ATTACK + MaxConnectResponseRexmitCountTmp = MaxConnectResponseRexmitCount; +#endif + + // + // Use the greater of the two, hence both values should be valid + // + + MaxDataRexmitCount = (maxDataRexmits > pptpmaxDataRexmits ? maxDataRexmits : pptpmaxDataRexmits) ; + + return(1); +} + + +#define WORK_BUFFER_SIZE 256 + +NTSTATUS +TCPInitializeParameter( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG Value + ) + +/*++ + +Routine Description: + + Initializes a ULONG parameter from the registry or to a default + parameter if accessing the registry value fails. + +Arguments: + + KeyHandle - An open handle to the registry key for the parameter. + ValueName - The UNICODE name of the registry value to read. + Value - The ULONG into which to put the data. + DefaultValue - The default to assign if reading the registry fails. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_FULL_INFORMATION keyValueFullInformation; + UCHAR keybuf[WORK_BUFFER_SIZE]; + UNICODE_STRING UValueName; + + + RtlInitUnicodeString(&UValueName, ValueName); + + keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf; + RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation)); + + status = ZwQueryValueKey( + KeyHandle, + &UValueName, + KeyValueFullInformation, + keyValueFullInformation, + WORK_BUFFER_SIZE, + &resultLength + ); + + if (status == STATUS_SUCCESS) { + if (keyValueFullInformation->Type == REG_DWORD) { + *Value = *((ULONG UNALIGNED *) ((PCHAR)keyValueFullInformation + + keyValueFullInformation->DataOffset)); + } + } + + return(status); +} + + +#ifdef SECFLTR + +TDI_STATUS +GetSecurityFilterList( + NDIS_HANDLE ConfigHandle, + ulong Protocol, + PNDIS_STRING FilterList + ) +{ + PWCHAR parameterName; + TDI_STATUS status; + + + if (Protocol == PROTOCOL_TCP) { + parameterName = L"TcpAllowedPorts"; + } + else if (Protocol == PROTOCOL_UDP) { + parameterName = L"UdpAllowedPorts"; + } + else { + parameterName = L"RawIpAllowedProtocols"; + } + + status = GetRegMultiSZValue( + ConfigHandle, + parameterName, + FilterList + ); + + if (!NT_SUCCESS(status)) { + FilterList->Length = 0; + } + + return(status); +} + + +uint +EnumSecurityFilterValue( + PNDIS_STRING FilterList, + ulong Index, + ulong *FilterValue + ) +{ + PWCHAR valueString; + UNICODE_STRING unicodeString; + NTSTATUS status; + + + PAGED_CODE(); + + + valueString = EnumRegMultiSz( + FilterList->Buffer, + FilterList->Length, + Index + ); + + if ((valueString == NULL) || (valueString[0] == UNICODE_NULL)) { + return(FALSE); + } + + RtlInitUnicodeString(&unicodeString, valueString); + + status = RtlUnicodeStringToInteger(&unicodeString, 0, FilterValue); + + if (!(NT_SUCCESS(status))) { + TCPTRACE(("TCP: Invalid filter value %ws\n", valueString)); + return(FALSE); + } + + return(TRUE); +} + +#endif // SECFLTR diff --git a/private/ntos/tdi/tcpip/tcp/ppc/sources b/private/ntos/tdi/tcpip/tcp/ppc/sources new file mode 100644 index 000000000..3fd3f6b3b --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/ppc/sources @@ -0,0 +1,4 @@ +PPC_SOURCES= \ + ..\ppc\xsum.s + + diff --git a/private/ntos/tdi/tcpip/tcp/ppc/xsum.s b/private/ntos/tdi/tcpip/tcp/ppc/xsum.s new file mode 100644 index 000000000..831ef6b53 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/ppc/xsum.s @@ -0,0 +1,257 @@ +// TITLE("Compute Checksum") +//++ +// +// Copyright (c) 1994 IBM Corporation +// +// Module Name: +// +// xsum.s +// +// Abstract: +// +// This module implement a function to compute the checksum of a buffer. +// +// Author: +// +// David N. Cutler (davec) 27-Jan-1992 +// +// Environment: +// +// User mode. +// +// Revision History: +// +// Michael W. Thomas 02/14/94 Converted from MIPS +// Peter L. Johnston 07/19/94 Updated for Daytona Lvl 734 and +// optimized for PowerPC. +// +//-- + +#include "ksppc.h" + + SBTTL("Compute Checksum") +//++ +// +// ULONG +// tcpxsum ( +// IN ULONG Checksum, +// IN PUCHAR Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function computes the checksum of the specified buffer. +// +// N.B. The checksum is the 16 bit checksum of the 16 bit aligned +// buffer. If the buffer is not 16 bit aligned the first byte is +// moved to high order position to be added to the correct half. +// +// Arguments: +// +// Checksum (r3) - Supplies the initial checksum value. +// +// Source (r4) - Supplies a pointer to the checksum buffer. +// +// Length (r5) - Supplies the length of the buffer in bytes. +// +// Return Value: +// +// The computed checksum is returned as the function value. +// +//-- + + LEAF_ENTRY(tcpxsum) + + cmpwi r.5, 0 // check if bytes to checksum + mtcrf 0x01, r.4 // set up for alignment check + li r.6, 0 // initialize partial checksum + beqlr- // return if no bytes to checksum + + andi. r.7, r.5, 1 // check if length is even + crmove 7, 31 // remember original alignment + bf 31, evenalign // jif 16 bit aligned + +// +// Initialize the checksum to the first byte shifted up a byte. +// + lbz r.6, 0(r.4) // get first byte of buffer + subi r.5, r.5, 1 // reduce count of bytes to checksum + cmpwi cr.6, r.5, 0 // check if done + crnot eq, eq // invert odd/even length check + addi r.4, r.4, 1 // advance buffer address + mtcrf 0x01, r.4 // reset 32 bit alignment check + slwi r.6, r.6, 8 // shift byte up in computed checksum + // max current checksum is 0x0ff00 + beq cr.6, combine // jif no more bytes to checksum + +evenalign: + +// +// Check if the length of the buffer is an even number of bytes. +// +// If the buffer is not an even number of bytes, add the last byte to the +// computed checksum. +// + + beq evenlength + subic. r.5, r.5, 1 // reduce count of bytes to checksum + lbzx r.7, r.4, r.5 // get last byte from buffer + add r.6, r.6, r.7 // add last byte to computed checksum + // max current checksum is 0x0ffff + beq combine // jif no more bytes in buffer + +evenlength: + +// +// Check if we are 4 byte aligned, if not add first 2 byte word into +// checksum so the buffer is then 4 byte aligned. +// + + bf 30, fourbytealigned // jif 4 byte aligned + + lhz r.7, 0(r.4) // get 2 byte word + subic. r.5, r.5, 2 // reduce length + addi r.4, r.4, 2 // bump address + add r.6, r.6, r.7 // add 2 bytes to computed checksum + // max current checksum is 0x1fffe + beq combine // jif no more bytes to checksum + +// +// Attempt to sum the remainder of the buffer in sets of 32 bytes. This +// should achieve 2 bytes per clock on 601 and 603, and 3.2 bytes per clock +// on 604. (A seperate implementation will be required to take advantage +// of 64 bit loads on the 620). +// + +fourbytealigned: + + srwi. r.7, r.5, 5 // get count of 32 byte sets + mtcrf 0x03, r.5 // break length into block for + // various run lengths. + subi r.4, r.4, 4 // adjust buffer address for lwzu + mtctr r.7 + addic r.6, r.6, 0 // clear carry bit + beq try16 // jif no 32 byte sets + +do32: lwz r.8, 4(r.4) // get 1st 4 bytes in set + lwz r.9, 8(r.4) // get 2nd 4 + adde r.6, r.6, r.8 // add 1st 4 to checksum + lwz r.10, 12(r.4) // get 3rd 4 + adde r.6, r.6, r.9 // add 2nd 4 + lwz r.11, 16(r.4) // get 4th 4 + adde r.6, r.6, r.10 // add 3rd 4 + lwz r.8, 20(r.4) // get 5th 4 + adde r.6, r.6, r.11 // add 4th 4 + lwz r.9, 24(r.4) // get 6th 4 + adde r.6, r.6, r.8 // add 5th 4 + lwz r.10, 28(r.4) // get 7th 4 + adde r.6, r.6, r.9 // add 6th 4 + lwzu r.11, 32(r.4) // get 8th 4 and update address + adde r.6, r.6, r.10 // add 7th 4 + adde r.6, r.6, r.11 // add 8th 4 + bdnz do32 + +try16: bf 27, try8 // jif no 16 byte block + + lwz r.8, 4(r.4) // get 1st 4 + lwz r.9, 8(r.4) // get 2nd 4 + adde r.6, r.6, r.8 // add 1st 4 + lwz r.10, 12(r.4) // get 3rd 4 + adde r.6, r.6, r.9 // add 2nd 4 + lwzu r.11, 16(r.4) // get 4th 4 and update address + adde r.6, r.6, r.10 // add 3rd 4 + adde r.6, r.6, r.11 // add 4th 4 + +try8: bf 28, try4 // jif no 8 byte block + lwz r.8, 4(r.4) // get 1st 4 + lwzu r.9, 8(r.4) // get 2nd 4 and update address + adde r.6, r.6, r.8 // add 1st 4 + adde r.6, r.6, r.9 // add 2nd 4 + +try4: bf 29, try2 // jif no 4 byte block + lwzu r.8, 4(r.4) // get 4 bytes and update address + adde r.6, r.6, r.8 + +try2: bf 30, fold // jif no 2 byte block + +// +// At this point, r.4 is pointing at the last 4 byte block processed (or +// not processed if there were no 4 byte blocks). We need to add when we +// pull the last two bytes. +// + lhz r.8, 4(r.4) // get last two bytes + adde r.6, r.6, r.8 // add last two bytes + +// +// Collapse 33 bit (1 carry bit, 32 bits in r.6) into 17 bit checksum. +// + +fold: rlwinm r.7, r.6, 16, 0xffff // get 16 most significant bits (upper) + rlwinm r.6, r.6, 0, 0xffff // get least significant 16 bits (lower) + adde r.6, r.6, r.7 // upper + lower + carry + // max current checksum is 0x1ffff + +// +// Combine input checksum and partial checksum. +// +// If the input buffer was byte aligned, then word swap bytes in computed +// checksum before combination with input chewcksum. +// + +combine: + + bf 7, waseven // jif original alignment was 16 bit + +// +// Swap bytes within upper and lower halves. +// eg: AA BB CC DD becomes BB AA DD CC +// +// As the current maximum partial checksum is 0x1ffff don't worry about AA. +// ie: want BB 00 DD CC +// + + rlwimi r.6, r.6, 16, 0xff000000// r.7 = CC BB CC DD + rlwinm r.6, r.6, 8, 0xff00ffff// r.7 = BB 00 DD CC + +waseven: + + add r.3, r.3, r.6 // combine checksums + // max current checksum is 0x101fffe + rotlwi r.4, r.3, 16 // swap checksum words + add r.3, r.3, r.4 // add words with carry into high word + srwi r.3, r.3, 16 // extract final checksum + + LEAF_EXIT(tcpxsum) + + .debug$S + .ualong 1 + + .uashort 15 + .uashort 0x9 # S_OBJNAME + .ualong 0 + .byte 8, "xsum.obj" + + .uashort 24 + .uashort 0x1 # S_COMPILE + .byte 0x42 # Target processor = PPC 604 + .byte 3 # Language = ASM + .byte 0 + .byte 0 + .byte 17, "PowerPC Assembler" + + .uashort 43 + .uashort 0x205 # S_GPROC32 + .ualong 0 + .ualong 0 + .ualong 0 + .ualong tcpxsum.end-..tcpxsum + .ualong 0 + .ualong tcpxsum.end-..tcpxsum + .ualong [secoff]..tcpxsum + .uashort [secnum]..tcpxsum + .uashort 0x1000 + .byte 0x00 + .byte 7, "tcpxsum" + + .uashort 2, 0x6 # S_END diff --git a/private/ntos/tdi/tcpip/tcp/raw.c b/private/ntos/tdi/tcpip/tcp/raw.c new file mode 100644 index 000000000..6b59f9dd1 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/raw.c @@ -0,0 +1,693 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** RAW.C - Raw IP interface code. +// +// This file contains the code for the Raw IP interface functions, +// principally send and receive datagram. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" +#ifdef VXD +#include "tdivxd.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "raw.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + + +#define NO_TCP_DEFS 1 +#include "tcpdeb.h" + + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'rPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'rPCT') + +#endif // POOL_TAGGING + +#endif // NT + +EXTERNAL_LOCK(AddrObjTableLock) + +void *RawProtInfo = NULL; + +extern IPInfo LocalNetInfo; + +#ifdef CHICAGO +extern uchar TransportName[]; +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + + + +//** RawSend - Send a datagram. +// +// The real send datagram routine. We assume that the busy bit is +// set on the input AddrObj, and that the address of the SendReq +// has been verified. +// +// We start by sending the input datagram, and we loop until there's +// nothing left on the send q. +// +// Input: SrcAO - Pointer to AddrObj doing the send. +// SendReq - Pointer to sendreq describing send. +// +// Returns: Nothing +// +void +RawSend(AddrObj *SrcAO, DGSendReq *SendReq) +{ + PNDIS_BUFFER RawBuffer; + CTELockHandle HeaderHandle, AOHandle; + RouteCacheEntry *RCE; // RCE used for each send. + IPAddr SrcAddr; // Source address IP thinks we should + // use. + uchar DestType; // Type of destination address. + IP_STATUS SendStatus; // Status of send attempt. + ushort MSS; + uint AddrValid; + IPOptInfo *OptInfo; + IPAddr OrigSrc; + uchar protocol; + + + CTEStructAssert(SrcAO, ao); + CTEAssert(SrcAO->ao_usecnt != 0); + + protocol = SrcAO->ao_prot; + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(( + "RawSend called, prot %u\n", protocol + )); + } + + //* Loop while we have something to send, and can get + // resources to send. + for (;;) { + + CTEStructAssert(SendReq, dsr); + + // Make sure we have a Raw header buffer for this send. If we + // don't, try to get one. + if ((RawBuffer = SendReq->dsr_header) == NULL) { + // Don't have one, so try to get one. + CTEGetLock(&DGSendReqLock, &HeaderHandle); + RawBuffer = GetDGHeader(); + if (RawBuffer != NULL) + SendReq->dsr_header = RawBuffer; + else { + // Couldn't get a header buffer. Push the send request + // back on the queue, and queue the addr object for when + // we get resources. + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q); + PutPendingQ(SrcAO); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + return; + } + CTEFreeLock(&DGSendReqLock, HeaderHandle); + } + + // At this point, we have the buffer we need. Call IP to get an + // RCE (along with the source address if we need it), then + // send the data. + CTEAssert(RawBuffer != NULL); + + if (!CLASSD_ADDR(SendReq->dsr_addr)) { + // This isn't a multicast send, so we'll use the ordinary + // information. + OrigSrc = SrcAO->ao_addr; + OptInfo = &SrcAO->ao_opt; + } else { + OrigSrc = SrcAO->ao_mcastaddr; + OptInfo = &SrcAO->ao_mcastopt; + } + + CTEAssert(!(SrcAO->ao_flags & AO_DHCP_FLAG)); + + SrcAddr = (*LocalNetInfo.ipi_openrce)(SendReq->dsr_addr, + OrigSrc, &RCE, &DestType, &MSS, OptInfo); + + AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR); + + if (AddrValid) { + // The OpenRCE worked. Send it. + + if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR)) + SrcAddr = OrigSrc; + + NdisBufferLength(RawBuffer) = 0; + NDIS_BUFFER_LINKAGE(RawBuffer) = SendReq->dsr_buffer; + + // Now send the packet. + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("RawSend transmitting\n")); + } + + UStats.us_outdatagrams++; + SendStatus = (*LocalNetInfo.ipi_xmit)(RawProtInfo, SendReq, + RawBuffer, (uint)SendReq->dsr_size, SendReq->dsr_addr, SrcAddr, + OptInfo, RCE, protocol); + + (*LocalNetInfo.ipi_closerce)(RCE); + + // If it completed immediately, give it back to the user. + // Otherwise we'll complete it when the SendComplete happens. + // Currently, we don't map the error code from this call - we + // might need to in the future. + if (SendStatus != IP_PENDING) + DGSendComplete(SendReq, RawBuffer); + + } else { + TDI_STATUS Status; + + if (DestType == DEST_INVALID) + Status = TDI_BAD_ADDR; + else + Status = TDI_DEST_UNREACHABLE; + + // Complete the request with an error. + (*SendReq->dsr_rtn)(SendReq->dsr_context, Status, 0); + // Now free the request. + SendReq->dsr_rtn = NULL; + DGSendComplete(SendReq, RawBuffer); + } + + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + + if (!EMPTYQ(&SrcAO->ao_sendq)) { + DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + } else { + CLEAR_AO_REQUEST(SrcAO, AO_SEND); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + return; + } + + } +} + + +//* RawDeliver - Deliver a datagram to a user. +// +// This routine delivers a datagram to a Raw user. We're called with +// the AddrObj to deliver on, and with the AddrObjTable lock held. +// We try to find a receive on the specified AddrObj, and if we do +// we remove it and copy the data into the buffer. Otherwise we'll +// call the receive datagram event handler, if there is one. If that +// fails we'll discard the datagram. +// +// Input: RcvAO - AO to receive the datagram. +// SrcIP - Source IP address of datagram. +// IPH - IP Header +// IPHLength - Bytes in IPH. +// RcvBuf - The IPReceive buffer containing the data. +// RcvSize - Size received, including the Raw header. +// TableHandle - Lock handle for AddrObj table. +// +// Returns: Nothing. +// +void +RawDeliver(AddrObj *RcvAO, IPAddr SrcIP, IPHeader UNALIGNED *IPH, + uint IPHLength, IPRcvBuf *RcvBuf, uint RcvSize, IPOptInfo *OptInfo, + CTELockHandle TableHandle) +{ + Queue *CurrentQ; + CTELockHandle AOHandle; + DGRcvReq *RcvReq; + uint BytesTaken = 0; + uchar AddressBuffer[TCP_TA_SIZE]; + uint RcvdSize; + EventRcvBuffer *ERB = NULL; + + CTEStructAssert(RcvAO, ao); + + CTEGetLock(&RcvAO->ao_lock, &AOHandle); + CTEFreeLock(&AddrObjTableLock, AOHandle); + + if (AO_VALID(RcvAO)) { + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(( + "Raw delivering %u byte header + %u data bytes to AO %lx\n", + IPHLength, RcvSize, RcvAO + )); + } + + CurrentQ = QHEAD(&RcvAO->ao_rcvq); + + // Walk the list, looking for a receive buffer that matches. + while (CurrentQ != QEND(&RcvAO->ao_rcvq)) { + RcvReq = QSTRUCT(DGRcvReq, CurrentQ, drr_q); + + CTEStructAssert(RcvReq, drr); + + // If this request is a wildcard request, or matches the source IP + // address, deliver it. + + if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) { + + TDI_STATUS Status; + PNDIS_BUFFER DestBuf = RcvReq->drr_buffer; + uint DestOffset = 0; + + + // Remove this from the queue. + REMOVEQ(&RcvReq->drr_q); + + // We're done. We can free the AddrObj lock now. + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("Copying to posted receive\n")); + } + + // Copy the header + DestBuf = CopyFlatToNdis(DestBuf, (uchar *)IPH, IPHLength, + &DestOffset, &RcvdSize); + + // Copy the data and then complete the request. + RcvdSize += CopyRcvToNdis(RcvBuf, DestBuf, + RcvSize, 0, DestOffset); + + CTEAssert(RcvdSize <= RcvReq->drr_size); + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("Copied %u bytes\n", RcvdSize)); + } + + Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo, + SrcIP, 0); + + UStats.us_indatagrams++; + + (*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize); + + FreeDGRcvReq(RcvReq); + + return; + + } + + // Either the IP address or the port didn't match. Get the next + // one. + CurrentQ = QNEXT(CurrentQ); + } + + // We've walked the list, and not found a buffer. Call the recv. + // handler now. + + if (RcvAO->ao_rcvdg != NULL) { + PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg; + PVOID RcvContext = RcvAO->ao_rcvdgcontext; + TDI_STATUS RcvStatus; + CTELockHandle OldLevel; + uint IndicateSize; + uint DestOffset; + PNDIS_BUFFER DestBuf; + + + + REF_AO(RcvAO); + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + BuildTDIAddress(AddressBuffer, SrcIP, 0); + + IndicateSize = IPHLength; + + if (((uchar *)IPH + IPHLength) == RcvBuf->ipr_buffer) { + // + // The header is contiguous with the data + // + IndicateSize += RcvBuf->ipr_size; + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("RawRcv: header & data are contiguous\n")); + } + } + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("Indicating %u bytes\n", IndicateSize)); + } + + UStats.us_indatagrams++; + RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE, + (PTRANSPORT_ADDRESS)AddressBuffer, 0, + NULL, TDI_RECEIVE_COPY_LOOKAHEAD, + IndicateSize, + IPHLength + RcvSize, &BytesTaken, + (uchar *)IPH, &ERB); + + if (RcvStatus == TDI_MORE_PROCESSING) { + CTEAssert(ERB != NULL); + + // We were passed back a receive buffer. Copy the data in now. + + // He can't have taken more than was in the indicated + // buffer, but in debug builds we'll check to make sure. + + CTEAssert(BytesTaken <= RcvBuf->ipr_size); + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("ind took %u bytes\n", BytesTaken)); + } + +#ifdef NT + { + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation; + + IrpSp = IoGetCurrentIrpStackLocation(ERB); + DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) + &(IrpSp->Parameters); + + DestBuf = ERB->MdlAddress; +#else // NT + DestBuf = ERB->erb_buffer; +#endif // NT + DestOffset = 0; + + if (BytesTaken < IPHLength) { + + // Copy the rest of the IP header + DestBuf = CopyFlatToNdis( + DestBuf, + (uchar *)IPH + BytesTaken, + IPHLength - BytesTaken, + &DestOffset, + &RcvdSize + ); + + BytesTaken = 0; + } + else { + BytesTaken -= IPHLength; + RcvdSize = 0; + } + + // Copy the data + RcvdSize += CopyRcvToNdis( + RcvBuf, + DestBuf, + RcvSize - BytesTaken, + BytesTaken, + DestOffset + ); + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("Copied %u bytes\n", RcvdSize)); + } + +#ifdef NT + // + // Update the return address info + // + RcvStatus = UpdateConnInfo( + DatagramInformation->ReturnDatagramInformation, + OptInfo, SrcIP, 0); + + // + // Complete the IRP. + // + ERB->IoStatus.Information = RcvdSize; + ERB->IoStatus.Status = RcvStatus; + IoCompleteRequest(ERB, 2); + } + +#else // NT + // + // Call the completion routine. + // + (*ERB->erb_rtn)(ERB->erb_context, TDI_SUCCESS, RcvdSize); + +#endif // NT + + } + else { + CTEAssert( + (RcvStatus == TDI_SUCCESS) || + (RcvStatus == TDI_NOT_ACCEPTED) + ); + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(( + "Data %s taken\n", + (RcvStatus == TDI_SUCCESS) ? "all" : "not" + )); + } + + CTEAssert(ERB == NULL); + } + + DELAY_DEREF_AO(RcvAO); + + return; + + } else + UStats.us_inerrors++; + + // When we get here, we didn't have a buffer to put this data into. + // Fall through to the return case. + } else + UStats.us_inerrors++; + + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + +} + + +//* RawRcv - Receive a Raw datagram. +// +// The routine called by IP when a Raw datagram arrived. We +// look up the port/local address pair in our address table, +// and deliver the data to a user if we find one. For broadcast +// frames we may deliver it to multiple users. +// +// Entry: IPContext - IPContext identifying physical i/f that +// received the data. +// Dest - IPAddr of destionation. +// Src - IPAddr of source. +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the packet +// IPH - IP Header. +// IPHLength - Bytes in IPH. +// RcvBuf - Pointer to receive buffer chain containing data. +// Size - Size in bytes of data received. +// IsBCast - Boolean indicator of whether or not this came in as +// a bcast. +// Protocol - Protocol this came in on - should be Raw. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception. Anything other than IP_SUCCESS will cause +// IP to send a 'port unreachable' message. +// +IP_STATUS +RawRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf, + uint Size, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo) +{ + CTELockHandle AOTableHandle; + AddrObj *ReceiveingAO; + uchar DType; + AOSearchContext Search; + IP_STATUS Status = IP_DEST_PROT_UNREACHABLE; + + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("RawRcv prot %u size %u\n", IPH->iph_protocol, Size)); + } + + DType = (*LocalNetInfo.ipi_getaddrtype)(Src); + + // The following code relies on DEST_INVALID being a broadcast dest type. + // If this is changed the code here needs to change also. + if (IS_BCAST_DEST(DType)) { + if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) { + UStats.us_inerrors++; + return IP_SUCCESS; // Bad src address. + } + } + + // Get the AddrObjTable lock, and then try to find some AddrObj(s) to give + // this to. We deliver to all addr objs registered for the protocol and + // address. + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + +#ifdef SECFLTR + + if ( !SecurityFilteringEnabled || + IsPermittedSecurityFilter(SrcAddr, IPContext, PROTOCOL_RAW, Protocol) + ) + { + +#endif // SECFLTR + + ReceiveingAO = GetFirstAddrObj( + LocalAddr, + 0, // port is zero + Protocol, + &Search + ); + + if (ReceiveingAO != NULL) { + do { + RawDeliver( + ReceiveingAO, Src, IPH, IPHLength, RcvBuf, Size, + OptInfo, AOTableHandle + ); + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + ReceiveingAO = GetNextAddrObj(&Search); + } while (ReceiveingAO != NULL); + Status = IP_SUCCESS; + } else { + UStats.us_noports++; + } + +#ifdef SECFLTR + + } +#endif SECFLTR + + + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + return Status; +} + + +//* RawStatus - Handle a status indication. +// +// This is the Raw status handler, called by IP when a status event +// occurs. For most of these we do nothing. For certain severe status +// events we will mark the local address as invalid. +// +// Entry: StatusType - Type of status (NET or HW). NET status +// is usually caused by a received ICMP +// message. HW status indicate a HW +// problem. +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is NET status, the original dest. of +// DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local +// or remote). +// Param - Additional information for status - i.e. the +// param field of an ICMP message. +// Data - Data pertaining to status - for NET status, this +// is the first 8 bytes of the original DG. +// +// Returns: Nothing +// +void +RawStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) +{ + + IF_TCPDBG(TCP_DEBUG_RAW) { + TCPTRACE(("RawStatus called\n")); + } + + // If this is a HW status, it could be because we've had an address go + // away. + if (StatusType == IP_HW_STATUS) { + + if (StatusCode == IP_ADDR_DELETED) { + + // An address has gone away. OrigDest identifies the address. + +#ifndef _PNP_POWER + // + // This is done via TDI notifications in the PNP world. + // + InvalidateAddrs(OrigDest); + +#endif // _PNP_POWER + +#ifdef SECFLTR + // + // Delete any security filters associated with this address + // + DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_RAW); + +#endif // SECFLTR + + return; + } + + if (StatusCode == IP_ADDR_ADDED) { + +#ifdef SECFLTR + // + // An address has materialized. OrigDest identifies the address. + // Data is a handle to the IP configuration information for the + // interface on which the address is instantiated. + // + AddProtocolSecurityFilter(OrigDest, PROTOCOL_RAW, + (NDIS_HANDLE) Data); +#endif // SECFLTR + + return; + } + +#ifdef CHICAGO + if (StatusCode == IP_UNLOAD) { + // IP is telling us we're being unloaded. First, deregister + // with VTDI, and then call CTEUnload(). + (void)TLRegisterProtocol(PROTOCOL_ANY, NULL, NULL, NULL, NULL); + +#ifdef UDP_ONLY + // Only do the following in the UDP_ONLY version. TCP does it in + // the generic version. + TLRegisterDispatch(TransportName, NULL); + (void)RegisterAddrChangeHndlr(AddrChange, FALSE); + CTEUnload(TransportName); +#endif // UDP_ONLY + + return; + } +#endif // CHICAGO + } +} + + + diff --git a/private/ntos/tdi/tcpip/tcp/raw.h b/private/ntos/tdi/tcpip/tcp/raw.h new file mode 100644 index 000000000..8619dc30f --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/raw.h @@ -0,0 +1,34 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** RAW.H - Raw IP interface definitions. +// +// This file contains definitions for the Raw IP interface functions. +// + +#include "dgram.h" + + +// +// This value is used to identify the RAW transport for security filtering. +// It is out of the range of valid IP protocols. +// +#define PROTOCOL_RAW 255 + + +//* External definitions. +extern IP_STATUS RawRcv(void *IPContext, IPAddr Dest, IPAddr Src, + IPAddr LocalAddr, IPAddr SrcAddr, + IPHeader UNALIGNED *IPH, uint IPHLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, + uchar Protocol, IPOptInfo *OptInfo); + +extern void RawStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data); + +extern void RawSend(AddrObj *SrcAO, DGSendReq *SendReq); + + diff --git a/private/ntos/tdi/tcpip/tcp/secfltr.c b/private/ntos/tdi/tcpip/tcp/secfltr.c new file mode 100644 index 000000000..df64419e9 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/secfltr.c @@ -0,0 +1,1425 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +#ifdef SECFLTR + + +//** SECFLTR.C - Security Filter Support +// +// +// Security filters provide a mechanism by which the transport protocol +// traffic accepted on IP interfaces may be controlled. Security filtering +// is globally enabled or disabled for all IP interfaces and transports. +// If filtering is enabled, incoming traffic is filtered based on registered +// {interface, protocol, transport value} tuples. The tuples specify +// permissible traffic. All other values will be rejected. For UDP datagrams +// and TCP connections, the transport value is the port number. For RawIP +// datagrams, the transport value is the IP protocol number. An entry exists +// in the filter database for all active interfaces and protocols in the +// system. +// +// The initial status of security filtering - enabled or disabled, is +// controlled by the registry parameter +// +// Services\Tcpip\Parameters\EnableSecurityFilters +// +// If the parameter is not found, filtering is disabled. +// +// The list of permissible values for each protocol is stored in the registry +// under the \Parameters\Tcpip key in MULTI_SZ parameters. +// The parameter names are TCPAllowedPorts, UDPAllowedPorts and +// RawIPAllowedProtocols. If no parameter is found for a particular protocol, +// all values are permissible. If a parameter is found, the string identifies +// the permissible values. If the string is empty, no values are permissible. +// +// Filter Operation (Filtering Enabled): +// +// IF ( Match(interface, protocol) AND ( AllValuesPermitted(Protocol) OR +// Match(Value) )) +// THEN operation permitted. +// ELSE operation rejected. +// +// Database Implementation: +// +// The filter database is implemented as a three-level structure. The top +// level is a list of interface entries. Each interface entry points to +// a list of protocol entries. Each protocol entry contains a bucket hash +// table used to store transport value entries. +// + +// The following calls may be used to access the security filter database: +// +// InitializeSecurityFilters +// CleanupSecurityFilters +// IsSecurityFilteringEnabled +// ControlSecurityFiltering +// AddProtocolSecurityFilter +// DeleteProtocolSecurityFilter +// AddValueSecurityFilter +// DeleteValueSecurityFilter +// EnumerateSecurityFilters +// IsPermittedSecurityFilter +// + +#include "oscfg.h" +#include "queue.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" + +#ifdef NT + +#include "tdikrnl.h" +#include "tdint.h" +#include "tdistat.h" + +#endif // NT + +#include "addr.h" +#include "tlcommon.h" +#include "udp.h" +#include "tcp.h" +#include "raw.h" +#include "tcpcfg.h" +#include "tcpinfo.h" +#include "secfltr.h" + + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, InitializeSecurityFilters) + +#endif // ALLOC_PRAGMA +#endif // NT + +// +// The following routines must be supplied by each platform which implements +// security filters. +// +extern TDI_STATUS +GetSecurityFilterList( + IN NDIS_HANDLE ConfigHandle, + IN ulong Protocol, + IN OUT PNDIS_STRING FilterList + ); + +extern uint +EnumSecurityFilterValue( + IN PNDIS_STRING FilterList, + IN ulong Index, + OUT ulong *FilterValue + ); + +// +// Constants +// + +#define DHCP_CLIENT_PORT 68 + + +// +// Modification Opcodes +// +#define ADD_VALUE_SECURITY_FILTER 0 +#define DELETE_VALUE_SECURITY_FILTER 1 + + +// +// Types +// + +// +// Structure for a transport value entry. +// +struct value_entry { + struct Queue ve_link; + ulong ve_value; +}; + +typedef struct value_entry VALUE_ENTRY, *PVALUE_ENTRY; + + +#define VALUE_ENTRY_HASH_SIZE 16 +#define VALUE_ENTRY_HASH(value) (value % VALUE_ENTRY_HASH_SIZE) + +// +// Structure for a protocol entry. +// +struct protocol_entry { + struct Queue pe_link; + ulong pe_protocol; + ULONG pe_accept_all; // TRUE if all values are accepted. + struct Queue pe_entries[VALUE_ENTRY_HASH_SIZE]; +}; + +typedef struct protocol_entry PROTOCOL_ENTRY, *PPROTOCOL_ENTRY; + +// +// Structure for an interface entry. +// +struct interface_entry { + struct Queue ie_link; + IPAddr ie_address; + struct Queue ie_protocol_list; // list of protocols to filter +}; + +typedef struct interface_entry INTERFACE_ENTRY, *PINTERFACE_ENTRY; + + +// +// Global Data +// + +// +// This list of interface entries is the root of the filter database. +// +struct Queue InterfaceEntryList; + +// +// The filter operations are synchronized using the AddrObjTableLock. +// +EXTERNAL_LOCK(AddrObjTableLock) + +extern IPInfo LocalNetInfo; + + +// +// Filter Database Helper Functions +// + +//* FindInterfaceEntry - Search for an interface entry. +// +// This utility routine searches the security filter database +// for the specified interface entry. +// +// +// Input: InterfaceAddress - The address of the interface to search for. +// +// +// Returns: A pointer to the database entry for the Interface, +// or NULL if no match was found. +// +// +PINTERFACE_ENTRY +FindInterfaceEntry(ULONG InterfaceAddress) +{ + PINTERFACE_ENTRY ientry; + struct Queue *qentry; + + + for ( qentry = InterfaceEntryList.q_next; + qentry != &InterfaceEntryList; + qentry = qentry->q_next + ) + { + ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link); + + if (ientry->ie_address == InterfaceAddress) { + return(ientry); + } + } + + return(NULL); +} + + +//* FindProtocolEntry - Search for a protocol associated with an interface. +// +// This utility routine searches the security filter database +// for the specified protocol registered under the specified interface. +// +// +// Input: InterfaceEntry - A pointer to an interface entry to search under. +// Protocol - The protocol value to search for. +// +// +// Returns: A pointer to the database entry for the , +// or NULL if no match was found. +// +// +PPROTOCOL_ENTRY +FindProtocolEntry(PINTERFACE_ENTRY InterfaceEntry, ULONG Protocol) + +{ + PPROTOCOL_ENTRY pentry; + struct Queue *qentry; + + + for ( qentry = InterfaceEntry->ie_protocol_list.q_next; + qentry != &(InterfaceEntry->ie_protocol_list); + qentry = qentry->q_next + ) + { + pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link); + + if (pentry->pe_protocol == Protocol) { + return(pentry); + } + } + + return(NULL); +} + + +//* FindValueEntry - Search for a value on a particular protocol. +// +// This utility routine searches the security filter database +// for the specified value registered under the specified protocol. +// +// +// Input: ProtocolEntry - A pointer to the database structure for the +// Protocol to search. +// FilterValue - The value to search for. +// +// +// Returns: A pointer to the database entry for the , +// or NULL if no match was found. +// +// +PVALUE_ENTRY +FindValueEntry(PPROTOCOL_ENTRY ProtocolEntry, ULONG FilterValue) +{ + PVALUE_ENTRY ventry; + ulong hash_value = VALUE_ENTRY_HASH(FilterValue); + struct Queue *qentry; + + + for ( qentry = ProtocolEntry->pe_entries[hash_value].q_next; + qentry != &(ProtocolEntry->pe_entries[hash_value]); + qentry = qentry->q_next + ) + { + ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link); + + if (ventry->ve_value == FilterValue) { + return(ventry); + } + } + + return(NULL); +} + + +//* DeleteProtocolValueEntries +// +// This utility routine deletes all the value entries associated with +// a protocol filter entry. +// +// +// Input: ProtocolEntry - The protocol filter entry for which to +// delete the value entries. +// +// +// Returns: Nothing +// +void +DeleteProtocolValueEntries(PPROTOCOL_ENTRY ProtocolEntry) +{ + ulong i; + PVALUE_ENTRY entry; + + + for (i=0; i < VALUE_ENTRY_HASH_SIZE; i++) { + while (!EMPTYQ(&(ProtocolEntry->pe_entries[i]))) { + + DEQUEUE(&(ProtocolEntry->pe_entries[i]), entry, VALUE_ENTRY, ve_link); + CTEFreeMem(entry); + } + } + + return; +} + + +//* ModifyProtocolEntry +// +// This utility routine modifies one or more filter values associated +// with a protocol. +// +// +// Input: Operation - The operation to perform (add or delete) +// +// ProtocolEntry - A pointer to the protocol entry structure on +// which to operate. +// +// FilterValue - The value to add or delete. +// +// +// Returns: TDI_STATUS code +// +TDI_STATUS +ModifyProtocolEntry(ulong Operation, PPROTOCOL_ENTRY ProtocolEntry, + ulong FilterValue) +{ + TDI_STATUS status = TDI_SUCCESS; + + + if (FilterValue == 0) { + if (Operation == ADD_VALUE_SECURITY_FILTER) { + // + // Accept all values for the protocol + // + ProtocolEntry->pe_accept_all = TRUE; + } + else { + // + // Reject all values for the protocol + // + ProtocolEntry->pe_accept_all = FALSE; + } + + DeleteProtocolValueEntries(ProtocolEntry); + } + else { + PVALUE_ENTRY ventry; + ulong hash_value; + + // + // This request modifies an individual entry. + // + ventry = FindValueEntry(ProtocolEntry, FilterValue); + + if (Operation == ADD_VALUE_SECURITY_FILTER) { + + if (ventry == NULL) { + + ventry = CTEAllocMem(sizeof(VALUE_ENTRY)); + + if (ventry != NULL) { + ventry->ve_value = FilterValue; + hash_value = VALUE_ENTRY_HASH(FilterValue); + + ENQUEUE(&(ProtocolEntry->pe_entries[hash_value]), + &(ventry->ve_link)); + + ProtocolEntry->pe_accept_all = FALSE; + } + else { + status = TDI_NO_RESOURCES; + } + } + } + else { + if (ventry != NULL) { + REMOVEQ(&(ventry->ve_link)); + CTEFreeMem(ventry); + } + } + } + + return(status); +} + + +//* ModifyInterfaceEntry +// +// This utility routine modifies the value entries of one or more protocol +// entries associated with an interface. +// +// +// Input: Operation - The operation to perform (add or delete) +// +// ProtocolEntry - A pointer to the interface entry structure on +// which to operate. +// +// Protocol - The protocol on which to operate. +// +// FilterValue - The value to add or delete. +// +// +// Returns: TDI_STATUS code +// +TDI_STATUS +ModifyInterfaceEntry(ulong Operation, PINTERFACE_ENTRY InterfaceEntry, + ulong Protocol, ulong FilterValue) +{ + PPROTOCOL_ENTRY pentry; + TDI_STATUS status; + TDI_STATUS returnStatus = TDI_SUCCESS; + + + if (Protocol == 0) { + struct Queue *qentry; + + + // + // Modify all protocols on the interface + // + for ( qentry = InterfaceEntry->ie_protocol_list.q_next; + qentry != &(InterfaceEntry->ie_protocol_list); + qentry = qentry->q_next + ) + { + pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link); + status = ModifyProtocolEntry(Operation, pentry, FilterValue); + + if (status != TDI_SUCCESS) { + returnStatus = status; + } + } + } + else { + // + // Modify a specific protocol on the interface + // + pentry = FindProtocolEntry(InterfaceEntry, Protocol); + + if (pentry != NULL) { + returnStatus = ModifyProtocolEntry(Operation, pentry, FilterValue); + } + else { + returnStatus = TDI_INVALID_PARAMETER; + } + } + + return(returnStatus); +} + + +//* ModifySecurityFilter - Add or delete an entry. +// +// This routine adds or deletes an entry to/from the security filter database. +// +// +// Input: Operation - The operation to perform (Add or Delete) +// InterfaceAddress - The interface address to modify. +// Protocol - The protocol to modify. +// FilterValue - The transport value to add/delete. +// +// Returns: A TDI status code: +// TDI_INVALID_PARAMETER if the protocol is not in the database. +// TDI_ADDR_INVALID if the interface is not in the database. +// TDI_NO_RESOURCES if memory could not be allocated. +// TDI_SUCCESS otherwise +// +// NOTES: +// +TDI_STATUS +ModifySecurityFilter(ulong Operation, IPAddr InterfaceAddress, ulong Protocol, + ulong FilterValue) +{ + PINTERFACE_ENTRY ientry; + TDI_STATUS status; + TDI_STATUS returnStatus = TDI_SUCCESS; + + + if (InterfaceAddress == 0) { + struct Queue *qentry; + + // + // Modify on all interfaces + // + for ( qentry = InterfaceEntryList.q_next; + qentry != &InterfaceEntryList; + qentry = qentry->q_next + ) + { + ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link); + status = ModifyInterfaceEntry(Operation, ientry, Protocol, + FilterValue); + + if (status != TDI_SUCCESS) { + returnStatus = status; + } + } + } + else { + ientry = FindInterfaceEntry(InterfaceAddress); + + if (ientry != NULL) { + returnStatus = ModifyInterfaceEntry(Operation, ientry, Protocol, + FilterValue); + } + else { + returnStatus = TDI_ADDR_INVALID; + } + } + + return(returnStatus); +} + + +//* FillInEnumerationEntry +// +// This utility routine fills in an enumeration entry for a particular +// filter value entry. +// +// +// Input: InterfaceAddress - The address of the associated interface. +// +// Protocol - The associated protocol number. +// +// Value - The enumerated value. +// +// Buffer - Pointer to the user's enumeration buffer. +// +// BufferSize - Pointer to the size of the enumeration buffer. +// +// EntriesReturned - Pointer to a running count of enumerated +// entries stored in Buffer. +// +// EntriesAvailable - Pointer to a running count of entries available +// for enumeration. +// +// Returns: Nothing. +// +// Note: Values written to enumeration entry are in host byte order. +// +void +FillInEnumerationEntry(IPAddr InterfaceAddress, ulong Protocol, ulong Value, + uchar **Buffer, ulong *BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + TCPSecurityFilterEntry *entry = (TCPSecurityFilterEntry *) *Buffer; + + + if (*BufferSize >= sizeof(TCPSecurityFilterEntry)) { + entry->tsf_address = net_long(InterfaceAddress); + entry->tsf_protocol = Protocol; + entry->tsf_value = Value; + + *Buffer += sizeof(TCPSecurityFilterEntry); + *BufferSize -= sizeof(TCPSecurityFilterEntry); + (*EntriesReturned)++; + } + + (*EntriesAvailable)++; + + return; +} + + +//* EnumerateProtocolValues +// +// This utility routine enumerates values associated with a +// protocol on an interface. +// +// +// Input: InterfaceEntry - Pointer to the associated interface entry. +// +// ProtocolEntry - Pointer to the protocol being enumerated. +// +// Value - The value to enumerate. +// +// Buffer - Pointer to the user's enumeration buffer. +// +// BufferSize - Pointer to the size of the enumeration buffer. +// +// EntriesReturned - Pointer to a running count of enumerated +// entries stored in Buffer. +// +// EntriesAvailable - Pointer to a running count of entries available +// for enumeration. +// +// Returns: Nothing. +// +void +EnumerateProtocolValues(PINTERFACE_ENTRY InterfaceEntry, + PPROTOCOL_ENTRY ProtocolEntry, ulong Value, + uchar **Buffer, ulong *BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + struct Queue *qentry; + TDI_STATUS status = TDI_SUCCESS; + PVALUE_ENTRY ventry; + ulong i; + + + if (Value == 0) { + // + // Enumerate all values + // + if (ProtocolEntry->pe_accept_all == TRUE) { + // + // All values permitted. + // + FillInEnumerationEntry( + InterfaceEntry->ie_address, + ProtocolEntry->pe_protocol, + 0, + Buffer, + BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + else { + for (i=0; i < VALUE_ENTRY_HASH_SIZE; i++) { + for ( qentry = ProtocolEntry->pe_entries[i].q_next; + qentry != &(ProtocolEntry->pe_entries[i]); + qentry = qentry->q_next + ) + { + ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link); + + FillInEnumerationEntry( + InterfaceEntry->ie_address, + ProtocolEntry->pe_protocol, + ventry->ve_value, + Buffer, + BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + } + } + else { + // + // Enumerate a specific value, if it is registered. + // + ventry = FindValueEntry(ProtocolEntry, Value); + + if (ventry != NULL) { + FillInEnumerationEntry( + InterfaceEntry->ie_address, + ProtocolEntry->pe_protocol, + ventry->ve_value, + Buffer, + BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + + return; +} + + +//* EnumerateInterfaceProtocols +// +// This utility routine enumerates protocols associated with +// an interface. +// +// +// Input: InterfaceEntry - Pointer to the associated interface entry. +// +// Protocol - Protocol number to enumerate. +// +// Value - The filter value to enumerate. +// +// Buffer - Pointer to the user's enumeration buffer. +// +// BufferSize - Pointer to the size of the enumeration buffer. +// +// EntriesReturned - Pointer to a running count of enumerated +// entries stored in Buffer. +// +// EntriesAvailable - Pointer to a running count of entries available +// for enumeration. +// +// Returns: Nothing. +// +void +EnumerateInterfaceProtocols(PINTERFACE_ENTRY InterfaceEntry, ulong Protocol, + ulong Value, uchar **Buffer, ulong *BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + PPROTOCOL_ENTRY pentry; + + + if (Protocol == 0) { + struct Queue *qentry; + + // + // Enumerate all protocols. + // + for ( qentry = InterfaceEntry->ie_protocol_list.q_next; + qentry != &(InterfaceEntry->ie_protocol_list); + qentry = qentry->q_next + ) + { + pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link); + + EnumerateProtocolValues( + InterfaceEntry, + pentry, + Value, + Buffer, + BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + else { + // + // Enumerate a specific protocol + // + + pentry = FindProtocolEntry(InterfaceEntry, Protocol); + + if (pentry != NULL) { + EnumerateProtocolValues( + InterfaceEntry, + pentry, + Value, + Buffer, + BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + + return; +} + + +// +// Filter Database Public API. +// + +//* InitializeSecurityFilters - Initializes the security filter database. +// +// The routine performs the initialization necessary to enable the +// security filter database for operation. +// +// Input: None. +// +// Returns: Nothing. +// +// +void +InitializeSecurityFilters(void) +{ + INITQ(&InterfaceEntryList); + + return; +} + + +//* CleanupSecurityFilters - Deletes the entire security filter database. +// +// This routine deletes all entries from the security filter database. +// +// +// Input: None. +// +// Returns: Nothing. +// +// NOTE: This routine acquires the AddrObjTableLock. +// +// +void +CleanupSecurityFilters(void) +{ + PPROTOCOL_ENTRY pentry; + PINTERFACE_ENTRY ientry; + CTELockHandle handle; + + + CTEGetLock(&AddrObjTableLock, &handle); + + while (!EMPTYQ(&InterfaceEntryList)) { + DEQUEUE(&InterfaceEntryList, ientry, INTERFACE_ENTRY, ie_link); + + while (!EMPTYQ(&(ientry->ie_protocol_list))) { + DEQUEUE(&(ientry->ie_protocol_list), pentry, PROTOCOL_ENTRY, + pe_link); + + DeleteProtocolValueEntries(pentry); + + CTEFreeMem(pentry); + } + + CTEFreeMem(ientry); + } + + SecurityFilteringEnabled = FALSE; + + CTEFreeLock(&AddrObjTableLock, handle); + + return; +} + + +//* IsSecurityFilteringEnabled +// +// This routine returns the current global status of security filtering. +// +// Entry: Nothing +// +// Returns: 0 if filtering is disabled, !0 if filtering is enabled. +// +extern uint +IsSecurityFilteringEnabled(void) +{ + uint enabled; + CTELockHandle handle; + + + CTEGetLock(&AddrObjTableLock, &handle); + + enabled = SecurityFilteringEnabled; + + CTEFreeLock(&AddrObjTableLock, handle); + + return(enabled); +} + + +//* ControlSecurityFiltering +// +// This routine globally enables/disables security filtering. +// +// Entry: IsEnabled - 0 disabled filtering, !0 enables filtering. +// +// Returns: Nothing +// +extern void +ControlSecurityFiltering(uint IsEnabled) +{ + CTELockHandle handle; + + + CTEGetLock(&AddrObjTableLock, &handle); + + if (IsEnabled) { + SecurityFilteringEnabled = TRUE; + } + else { + SecurityFilteringEnabled = FALSE; + } + + CTEFreeLock(&AddrObjTableLock, handle); + + return; +} + + +//* AddProtocolSecurityFilter +// +// This routine enables security filtering for a specified protocol +// on a specified IP interface. +// +// Entry: InterfaceAddress - The interface on which to enable the protocol. +// (in network byte order) +// Protocol - The protocol to enable. +// ConfigName - The configuration key from which to read +// the filter value information. +// +// Returns: Nothing +// +void +AddProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, + NDIS_HANDLE ConfigHandle) +{ + NDIS_STRING filterList; + ulong filterValue; + ulong i; + PINTERFACE_ENTRY ientry; + PPROTOCOL_ENTRY pentry; + PVOID temp; + CTELockHandle handle; + TDI_STATUS status; + + + if ( IP_ADDR_EQUAL(InterfaceAddress, NULL_IP_ADDR) || + IP_LOOPBACK_ADDR(InterfaceAddress) + ) + { + return; + } + + CTEAssert( (Protocol != 0) && (Protocol <= 0xFF) ); + + // + // Read the protocol-specific filter value list from the registry. + // + filterList.MaximumLength = filterList.Length = 0; + filterList.Buffer = NULL; + + if (ConfigHandle != NULL) { + status = GetSecurityFilterList(ConfigHandle, Protocol, &filterList); + } + + // + // Preallocate interface & protocol structures. We abort on failure. + // The interface & protocol will be protected by default. + // + ientry = CTEAllocMem(sizeof(INTERFACE_ENTRY)); + + if (ientry == NULL) { + return; + } + + ientry->ie_address = InterfaceAddress; + INITQ(&(ientry->ie_protocol_list)); + + pentry = CTEAllocMem(sizeof(PROTOCOL_ENTRY)); + + if (pentry == NULL) { + CTEFreeMem(ientry); + return; + } + + pentry->pe_protocol = Protocol; + pentry->pe_accept_all = FALSE; + + for (i=0; ipe_entries[i])); + } + + // + // Now go set everything up. First create the interface and protocol + // structures. + // + CTEGetLock(&AddrObjTableLock, &handle); + + temp = FindInterfaceEntry(InterfaceAddress); + + if (temp == NULL) { + // + // New interface & protocol. + // + ENQUEUE(&InterfaceEntryList, &(ientry->ie_link)); + ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link)); + } + else { + // + // Existing interface + // + CTEFreeMem(ientry); + ientry = temp; + + temp = FindProtocolEntry(ientry, Protocol); + + if (temp == NULL) { + // + // New protocol + // + ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link)); + } + else { + // + // Existing protocol + // + CTEFreeMem(pentry); + } + } + + CTEFreeLock(&AddrObjTableLock, handle); + + // + // At this point, the protocol entry is installed, but no values + // are permitted. This is the safest default. + // + + if (ConfigHandle != NULL) { + // + // Process the filter value list. + // + if (status == TDI_SUCCESS) { + for ( i=0; + EnumSecurityFilterValue(&filterList, i, &filterValue); + i++ + ) + { + AddValueSecurityFilter(InterfaceAddress, Protocol, + filterValue); + } + } + else if (status == TDI_ITEM_NOT_FOUND) { + // + // No filter registered, permit everything. + // + AddValueSecurityFilter(InterfaceAddress, Protocol, 0); + } + } + + if (filterList.Buffer != NULL) { + CTEFreeMem(filterList.Buffer); + } + + return; +} + + +//* DeleteProtocolSecurityFilter +// +// This routine disables security filtering for a specified protocol +// on a specified IP interface. +// +// Entry: InterfaceAddress - The interface on which to disable the protocol. +// (in network byte order) +// Protocol - The protocol to disable. +// +// Returns: Nothing +// +void +DeleteProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol) +{ + PINTERFACE_ENTRY ientry; + PPROTOCOL_ENTRY pentry; + CTELockHandle handle; + BOOLEAN deleteInterface = FALSE; + + + CTEGetLock(&AddrObjTableLock, &handle); + + ientry = FindInterfaceEntry(InterfaceAddress); + + if (ientry != NULL) { + + CTEAssert(!EMPTYQ(&(ientry->ie_protocol_list))); + + pentry = FindProtocolEntry(ientry, Protocol); + + if (pentry != NULL) { + REMOVEQ(&(pentry->pe_link)); + } + + if (EMPTYQ(&(ientry->ie_protocol_list))) { + // + // Last protocol, delete interface as well. + // + REMOVEQ(&(ientry->ie_link)); + deleteInterface = TRUE; + } + + CTEFreeLock(&AddrObjTableLock, handle); + + DeleteProtocolValueEntries(pentry); + CTEFreeMem(pentry); + + if (deleteInterface) { + CTEAssert(EMPTYQ(&(ientry->ie_protocol_list))); + CTEFreeMem(ientry); + } + } + else { + CTEFreeLock(&AddrObjTableLock, handle); + } + + return; +} + + +//* AddValueSecurityFilter - Add an entry. +// +// This routine adds a value entry for a specified protocol on a specified +// interface in the security filter database. +// +// +// Input: InterfaceAddress - The interface address to which to add. +// (in network byte order) +// Protocol - The protocol to which to add. +// FilterValue - The transport value to add. +// (in host byte order) +// +// Returns: A TDI status code: +// TDI_INVALID_PARAMETER if the protocol is not in the database. +// TDI_ADDR_INVALID if the interface is not in the database. +// TDI_NO_RESOURCES if memory could not be allocated. +// TDI_SUCCESS otherwise +// +// NOTES: +// +// This routine acquires AddrObjTableLock. +// +// Zero is a wildcard value. Supplying a zero value for the +// InterfaceAddress and/or Protocol causes the operation to be applied +// to all interfaces and/or protocols, as appropriate. Supplying a +// non-zero value causes the operation to be applied to only the +// specified interface and/or protocol. Supplying a FilterValue parameter +// of zero causes all values to be acceptable. Any previously +// registered values are deleted from the database. +// +TDI_STATUS +AddValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, ulong FilterValue) +{ + CTELockHandle handle; + TDI_STATUS status; + + + CTEGetLock(&AddrObjTableLock, &handle); + + status = ModifySecurityFilter(ADD_VALUE_SECURITY_FILTER, InterfaceAddress, + Protocol, FilterValue); + + CTEFreeLock(&AddrObjTableLock, handle); + + return(status); +} + + +//* DeleteValueSecurityFilter - Delete an entry. +// +// This routine deletes a value entry for a specified protocol on a specified +// interface in the security filter database. +// +// +// Input: InterfaceAddress - The interface address from which to delete. +// (in network byte order) +// Protocol - The protocol from which to delete. +// FilterValue - The transport value to delete. +// (in host byte order) +// +// Returns: A TDI status code: +// TDI_INVALID_PARAMETER if the protocol is not in the database. +// TDI_ADDR_INVALID if the interface is not in the database. +// TDI_NO_RESOURCES if memory could not be allocated. +// TDI_SUCCESS otherwise +// +// NOTES: +// +// This routine acquires AddrObjTableLock. +// +// Zero is a wildcard value. Supplying a zero value for the +// InterfaceAddress and/or Protocol causes the operation to be applied +// to all interfaces and/or protocols, as appropriate. Supplying a +// non-zero value causes the operation to be applied to only the +// specified interface and/or protocol. Supplying a FilterValue parameter +// of zero causes all values to be rejected. Any previously +// registered values are deleted from the database. +// +TDI_STATUS +DeleteValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, + ulong FilterValue) +{ + CTELockHandle handle; + TDI_STATUS status; + + + CTEGetLock(&AddrObjTableLock, &handle); + + status = ModifySecurityFilter(DELETE_VALUE_SECURITY_FILTER, + InterfaceAddress, Protocol, FilterValue); + + CTEFreeLock(&AddrObjTableLock, handle); + + return(status); +} + + +//* EnumerateSecurityFilters - Enumerate security filter database. +// +// This routine enumerates the contents of the security filter database +// for the specified protocol and IP interface. +// +// Input: InterfaceAddress - The interface address to enumerate. A value +// of zero means enumerate all interfaces. +// (in network byte order) +// +// Protocol - The protocol to enumerate. A value of zero +// means enumerate all protocols. +// +// Value - The Protocol value to enumerate. A value of +// zero means enumerate all protocol values. +// (in host byte order) +// +// Buffer - A pointer to a buffer into which to put +// the returned filter entries. +// +// BufferSize - On input, the size in bytes of Buffer. +// On output, the number of bytes written. +// +// EntriesAvailable - On output, the total number of filter entries +// available in the database. +// +// Returns: A TDI status code: +// +// TDI_ADDR_INVALID if the address is not a valid IP interface. +// TDI_SUCCESS otherwise. +// +// NOTES: +// +// This routine acquires AddrObjTableLock. +// +// Entries written to output buffer are in host byte order. +// +void +EnumerateSecurityFilters(IPAddr InterfaceAddress, ulong Protocol, + ulong Value, uchar *Buffer, ulong BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + PINTERFACE_ENTRY ientry; + TDI_STATUS status = TDI_SUCCESS; + CTELockHandle handle; + + + *EntriesAvailable = *EntriesReturned = 0; + + CTEGetLock(&AddrObjTableLock, &handle); + + if (InterfaceAddress == 0) { + struct Queue *qentry; + + // + // Enumerate all interfaces. + // + for ( qentry = InterfaceEntryList.q_next; + qentry != &InterfaceEntryList; + qentry = qentry->q_next + ) + { + ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link); + + EnumerateInterfaceProtocols( + ientry, + Protocol, + Value, + &Buffer, + &BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + else { + // + // Enumerate a specific interface. + // + + ientry = FindInterfaceEntry(InterfaceAddress); + + if (ientry != NULL) { + EnumerateInterfaceProtocols( + ientry, + Protocol, + Value, + &Buffer, + &BufferSize, + EntriesReturned, + EntriesAvailable + ); + } + } + + CTEFreeLock(&AddrObjTableLock, handle); + + return; +} + + +//* IsPermittedSecurityFilter +// +// This routine determines if communications addressed to +// {Protocol, InterfaceAddress, Value} are permitted by the security filters. +// It looks up the tuple in the security filter database. +// +// Input: InterfaceAddress - The IP interface address to check +// (in network byte order) +// IPContext - The IPContext value passed to the transport +// Protocol - The protocol to check +// Value - The value to check (in host byte order) +// +// Returns: A boolean indicating whether or not the communication is permitted. +// +// NOTES: +// +// This routine must be called with AddrObjTableLock held. +// +// +BOOLEAN +IsPermittedSecurityFilter(IPAddr InterfaceAddress, void *IPContext, + ulong Protocol, ulong FilterValue) +{ + PINTERFACE_ENTRY ientry; + PPROTOCOL_ENTRY pentry; + PVALUE_ENTRY ventry; + ulong hash_value; + struct Queue *qentry; + + + CTEAssert(Protocol <= 0xFF); + + for ( qentry = InterfaceEntryList.q_next; + qentry != &InterfaceEntryList; + qentry = qentry->q_next + ) + { + ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link); + + if (ientry->ie_address == InterfaceAddress) { + + for ( qentry = ientry->ie_protocol_list.q_next; + qentry != &(ientry->ie_protocol_list); + qentry = qentry->q_next + ) + { + pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link); + + if (pentry->pe_protocol == Protocol) { + + if (pentry->pe_accept_all == TRUE) { + // + // All values accepted. Permit operation. + // + return(TRUE); + } + + hash_value = VALUE_ENTRY_HASH(FilterValue); + + for ( qentry = pentry->pe_entries[hash_value].q_next; + qentry != &(pentry->pe_entries[hash_value]); + qentry = qentry->q_next + ) + { + ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link); + + if (ventry->ve_value == FilterValue) { + // + // Found it. Operation is permitted. + // + return(TRUE); + } + } + + // + // {Interface, Protocol} protected, but no value found. + // Reject operation. + // + return(FALSE); + } + } + + // + // Protocol not registered. Reject operation + // + return(FALSE); + } + } + + // + // If this packet is on the loopback interface, let it through. + // + if (IP_LOOPBACK_ADDR(InterfaceAddress)) { + return(TRUE); + } + + // + // Special check to allow the DHCP client to receive its packets. + // It is safe to make this check all the time because IP will + // not permit a packet to get through on an NTE with a zero address + // unless DHCP is in the process of configuring that NTE. + // + if ( (Protocol == PROTOCOL_UDP) && + (FilterValue == DHCP_CLIENT_PORT) && + (*LocalNetInfo.ipi_isdhcpinterface)(IPContext) + ) + { + return(TRUE); + } + + // + // Interface not registered. Deny operation. + // + return(FALSE); +} + + +#endif // SECFLTR + diff --git a/private/ntos/tdi/tcpip/tcp/secfltr.h b/private/ntos/tdi/tcpip/tcp/secfltr.h new file mode 100644 index 000000000..ef05de497 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/secfltr.h @@ -0,0 +1,61 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +#ifndef _SECFLTR_INCLUDED +#define _SECFLTR_INCLUDED 1 + +#ifdef SECFLTR + + +//** SECFLTR.H - Security filtering support +// +// This file contains definitions related to security filtering. +// + +// +// Functions +// +extern void +InitializeSecurityFilters(void); + +extern void +CleanupSecurityFilters(void); + +extern uint +IsSecurityFilteringEnabled(void); + +extern void +ControlSecurityFiltering(uint IsEnabled); + +extern void +AddProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, + NDIS_HANDLE ConfigHandle); + +extern void +DeleteProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol); + +extern TDI_STATUS +AddValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, + ulong FilterValue); + +extern TDI_STATUS +DeleteValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, + ulong FilterValue); + +extern void +EnumerateSecurityFilters(IPAddr InterfaceAddress, ulong Protocol, + ulong Value, uchar *Buffer, ulong BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable); + +extern BOOLEAN +IsPermittedSecurityFilter(IPAddr InterfaceAddress, void *IPContext, + ulong Protocol, ulong FilterValue); + + +#endif // SECFLTR + +#endif // _SECFLTR_INCLUDED + diff --git a/private/ntos/tdi/tcpip/tcp/sources.inc b/private/ntos/tdi/tcpip/tcp/sources.inc new file mode 100644 index 000000000..ad8aeff0f --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/sources.inc @@ -0,0 +1,67 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=tcp + +NTPROFILEINPUT=yes + +TARGETNAME=tcpip +TARGETTYPE=EXPORT_DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib \ + $(TARGETLIBS) + +INCLUDES=..\..\..\..\inc;..\..\..\..\..\inc;..\..\h + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER -DSECFLTR -DFAST_RETRANSMIT=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\tcpip.rc \ + ..\addr.c \ + ..\dgram.c \ + ..\info.c \ + ..\init.c \ + ..\ntdisp.c \ + ..\ntinit.c \ + ..\ntautodl.c \ + ..\raw.c \ + ..\secfltr.c \ + ..\tcb.c \ + ..\tcpconn.c \ + ..\tcpdeb.c \ + ..\tcpdeliv.c \ + ..\tcprcv.c \ + ..\tcpsend.c \ + ..\tlcommon.c \ + ..\udp.c + +DLLDEF=..\tcpip.def diff --git a/private/ntos/tdi/tcpip/tcp/tcb.c b/private/ntos/tdi/tcpip/tcp/tcb.c new file mode 100644 index 000000000..4b01c66d3 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcb.c @@ -0,0 +1,1524 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCB.C - TCP TCB management code. +// +// This file contains the code for managing TCBs. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "info.h" +#include "tcpcfg.h" +#include "tcpdeliv.h" +#ifdef RASAUTODIAL +#include +#include +#endif // RASAUTODIAL + +DEFINE_LOCK_STRUCTURE(TCBTableLock) + +uint TCPTime; +uint TCBWalkCount; + +TCB *TCBTable[TCB_TABLE_SIZE]; + +TCB *LastTCB; + +TCB *PendingFreeList; + +#ifndef NT +TCB *FreeTCBList = NULL; // TCB free list +#else +SLIST_HEADER FreeTCBList; +#endif + +DEFINE_LOCK_STRUCTURE(FreeTCBListLock) // Lock to protect TCB free list. + +EXTERNAL_LOCK(AddrObjTableLock) + +uint CurrentTCBs = 0; +uint MaxTCBs = 0xffffffff; + +#ifdef NT +#define MAX_FREE_TCBS 1000 +#else +#define MAX_FREE_TCBS 10 +#endif + +#define NUM_DEADMAN_TICKS MS_TO_TICKS(1000) + +uint MaxFreeTCBs = MAX_FREE_TCBS; +uint DeadmanTicks; + +CTETimer TCBTimer; + +extern IPInfo LocalNetInfo; + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitTCB(void); +void UnInitTCB(void); + +#pragma alloc_text(INIT, InitTCB) +#pragma alloc_text(INIT, UnInitTCB) + +#endif // ALLOC_PRAGMA +#endif + +#ifdef RASAUTODIAL +extern ACD_DRIVER AcdDriverG; + +VOID +TCPNoteNewConnection( + IN TCB *pTCB, + IN CTELockHandle Handle + ); +#endif // RASAUTODIAL + + +//* ReadNextTCB - Read the next TCB in the table. +// +// Called to read the next TCB in the table. The needed information +// is derived from the incoming context, which is assumed to be valid. +// We'll copy the information, and then update the context value with +// the next TCB to be read. +// +// Input: Context - Poiner to a TCPConnContext. +// Buffer - Pointer to a TCPConnTableEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +ReadNextTCB(void *Context, void *Buffer) +{ + TCPConnContext *TCContext = (TCPConnContext *)Context; + TCPConnTableEntry *TCEntry = (TCPConnTableEntry *)Buffer; + CTELockHandle Handle; + TCB *CurrentTCB; + uint i; + + + CurrentTCB = TCContext->tcc_tcb; + CTEStructAssert(CurrentTCB, tcb); + + CTEGetLock(&CurrentTCB->tcb_lock, &Handle); + if (CLOSING(CurrentTCB)) + TCEntry->tct_state = TCP_CONN_CLOSED; + else + TCEntry->tct_state = (uint)CurrentTCB->tcb_state + + TCB_STATE_DELTA; + TCEntry->tct_localaddr = CurrentTCB->tcb_saddr; + TCEntry->tct_localport = CurrentTCB->tcb_sport; + TCEntry->tct_remoteaddr = CurrentTCB->tcb_daddr; + TCEntry->tct_remoteport = CurrentTCB->tcb_dport; + CTEFreeLock(&CurrentTCB->tcb_lock, Handle); + + // We've filled it in. Now update the context. + if (CurrentTCB->tcb_next != NULL) { + TCContext->tcc_tcb = CurrentTCB->tcb_next; + return TRUE; + } else { + // NextTCB is NULL. Loop through the TCBTable looking for a new + // one. + i = TCContext->tcc_index + 1; + while (i < TCB_TABLE_SIZE) { + if (TCBTable[i] != NULL) { + TCContext->tcc_tcb = TCBTable[i]; + TCContext->tcc_index = i; + return TRUE; + break; + } else + i++; + } + + TCContext->tcc_index = 0; + TCContext->tcc_tcb = NULL; + return FALSE; + } + +} + +//* ValidateTCBContext - Validate the context for reading a TCB table. +// +// Called to start reading the TCB table sequentially. We take in +// a context, and if the values are 0 we return information about the +// first TCB in the table. Otherwise we make sure that the context value +// is valid, and if it is we return TRUE. +// We assume the caller holds the TCB table lock. +// +// Input: Context - Pointer to a TCPConnContext. +// Valid - Where to return information about context being +// valid. +// +// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the +// context is valid. +// +uint +ValidateTCBContext(void *Context, uint *Valid) +{ + TCPConnContext *TCContext = (TCPConnContext *)Context; + uint i; + TCB *TargetTCB; + TCB *CurrentTCB; + + i = TCContext->tcc_index; + TargetTCB = TCContext->tcc_tcb; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetTCB == NULL) { + *Valid = TRUE; + do { + if ((CurrentTCB = TCBTable[i]) != NULL) { + CTEStructAssert(CurrentTCB, tcb); + break; + } + i++; + } while (i < TCB_TABLE_SIZE); + + if (CurrentTCB != NULL) { + TCContext->tcc_index = i; + TCContext->tcc_tcb = CurrentTCB; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < TCB_TABLE_SIZE) { + CurrentTCB = TCBTable[i]; + while (CurrentTCB != NULL) { + if (CurrentTCB == TargetTCB) { + *Valid = TRUE; + return TRUE; + break; + } else { + CurrentTCB = CurrentTCB->tcb_next; + } + } + + } + + // If we get here, we didn't find the matching TCB. + *Valid = FALSE; + return FALSE; + + } + +} + +//* FindNextTCB - Find the next TCB in a particular chain. +// +// This routine is used to find the 'next' TCB in a chain. Since we keep +// the chain in ascending order, we look for a TCB which is greater than +// the input TCB. When we find one, we return it. +// +// This routine is mostly used when someone is walking the table and needs +// to free the various locks to perform some action. +// +// Input: Index - Index into TCBTable +// Current - Current TCB - we find the one after this one. +// +// Returns: Pointer to the next TCB, or NULL. +// +TCB * +FindNextTCB(uint Index, TCB *Current) +{ + TCB *Next; + + CTEAssert(Index < TCB_TABLE_SIZE); + + Next = TCBTable[Index]; + + while (Next != NULL && (Next <= Current)) + Next = Next->tcb_next; + + return Next; +} + +//* ResetSendNext - Set the sendnext value of a TCB. +// +// Called to set the send next value of a TCB. We do that, and adjust all +// pointers to the appropriate places. We assume the caller holds the lock +// on the TCB. +// +// Input: SeqTCB - Pointer to TCB to be updated. +// NewSeq - Sequence number to set. +// +// Returns: Nothing. +// +void +ResetSendNext(TCB *SeqTCB, SeqNum NewSeq) +{ + TCPSendReq *SendReq; + uint AmtForward; + Queue *CurQ; + PNDIS_BUFFER Buffer; + uint Offset; + + CTEStructAssert(SeqTCB, tcb); + CTEAssert(SEQ_GTE(NewSeq, SeqTCB->tcb_senduna)); + + // The new seq must be less than send max, or NewSeq, senduna, sendnext, + // and sendmax must all be equal. (The latter case happens when we're + // called exiting TIME_WAIT, or possibly when we're retransmitting + // during a flow controlled situation). + CTEAssert(SEQ_LT(NewSeq, SeqTCB->tcb_sendmax) || + (SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendnext) && + SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendmax) && + SEQ_EQ(SeqTCB->tcb_senduna, NewSeq))); + + AmtForward = NewSeq - SeqTCB->tcb_senduna; + + SeqTCB->tcb_sendnext = NewSeq; + + // If we're backing off send next, turn off the FIN_OUTSTANDING flag to + // maintain a consistent state. + if (!SEQ_EQ(NewSeq, SeqTCB->tcb_sendmax)) + SeqTCB->tcb_flags &= ~FIN_OUTSTANDING; + + if (SYNC_STATE(SeqTCB->tcb_state) && SeqTCB->tcb_state != TCB_TIME_WAIT) { + // In these states we need to update the send queue. + + if (!EMPTYQ(&SeqTCB->tcb_sendq)) { + CurQ = QHEAD(&SeqTCB->tcb_sendq); + + SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q); + + // SendReq points to the first send request on the send queue. + // Move forward AmtForward bytes on the send queue, and set the + // TCB pointers to the resultant SendReq, buffer, offset, size. + while (AmtForward) { + + CTEStructAssert(SendReq, tsr); + + if (AmtForward >= SendReq->tsr_unasize) { + // We're going to move completely past this one. Subtract + // his size from AmtForward and get the next one. + + AmtForward -= SendReq->tsr_unasize; + CurQ = QNEXT(CurQ); + CTEAssert(CurQ != QEND(&SeqTCB->tcb_sendq)); + SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q); + } else { + // We're pointing at the proper send req now. Break out + // of this loop and save the information. Further down + // we'll need to walk down the buffer chain to find + // the proper buffer and offset. + break; + } + } + + // We're pointing at the proper send req now. We need to go down + // the buffer chain here to find the proper buffer and offset. + SeqTCB->tcb_cursend = SendReq; + SeqTCB->tcb_sendsize = SendReq->tsr_unasize - AmtForward; + Buffer = SendReq->tsr_buffer; + Offset = SendReq->tsr_offset; + + while (AmtForward) { + // Walk the buffer chain. + uint Length; + + // We'll need the length of this buffer. Use the portable + // macro to get it. We have to adjust the length by the offset + // into it, also. + CTEAssert((Offset < NdisBufferLength(Buffer)) || + ((Offset == 0) && (NdisBufferLength(Buffer) == 0))); + + Length = NdisBufferLength(Buffer) - Offset; + + if (AmtForward >= Length) { + // We're moving past this one. Skip over him, and 0 the + // Offset we're keeping. + + AmtForward -= Length; + Offset = 0; + Buffer = NDIS_BUFFER_LINKAGE(Buffer); + CTEAssert(Buffer != NULL); + } else + break; + } + + // Save the buffer we found, and the offset into that buffer. + SeqTCB->tcb_sendbuf = Buffer; + SeqTCB->tcb_sendofs = Offset + AmtForward; + + } else { + CTEAssert(SeqTCB->tcb_cursend == NULL); + CTEAssert(AmtForward == 0); + } + + } + + CheckTCBSends(SeqTCB); + +} + +#ifdef NT + +//* TCPAbortAndIndicateDisconnect +// +// Abortively closes a TCB and issues a disconnect indication up the the +// transport user. This function is used to support cancellation of +// TDI send and receive requests. +// +// Input: ConnectionContext - The connection ID to find a TCB for. +// +// Returns: Nothing. +// +void +TCPAbortAndIndicateDisconnect( + uint ConnectionContext + ) +{ + TCB *AbortTCB; + CTELockHandle ConnTableHandle, TCBHandle; + TCPConn *Conn; + + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID(ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + AbortTCB = Conn->tc_tcb; + + if (AbortTCB != NULL) { + + // If it's CLOSING or CLOSED, skip it. + if ((AbortTCB->tcb_state != TCB_CLOSED) && !CLOSING(AbortTCB)) { + CTEStructAssert(AbortTCB, tcb); + CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + + AbortTCB->tcb_refcnt++; + AbortTCB->tcb_flags |= NEED_RST; // send a reset if connected + TryToCloseTCB(AbortTCB, TCB_CLOSE_ABORTED, ConnTableHandle); + + RemoveTCBFromConn(AbortTCB); + + IF_TCPDBG(TCP_DEBUG_IRP) { + TCPTRACE(( + "TCPAbortAndIndicateDisconnect, indicating discon\n" + )); + } + + NotifyOfDisc(AbortTCB, NULL, TDI_CONNECTION_ABORTED); + + CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle); + DerefTCB(AbortTCB, TCBHandle); + + // TCB lock freed by DerefTCB. + + return; + } + } + } + + CTEFreeLock(&ConnTableLock, ConnTableHandle); +} + +#endif // NT + + +//* TCBTimeout - Do timeout events on TCBs. +// +// Called every MS_PER_TICKS milliseconds to do timeout processing on TCBs. +// We run throught the TCB table, decrementing timers. If one goes to zero +// we look at it's state to decide what to do. +// +// Input: Timer - Event structure for timer that fired. +// Context - Context for timer (NULL in this case. +// +// Returns: Nothing. +// +void +TCBTimeout(CTEEvent *Timer, void *Context) +{ + CTELockHandle TableHandle, TCBHandle; + uint i; + TCB *CurrentTCB; + uint Delayed = FALSE; + uint CallRcvComplete; + + + // Update our free running counter. + + TCPTime++; + + CTEInterlockedAddUlong(&TCBWalkCount, 1, &TCBTableLock); + + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + // Loop through each bucket in the table, going down the chain of + // TCBs on the bucket. + for (i = 0; i < TCB_TABLE_SIZE; i++) { + TCB *TempTCB; + uint maxRexmitCnt; + + CurrentTCB = TCBTable[i]; + + while (CurrentTCB != NULL) { + CTEStructAssert(CurrentTCB, tcb); + CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle); + + // If it's CLOSING or CLOSED, skip it. + if (CurrentTCB->tcb_state == TCB_CLOSED || CLOSING(CurrentTCB)) { + + TempTCB = CurrentTCB->tcb_next; + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + CurrentTCB = TempTCB; + continue; + } + + CheckTCBSends(CurrentTCB); + CheckTCBRcv(CurrentTCB); + + // First check the rexmit timer. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_rexmittimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_rexmittimer) == 0) { + + // And it's fired. Figure out what to do now. + + // If we've had too many retransits, abort now. + CurrentTCB->tcb_rexmitcnt++; + + if (CurrentTCB->tcb_state == TCB_SYN_SENT) { + maxRexmitCnt = MaxConnectRexmitCount; + } + else { + if (CurrentTCB->tcb_state == TCB_SYN_RCVD) { +#ifdef SYN_ATTACK + // + // Save on locking. Though MaxConnectRexmitCountTmp may + // be changing, we are assured that we will not use + // more than the MaxConnectRexmitCount. + // + maxRexmitCnt = MIN(MaxConnectResponseRexmitCountTmp, MaxConnectResponseRexmitCount); +#else + maxRexmitCnt = MaxConnectResponseRexmitCount; + +#endif + } + else { + maxRexmitCnt = MaxDataRexmitCount; + } + } + + // If we've run out of retransmits or we're in FIN_WAIT2, + // time out. + if (CurrentTCB->tcb_rexmitcnt > maxRexmitCnt) { + + CTEAssert(CurrentTCB->tcb_state > TCB_LISTEN); + + // This connection has timed out. Abort it. First + // reference him, then mark as closed, notify the + // user, and finally dereference and close him. + +TimeoutTCB: + CurrentTCB->tcb_refcnt++; + TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, TCBHandle); + + RemoveTCBFromConn(CurrentTCB); + NotifyOfDisc(CurrentTCB, NULL, TDI_TIMED_OUT); + +#ifdef SYN_ATTACK + if (SynAttackProtect) { + + CTELockHandle Handle; + + CTEGetLockAtDPC(&SynAttLock, &Handle); + // + // We have put the connection in the closed state. + // Decrement the counters for keeping track of half + // open connections + // + CTEAssert((TCPHalfOpen > 0) && (TCPHalfOpenRetried > 0)); + TCPHalfOpen--; + TCPHalfOpenRetried--; + CTEFreeLockFromDPC(&SynAttLock, Handle); + } +#endif + + CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle); + DerefTCB(CurrentTCB, TCBHandle); + + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } + + CurrentTCB->tcb_rtt = 0; // Stop round trip time + // measurement. + + + // Figure out what our new retransmit timeout should be. We + // double it each time we get a retransmit, and reset it + // back when we get an ack for new data. + CurrentTCB->tcb_rexmit = MIN(CurrentTCB->tcb_rexmit << 1, + MAX_REXMIT_TO); + + // Reset the sequence number, and reset the congestion + // window. + ResetSendNext(CurrentTCB, CurrentTCB->tcb_senduna); + + if (!(CurrentTCB->tcb_flags & FLOW_CNTLD)) { + // Don't let the slow start threshold go below 2 + // segments + CurrentTCB->tcb_ssthresh = + MAX( + MIN( + CurrentTCB->tcb_cwin, + CurrentTCB->tcb_sendwin + ) / 2, + (uint) CurrentTCB->tcb_mss * 2 + ); + CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss; + } else { + // We're probing, and the probe timer has fired. We + // need to set the FORCE_OUTPUT bit here. + CurrentTCB->tcb_flags |= FORCE_OUTPUT; + } + + // See if we need to probe for a PMTU black hole. + if (PMTUBHDetect && + CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt+1)/2)) { + // We may need to probe for a black hole. If we're + // doing MTU discovery on this connection and we + // are retransmitting more than a minimum segment + // size, or we are probing for a PMTU BH already, turn + // off the DF flag and bump the probe count. If the + // probe count gets too big we'll assume it's not + // a PMTU black hole, and we'll try to switch the + // router. + if ((CurrentTCB->tcb_flags & PMTU_BH_PROBE) || + ((CurrentTCB->tcb_opt.ioi_flags & IP_FLAG_DF) && + (CurrentTCB->tcb_sendmax - CurrentTCB->tcb_senduna) + > 8)) { + // May need to probe. If we haven't exceeded our + // probe count, do so, otherwise restore those + // values. + if (CurrentTCB->tcb_bhprobecnt++ < 2) { + + // We're going to probe. Turn on the flag, + // drop the MSS, and turn off the don't + // fragment bit. + if (!(CurrentTCB->tcb_flags & PMTU_BH_PROBE)) { + CurrentTCB->tcb_flags |= PMTU_BH_PROBE; + CurrentTCB->tcb_slowcount++; + CurrentTCB->tcb_fastchk |= TCP_FLAG_SLOW; + + // Drop the MSS to the minimum. Save the old + // one in case we need it later. + CurrentTCB->tcb_mss = MIN(MAX_REMOTE_MSS - + CurrentTCB->tcb_opt.ioi_optlength, + CurrentTCB->tcb_remmss); + + CTEAssert(CurrentTCB->tcb_mss > 0); + + CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss; + CurrentTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF; + } + + // Drop the rexmit count so we come here again, + // and don't retrigger DeadGWDetect. + + CurrentTCB->tcb_rexmitcnt--; + } else { + // Too many probes. Stop probing, and allow fallover + // to the next gateway. + // + // Currently this code won't do BH probing on the 2nd + // gateway. The MSS will stay at the minimum size. This + // might be a little suboptimal, but it's + // easy to implement for the Sept. 95 service pack + // and will keep connections alive if possible. + // + // In the future we should investigate doing + // dead g/w detect on a per-connection basis, and then + // doing PMTU probing for each connection. + + if (CurrentTCB->tcb_flags & PMTU_BH_PROBE) { + CurrentTCB->tcb_flags &= ~PMTU_BH_PROBE; + if (--(CurrentTCB->tcb_slowcount) == 0) + CurrentTCB->tcb_fastchk &= + ~TCP_FLAG_SLOW; + + } + CurrentTCB->tcb_bhprobecnt = 0; + } + } + } + + // Check to see if we're doing dead gateway detect. If we + // are, see if it's time to ask IP. + if (DeadGWDetect && + (CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt+1)/2))) { + (*LocalNetInfo.ipi_checkroute)(CurrentTCB->tcb_daddr, + CurrentTCB->tcb_saddr); + } + + // Now handle the various cases. + switch (CurrentTCB->tcb_state) { + + // In SYN-SENT or SYN-RCVD we'll need to retransmit + // the SYN. + case TCB_SYN_SENT: + case TCB_SYN_RCVD: + SendSYN(CurrentTCB, TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + + case TCB_FIN_WAIT1: + case TCB_CLOSING: + case TCB_LAST_ACK: + // The call to ResetSendNext (above) will have + // turned off the FIN_OUTSTANDING flag. + CurrentTCB->tcb_flags |= FIN_NEEDED; + case TCB_CLOSE_WAIT: + case TCB_ESTAB: + // In this state we have data to retransmit, unless + // the window is zero (in which case we need to + // probe), or we're just sending a FIN. + + CheckTCBSends(CurrentTCB); + + Delayed = TRUE; + DelayAction(CurrentTCB, NEED_OUTPUT); + break; + + + // If it's fired in TIME-WAIT, we're all done and + // can clean up. We'll call TryToCloseTCB even + // though he's already sort of closed. TryToCloseTCB + // will figure this out and do the right thing. + case TCB_TIME_WAIT: + TryToCloseTCB(CurrentTCB, TCB_CLOSE_SUCCESS, + TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + default: + break; + } + } + } + + + // Now check the SWS deadlock timer.. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_swstimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_swstimer) == 0) { + // And it's fired. Force output now. + + CurrentTCB->tcb_flags |= FORCE_OUTPUT; + Delayed = TRUE; + DelayAction(CurrentTCB, NEED_OUTPUT); + } + } + + // Check the push data timer. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_pushtimer)) { + // The timer is running. Decrement it. + if (--(CurrentTCB->tcb_pushtimer) == 0) { + // It's fired. + PushData(CurrentTCB); + Delayed = TRUE; + } + } + + // Check the delayed ack timer. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_delacktimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_delacktimer) == 0) { + // And it's fired. Set up to send an ACK. + + Delayed = TRUE; + DelayAction(CurrentTCB, NEED_ACK); + } + + } + + // Finally check the keepalive timer. + if (CurrentTCB->tcb_state == TCB_ESTAB) { + if (CurrentTCB->tcb_flags & KEEPALIVE) { + uint Delta; + + Delta = TCPTime - CurrentTCB->tcb_alive; + if (Delta > KeepAliveTime) { + Delta -= KeepAliveTime; + if (Delta > (CurrentTCB->tcb_kacount * KAInterval)) { + if (CurrentTCB->tcb_kacount < MaxDataRexmitCount) { + SendKA(CurrentTCB, TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } else + goto TimeoutTCB; + } + } else + CurrentTCB->tcb_kacount = 0; + } + } + + + + // If this is an active open connection in SYN-SENT or SYN-RCVD, + // or we have a FIN pending, check the connect timer. + if (CurrentTCB->tcb_flags & (ACTIVE_OPEN | FIN_NEEDED | FIN_SENT)) { + TCPConnReq *ConnReq = CurrentTCB->tcb_connreq; + + CTEAssert(ConnReq != NULL); + if (TCB_TIMER_RUNNING(ConnReq->tcr_timeout)) { + // Timer is running. + if (--(ConnReq->tcr_timeout) == 0) { + // The connection timer has timed out. + TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, + TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } + } + } + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify the + // automatic connection driver about this + // connection. + // + if (CurrentTCB->tcb_flags & ACD_CONN_NOTIF) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + // + // Clear the ACD_CONN_NOTIF flag + // and release the TCB table lock. + // + CurrentTCB->tcb_flags &= ~ACD_CONN_NOTIF; + // + // Determine if we need to notify + // the automatic connection driver. + // + CTEGetLockAtDPC(&AcdDriverG.SpinLock, &AcdHandle); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLockFromDPC(&AcdDriverG.SpinLock, AcdHandle); + if (fEnabled) + TCPNoteNewConnection(CurrentTCB, TCBHandle); + else + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + // + // Reacquire the TCB table lock + // and get the next TCB to process. + // + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } +#endif // RASAUTODIAL + + // Timer isn't running, or didn't fire. + TempTCB = CurrentTCB->tcb_next; + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + CurrentTCB = TempTCB; + } + } + + + // See if we need to call receive complete as part of deadman processing. + // We do this now because we want to restart the timer before calling + // receive complete, in case that takes a while. If we make this check + // while the timer is running we'd have to lock, so we'll check and save + // the result now before we start the timer. + if (DeadmanTicks == TCPTime) { + CallRcvComplete = TRUE; + DeadmanTicks += NUM_DEADMAN_TICKS; + } else + CallRcvComplete = FALSE; + + + // Now check the pending free list. If it's not null, walk down the + // list and decrement the walk count. If the count goes below 2, pull it + // from the list. If the count goes to 0, free the TCB. If the count is + // at 1 it'll be freed by whoever called RemoveTCB. + + CTEGetLockAtDPC(&TCBTableLock, &TableHandle); + if (PendingFreeList != NULL) { + TCB *PrevTCB; + + PrevTCB = STRUCT_OF(TCB, &PendingFreeList, tcb_delayq.q_next); + + do { + CurrentTCB = (TCB *)PrevTCB->tcb_delayq.q_next; + + CTEStructAssert(CurrentTCB, tcb); + + CurrentTCB->tcb_walkcount--; + if (CurrentTCB->tcb_walkcount <= 1) { + *(TCB **)&PrevTCB->tcb_delayq.q_next = + (TCB *)CurrentTCB->tcb_delayq.q_next; + + if (CurrentTCB->tcb_walkcount == 0) { + FreeTCB(CurrentTCB); + } + } else { + PrevTCB = CurrentTCB; + } + } while (PrevTCB->tcb_delayq.q_next != NULL); + } + + TCBWalkCount--; + CTEFreeLockFromDPC(&TCBTableLock, TableHandle); + + // Do AddrCheckTable cleanup + + if (AddrCheckTable) { + + TCPAddrCheckElement *Temp; + + CTEGetLockAtDPC(&AddrObjTableLock, &TableHandle); + + for (Temp=AddrCheckTable; TempTickCount > 0) { + if ((--(Temp->TickCount)) == 0) { + Temp->SourceAddress = 0; + } + } + } + + CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle); + } + + // Restart the timer again. + CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL); + + if (Delayed) + ProcessTCBDelayQ(); + + if (CallRcvComplete) + TCPRcvComplete(); + +} + +//* SetTCBMTU - Set TCB MTU values. +// +// A function called by TCBWalk to set the MTU values of all TCBs using +// a particular path. +// +// Input: CheckTCB - TCB to be checked. +// DestPtr - Ptr to destination address. +// SrcPtr - Ptr to source address. +// MTUPtr - Ptr to new MTU. +// +// Returns: TRUE. +// +uint +SetTCBMTU(TCB *CheckTCB, void *DestPtr, void *SrcPtr, void *MTUPtr) +{ + IPAddr DestAddr = *(IPAddr *)DestPtr; + IPAddr SrcAddr = *(IPAddr *)SrcPtr; + CTELockHandle TCBHandle; + + CTEStructAssert(CheckTCB, tcb); + + CTEGetLock(&CheckTCB->tcb_lock, &TCBHandle); + + if (IP_ADDR_EQUAL(CheckTCB->tcb_daddr,DestAddr) && + IP_ADDR_EQUAL(CheckTCB->tcb_saddr,SrcAddr) && + (CheckTCB->tcb_opt.ioi_flags & IP_FLAG_DF)) { + uint MTU = *(uint *)MTUPtr - CheckTCB->tcb_opt.ioi_optlength;; + + CheckTCB->tcb_mss = (ushort)MIN(MTU, (uint)CheckTCB->tcb_remmss); + + CTEAssert(CheckTCB->tcb_mss > 0); + + // + // Reset the Congestion Window if necessary + // + if (CheckTCB->tcb_cwin < CheckTCB->tcb_mss) { + CheckTCB->tcb_cwin = CheckTCB->tcb_mss; + + // + // Make sure the slow start threshold is at least + // 2 segments + // + if ( CheckTCB->tcb_ssthresh < + ((uint) CheckTCB->tcb_mss*2) + ) { + CheckTCB->tcb_ssthresh = CheckTCB->tcb_mss * 2; + } + } + } + + CTEFreeLock(&CheckTCB->tcb_lock, TCBHandle); + + return TRUE; +} + + +//* DeleteTCBWithSrc - Delete tcbs with a particular src address. +// +// A function called by TCBWalk to delete all TCBs with a particular source +// address. +// +// Input: CheckTCB - TCB to be checked. +// AddrPtr - Ptr to address. +// +// Returns: FALSE if CheckTCB is to be deleted, TRUE otherwise. +// +uint +DeleteTCBWithSrc(TCB *CheckTCB, void *AddrPtr, void *Unused1, void *Unused3) +{ + IPAddr Addr = *(IPAddr *)AddrPtr; + + CTEStructAssert(CheckTCB, tcb); + + if (IP_ADDR_EQUAL(CheckTCB->tcb_saddr,Addr)) + return FALSE; + else + return TRUE; +} + + +//* TCBWalk - Walk the TCBs in the table, and call a function for each of them. +// +// Called when we need to repetively do something to each TCB in the table. +// We call the specified function with a pointer to the TCB and the input +// context for each TCB in the table. If the function returns FALSE, we +// delete the TCB. +// +// Input: CallRtn - Routine to be called. +// Context1 - Context to pass to CallRtn. +// Context2 - Second context to pass to call routine. +// Context3 - Third context to pass to call routine. +// +// Returns: Nothing. +// +void +TCBWalk(uint (*CallRtn)(struct TCB *, void *, void *, void *), void *Context1, + void *Context2, void *Context3) +{ + uint i; + TCB *CurTCB; + CTELockHandle Handle, TCBHandle; + + // Loop through each bucket in the table, going down the chain of + // TCBs on the bucket. For each one call CallRtn. + CTEGetLock(&TCBTableLock, &Handle); + + for (i = 0; i < TCB_TABLE_SIZE; i++) { + + CurTCB = TCBTable[i]; + + // Walk down the chain on this bucket. + while (CurTCB != NULL) { + if (!(*CallRtn)(CurTCB, Context1, Context2, Context3)) { + // He failed the call. Notify the client and close the + // TCB. + CTEGetLock(&CurTCB->tcb_lock, &TCBHandle); + if (!CLOSING(CurTCB)) { + CurTCB->tcb_refcnt++; + CTEFreeLock(&TCBTableLock, TCBHandle); + TryToCloseTCB(CurTCB, TCB_CLOSE_ABORTED, Handle); + + RemoveTCBFromConn(CurTCB); + if (CurTCB->tcb_state != TCB_TIME_WAIT) + NotifyOfDisc(CurTCB, NULL, TDI_CONNECTION_ABORTED); + + CTEGetLock(&CurTCB->tcb_lock, &TCBHandle); + DerefTCB(CurTCB, TCBHandle); + CTEGetLock(&TCBTableLock, &Handle); + } else + CTEFreeLock(&CurTCB->tcb_lock, TCBHandle); + + CurTCB = FindNextTCB(i, CurTCB); + } else { + CurTCB = CurTCB->tcb_next; + } + } + } + + CTEFreeLock(&TCBTableLock, Handle); +} + + +//* FindTCB - Find a TCB in the tcb table. +// +// Called when we need to find a TCB in the TCB table. We take a quick +// look at the last TCB we found, and if it matches we return it. Otherwise +// we hash into the TCB table and look for it. We assume the TCB table lock +// is held when we are called. +// +// Input: Src - Source IP address of TCB to be found. +// Dest - Dest. "" "" "" "" "" "" "" +// DestPort - Destination port of TCB to be found. +// SrcPort - Source port of TCB to be found. +// +// Returns: Pointer to TCB found, or NULL if none. +// +TCB * +FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort) +{ + TCB *FoundTCB; + + if (LastTCB != NULL) { + CTEStructAssert(LastTCB, tcb); + if (IP_ADDR_EQUAL(LastTCB->tcb_daddr, Dest) && + LastTCB->tcb_dport == DestPort && + IP_ADDR_EQUAL(LastTCB->tcb_saddr, Src) && + LastTCB->tcb_sport == SrcPort) + return LastTCB; + } + + // Didn't find it in our 1 element cache. + FoundTCB = TCBTable[TCB_HASH(Dest, Src, DestPort, SrcPort)]; + while (FoundTCB != NULL) { + CTEStructAssert(FoundTCB, tcb); + if (IP_ADDR_EQUAL(FoundTCB->tcb_daddr, Dest) && + FoundTCB->tcb_dport == DestPort && + IP_ADDR_EQUAL(FoundTCB->tcb_saddr, Src) && + FoundTCB->tcb_sport == SrcPort) { + + // Found it. Update the cache for next time, and return. + LastTCB = FoundTCB; + return FoundTCB; + } else + FoundTCB = FoundTCB->tcb_next; + } + + return FoundTCB; + + +} + + +//* InsertTCB - Insert a TCB in the tcb table. +// +// This routine inserts a TCB in the TCB table. No locks need to be held +// when this routine is called. We insert TCBs in ascending address order. +// Before inserting we make sure that the TCB isn't already in the table. +// +// Input: NewTCB - TCB to be inserted. +// +// Returns: TRUE if we inserted, false if we didn't. +// +uint +InsertTCB(TCB *NewTCB) +{ + uint TCBIndex; + CTELockHandle TableHandle, TCBHandle; + TCB *PrevTCB, *CurrentTCB; + TCB *WhereToInsert; + + CTEAssert(NewTCB != NULL); + CTEStructAssert(NewTCB, tcb); + TCBIndex = TCB_HASH(NewTCB->tcb_daddr, NewTCB->tcb_saddr, + NewTCB->tcb_dport, NewTCB->tcb_sport); + + CTEGetLock(&TCBTableLock, &TableHandle); + CTEGetLockAtDPC(&NewTCB->tcb_lock, &TCBHandle); + + // Find the proper place in the table to insert him. While + // we're walking we'll check to see if a dupe already exists. + // When we find the right place to insert, we'll remember it, and + // keep walking looking for a duplicate. + + PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next); + WhereToInsert = NULL; + + while (PrevTCB->tcb_next != NULL) { + CurrentTCB = PrevTCB->tcb_next; + + if (IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, NewTCB->tcb_daddr) && + IP_ADDR_EQUAL(CurrentTCB->tcb_saddr, NewTCB->tcb_saddr) && + (CurrentTCB->tcb_sport == NewTCB->tcb_sport) && + (CurrentTCB->tcb_dport == NewTCB->tcb_dport)) { + + CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle); + CTEFreeLock(&TCBTableLock, TableHandle); + return FALSE; + + } else { + + if (WhereToInsert == NULL && CurrentTCB > NewTCB) { + WhereToInsert = PrevTCB; + } + + CTEStructAssert(PrevTCB->tcb_next, tcb); + PrevTCB = PrevTCB->tcb_next; + } + + } + + if (WhereToInsert == NULL) { + WhereToInsert = PrevTCB; + } + + NewTCB->tcb_next = WhereToInsert->tcb_next; + WhereToInsert->tcb_next = NewTCB; + NewTCB->tcb_flags |= IN_TCB_TABLE; + TStats.ts_numconns++; + + CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle); + CTEFreeLock(&TCBTableLock, TableHandle); + return TRUE; + +} + +//* RemoveTCB - Remove a TCB from the tcb table. +// +// Called when we need to remove a TCB from the TCB table. We assume the +// TCB table lock and the TCB lock are held when we are called. If the +// TCB isn't in the table we won't try to remove him. +// +// Input: RemovedTCB - TCB to be removed. +// +// Returns: TRUE if it's OK to free it, FALSE otherwise. +// +uint +RemoveTCB(TCB *RemovedTCB) +{ + uint TCBIndex; + TCB *PrevTCB; +#ifdef DEBUG + uint Found = FALSE; +#endif + + CTEStructAssert(RemovedTCB, tcb); + + if (RemovedTCB->tcb_flags & IN_TCB_TABLE) { + TCBIndex = TCB_HASH(RemovedTCB->tcb_daddr, RemovedTCB->tcb_saddr, + RemovedTCB->tcb_dport, RemovedTCB->tcb_sport); + + PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next); + + do { + if (PrevTCB->tcb_next == RemovedTCB) { + // Found him. + PrevTCB->tcb_next = RemovedTCB->tcb_next; + RemovedTCB->tcb_flags &= ~IN_TCB_TABLE; + TStats.ts_numconns--; +#ifdef DEBUG + Found = TRUE; +#endif + break; + } + PrevTCB = PrevTCB->tcb_next; +#ifdef DEBUG + if (PrevTCB != NULL) + CTEStructAssert(PrevTCB, tcb); +#endif + } while (PrevTCB != NULL); + + CTEAssert(Found); + + } + + if (LastTCB == RemovedTCB) + LastTCB = NULL; + + if (TCBWalkCount == 0) { + return TRUE; + } else { + RemovedTCB->tcb_walkcount = TCBWalkCount + 1; + *(TCB **)&RemovedTCB->tcb_delayq.q_next = PendingFreeList; + PendingFreeList = RemovedTCB; + return FALSE; + + } + + + +} + + +//* ScavengeTCB - Scavenge a TCB that's in the TIME_WAIT state. +// +// Called when we're running low on TCBs, and need to scavenge one from +// TIME_WAIT state. We'll walk through the TCB table, looking for the oldest +// TCB in TIME_WAIT. We'll remove and return a pointer to that TCB. If we +// don't find any TCBs in TIME_WAIT, we'll return NULL. +// +// Input: Nothing. +// +// Returns: Pointer to a reusable TCB, or NULL. +// +TCB * +ScavengeTCB(void) +{ + CTELockHandle TableHandle, TCBHandle, FoundLock; + uint Now = CTESystemUpTime(); + uint Delta = 0; + uint i; + TCB *FoundTCB = NULL, *PrevFound; + TCB *CurrentTCB, *PrevTCB; + + CTEGetLock(&TCBTableLock, &TableHandle); + + if (TCBWalkCount != 0) { + CTEFreeLock(&TCBTableLock, TableHandle); + return NULL; + } + + for (i = 0; i < TCB_TABLE_SIZE; i++) { + + PrevTCB = STRUCT_OF(TCB, &TCBTable[i], tcb_next); + CurrentTCB = PrevTCB->tcb_next; + + while (CurrentTCB != NULL) { + CTEStructAssert(CurrentTCB, tcb); + + CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle); + if (CurrentTCB->tcb_state == TCB_TIME_WAIT && + (CurrentTCB->tcb_refcnt == 0) && !CLOSING(CurrentTCB)){ + if (FoundTCB == NULL || ((Now - CurrentTCB->tcb_alive) > Delta)) { + // Found a new 'older' TCB. If we already have one, free + // the lock on him and get the lock on the new one. + if (FoundTCB != NULL) + CTEFreeLock(&FoundTCB->tcb_lock, TCBHandle); + else + FoundLock = TCBHandle; + + PrevFound = PrevTCB; + FoundTCB = CurrentTCB; + Delta = Now - FoundTCB->tcb_alive; + } else + CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle); + } else + CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle); + + // Look at the next one. + PrevTCB = CurrentTCB; + CurrentTCB = PrevTCB->tcb_next; + } + } + + // If we have one, pull him from the list. + if (FoundTCB != NULL) { + PrevFound->tcb_next = FoundTCB->tcb_next; + FoundTCB->tcb_flags &= ~IN_TCB_TABLE; + // Close the RCE on this guy. + (*LocalNetInfo.ipi_closerce)(FoundTCB->tcb_rce); + TStats.ts_numconns--; + if (LastTCB == FoundTCB) { + LastTCB = NULL; + } + CTEFreeLock(&FoundTCB->tcb_lock, FoundLock); + } + + CTEFreeLock(&TCBTableLock, TableHandle); + return FoundTCB; +} + +//* AllocTCB - Allocate a TCB. +// +// Called whenever we need to allocate a TCB. We try to pull one off the +// free list, or allocate one if we need one. We then initialize it, etc. +// +// Input: Nothing. +// +// Returns: Pointer to the new TCB, or NULL if we couldn't get one. +// +TCB * +AllocTCB(void) +{ + TCB *NewTCB; + + // First, see if we have one on the free list. The code for doing this + // is a little different between the NT and VxD worlds. +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList(&FreeTCBList, &FreeTCBListLock); + + if (BufferLink != NULL) { + NewTCB = STRUCT_OF(TCB, BufferLink, tcb_next); + CTEStructAssert(NewTCB, tcb); + } +#else // NT + NewTCB = FreeTCBList; + if (NewTCB != NULL) { + CTEStructAssert(NewTCB, tcb); + FreeTCBList = NewTCB->tcb_next; + } +#endif // NT + + else { + + // We have none on the free list. If the total number of TCBs + // outstanding is more than we like to keep on the free list, try + // to scavenge a TCB from time wait. + if (CurrentTCBs < MaxFreeTCBs || ((NewTCB = ScavengeTCB()) == NULL)) { + if (CurrentTCBs < MaxTCBs) { + NewTCB = CTEAllocMem(sizeof(TCB)); + if (NewTCB == NULL) { + return NewTCB; + } + else { + CTEInterlockedAddUlong(&CurrentTCBs, 1, &FreeTCBListLock); + } + } else + return NULL; + } + } + + CTEAssert(NewTCB != NULL); + + CTEMemSet(NewTCB, 0, sizeof(TCB)); +#ifdef DEBUG + NewTCB->tcb_sig = tcb_signature; +#endif + INITQ(&NewTCB->tcb_sendq); + NewTCB->tcb_cursend = NULL; + NewTCB->tcb_alive = TCPTime; + // Initially we're not on the fast path because we're not established. Set + // the slowcount to one and set up the fastchk fields so we don't take the + // fast path. + NewTCB->tcb_slowcount = 1; + NewTCB->tcb_fastchk = TCP_FLAG_ACK | TCP_FLAG_SLOW; + +#if FAST_RETRANSMIT + NewTCB->tcb_dup = 0; +#endif + + CTEInitLock(&NewTCB->tcb_lock); + + return NewTCB; +} + +//* FreeTCB - Free a TCB. +// +// Called whenever we need to free a TCB. +// +// Note: This routine may be called with the TCBTableLock held. +// +// Input: FreedTCB - TCB to be freed. +// +// Returns: Nothing. +// +void +FreeTCB(TCB *FreedTCB) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; +#endif // NT + + +#ifndef NT + // + // Since we've moved to using sequenced lists for these resources, + // it's risky to actually free the memory here, so we won't do this + // for NT unless it becomes a problem. + if (CurrentTCBs > MaxFreeTCBs) { + CTEInterlockedAddUlong(&CurrentTCBs, (ulong) -1, &FreeTCBListLock); + CTEFreeMem(FreedTCB); + return; + } +#endif + +#ifdef NT + + CTEStructAssert(FreedTCB, tcb); + + BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedTCB->tcb_next), Next); + ExInterlockedPushEntrySList( + &FreeTCBList, + BufferLink, + &FreeTCBListLock + ); + +#else // NT + + CTEStructAssert(FreedTCB, tcb); + + FreedTCB->tcb_next = FreeTCBList; + FreeTCBList = FreedTCB; + +#endif // NT +} + +#pragma BEGIN_INIT + +//* InitTCB - Initialize our TCB code. +// +// Called during init time to initialize our TCB code. We initialize +// the TCB table, etc, then return. +// +// Input: Nothing. +// +// Returns: TRUE if we did initialize, false if we didn't. +// +int +InitTCB(void) +{ + int i; + + for (i = 0; i < TCB_TABLE_SIZE; i++) + TCBTable[i] = NULL; + + LastTCB = NULL; + +#ifdef NT + ExInitializeSListHead(&FreeTCBList); +#endif + + CTEInitLock(&TCBTableLock); + CTEInitLock(&FreeTCBListLock); + + TCPTime = 0; + TCBWalkCount = 0; + DeadmanTicks = NUM_DEADMAN_TICKS; + CTEInitTimer(&TCBTimer); + CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL); + + return TRUE; +} + +//* UnInitTCB - UnInitialize our TCB code. +// +// Called during init time if we're going to fail the init. We don't actually +// do anything here. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +UnInitTCB(void) +{ + CTEStopTimer(&TCBTimer); + return; +} + +#pragma END_INIT + diff --git a/private/ntos/tdi/tcpip/tcp/tcb.h b/private/ntos/tdi/tcpip/tcp/tcb.h new file mode 100644 index 000000000..3d273e50e --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcb.h @@ -0,0 +1,67 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCB.H - TCB management definitions. +// +// This file contains the definitons needed for TCB management. +// + +#define TCB_TABLE_SIZE 64 + +#define MAX_REXMIT_CNT 5 +#define MAX_CONNECT_REXMIT_CNT 3 +#define MAX_CONNECT_RESPONSE_REXMIT_CNT 3 +#ifdef SYN_ATTACK +#define ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT 1 +#endif + +extern uint TCPTime; + +#ifdef OLDHASH1 +#define TCB_HASH(DA,SA,DP,SP) ((uint)(*(uchar *)&(DA) + *((uchar *)&(DA) + 1) \ + + *((uchar *)&(DA) + 2) + *((uchar *)&(DA) + 3)) % TCB_TABLE_SIZE) +#endif + +#ifdef OLDHASH +#define TCB_HASH(DA,SA,DP,SP) (((DA) + (SA) + (uint)(DP) + (uint)(SP)) % \ + TCB_TABLE_SIZE) +#endif + +#define ROR8(x) (uchar)(((uchar)(x) >> 1) | (uchar)(((uchar)(x) & 1) << 7)) + +#define TCB_HASH(DA,SA,DP,SP) (((uint)(ROR8(ROR8(ROR8(ROR8(*((uchar *)&(DP) + 1) + \ +*((uchar *)&(DP))) + \ +*((uchar *)&(DA) + 3)) + \ +*((uchar *)&(DA) + 2)) + \ +*((uchar *)&(DA) + 1)) + \ +*((uchar *)&(DA)) )) % TCB_TABLE_SIZE) + +extern struct TCB *FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort, + ushort SrcPort); +extern uint InsertTCB(struct TCB *NewTCB); +extern struct TCB *AllocTCB(void); +extern void FreeTCB(struct TCB *FreedTCB); +extern uint RemoveTCB(struct TCB *RemovedTCB); + +extern uint ValidateTCBContext(void *Context, uint *Valid); +extern uint ReadNextTCB(void *Context, void *OutBuf); + +extern int InitTCB(void); +extern void UnInitTCB(void); +extern void TCBWalk(uint (*CallRtn)(struct TCB *, void *, void *, + void *), void *Context1, void *Context2, + void *Context3); +extern uint DeleteTCBWithSrc(struct TCB *CheckTCB, void *AddrPtr, + void *Unused1, void *Unused2); +extern uint SetTCBMTU(struct TCB *CheckTCB, void *DestPtr, + void *SrcPtr, void *MTUPtr); +extern void ReetSendNext(struct TCB *SeqTCB, SeqNum DropSeq); + +extern uint TCBWalkCount; + + + + diff --git a/private/ntos/tdi/tcpip/tcp/tcp.h b/private/ntos/tdi/tcpip/tcp/tcp.h new file mode 100644 index 000000000..d88d3443e --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcp.h @@ -0,0 +1,426 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +#ifndef _TCP_INCLUDED_ +#define _TCP_INCLUDED_ + +//** TCP.H - TCP definitions. +// +// This file contains the definitions of TCP protocol specific options, such +// as the sequence numbers and TCB. +// + +#define PROTOCOL_TCP 6 +#define MAX_REMOTE_MSS 536 + +//* Timer stuff. We keep timers as ticks. +#define MS_PER_TICK 100 +#define MS_TO_TICKS(m) ((m) / MS_PER_TICK) +#define MIN_RETRAN_TICKS 3 + +#define DEL_ACK_TICKS 2 +// Define MAX_REXMIT_TO to be number of ticks in 2MSL (=240 seconds) + +#define MAX_REXMIT_TO ((ushort)FinWait2TO) + +#define SWS_TO MS_TO_TICKS(5000) + +#define FIN_WAIT2_TO 240 +#define PUSH_TO MS_TO_TICKS(500) + +typedef ulong TCP_TIME; +#define MAX_CONN_TO_TICKS 0xffff +#define INFINITE_CONN_TO(t) ((t) == 0) +#define TCP_TIME_TO_TICKS(t) (((t)/MS_PER_TICK)+1) + + +// Sequence numbers are kept as signed 32 bit quantities, with macros +// defined to do wraparound comparisons on them. + +typedef int SeqNum; // A sequence number. + +//* Macros for comparions on sequence numbers. + +#define SEQ_GT(a, b) (((a) - (b)) > 0) +#define SEQ_GTE(a, b) (((a) - (b)) >= 0) +#define SEQ_LT(a, b) (((a) - (b)) < 0) +#define SEQ_LTE(a, b) (((a) - (b)) <= 0) +#define SEQ_EQ(a, b) ((a) == (b)) + +//* The TCB - transport control block structure. This is the +// structure that contains all of the state for the transport +// connection, including sequence numbers, flow control information, +// pending sends and receives, etc. + +#define tcb_signature 0x20424354 // 'TCB ' + +struct TCB { + +#ifdef DEBUG + ulong tcb_sig; // Debug signature. +#endif + struct TCB *tcb_next; // Next pointer in TCB table. + DEFINE_LOCK_STRUCTURE(tcb_lock) + // Send sequence variables. + SeqNum tcb_senduna; // Sequence number of first unack'd + // data. + SeqNum tcb_sendnext; // Sequence number of next byte to + // send. + SeqNum tcb_sendmax; // Max value of sendnext this + // epoch. + uint tcb_sendwin; // Send window. + uint tcb_unacked; // Total number of bytes of unacked + // data. + uint tcb_maxwin; // Max send window seen. + uint tcb_cwin; // Congestion window. + uint tcb_ssthresh; // Slow start threshold. + uint tcb_phxsum; // Precomputed pseudo-header xsum. + struct TCPSendReq *tcb_cursend; // Current send in use. + PNDIS_BUFFER tcb_sendbuf; // Current buffer chain being sent. + uint tcb_sendofs; // Offset into start of chain. + uint tcb_sendsize; // Number of bytes unsent in current + // send. + Queue tcb_sendq; // Queue of send requests. + + // Receive sequence variables. + SeqNum tcb_rcvnext; // Next byte we expect to receive. + int tcb_rcvwin; // Receive window we're offering. + SeqNum tcb_sendwl1; // Window update sequence number. + SeqNum tcb_sendwl2; // Window update ack number. + struct TCPRcvReq *tcb_currcv; // Current receive buffer. + uint tcb_indicated; // Bytes of data indicated. + uint tcb_flags; // Flags for this TCB. + uint tcb_fastchk; // Fast receive path check field. + uint (*tcb_rcvhndlr)(struct TCB *, uint, + struct IPRcvBuf *, uint Size); + // Addressing info. + IPAddr tcb_daddr; // Destination IP address. + IPAddr tcb_saddr; // Source IP address. + ushort tcb_dport; // Destination port. + ushort tcb_sport; // Source port. + + ushort tcb_mss; // MSS for this connection. + ushort tcb_rexmit; // Rexmit value. + uint tcb_refcnt; // Reference count for TCB. + SeqNum tcb_rttseq; // Sequence number being measured + // for RTT. + + // Retransmit timer information. These are stored as ticks, where by + // default each tick is 100ms. + ushort tcb_smrtt; // Smoothed rtt value. + ushort tcb_delta; // Delta value. + + ushort tcb_remmss; // MSS advertised by peer. + uchar tcb_slowcount; // Count of reasons why we're on + // the slow path. + uchar tcb_pushtimer; // The 'push' timer. + + // State information. + uchar tcb_state; // State of this TCB. + uchar tcb_rexmitcnt; // Count of rexmits on this TCB. + uchar tcb_pending; // Pending actions on this TCB. + uchar tcb_kacount; // Count of keep alive probes sent. + IP_STATUS tcb_error; // Last error we heard about from + // IP. + + uint tcb_rtt; // Current round trip time TS. + + ushort tcb_rexmittimer; // Timer for rexmit. + ushort tcb_delacktimer; // Timer for delayed ack. + + + uint tcb_defaultwin; // Default rcv. window. + uint tcb_alive; // Keep alive time value. + + struct TCPRAHdr *tcb_raq; // Reassembly queue. + struct TCPRcvReq *tcb_rcvhead; // Head of recv. buffer queue. + struct TCPRcvReq *tcb_rcvtail; // Tail of recv. buffer queue. + uint tcb_pendingcnt; // Bytes waiting to be received. + struct IPRcvBuf *tcb_pendhead; // Head of pending recv. queue. + struct IPRcvBuf *tcb_pendtail; // Tail of pending recv. queue. + + struct TCPConnReq *tcb_connreq; // Connection-type request for + // this connection. + void *tcb_conncontext; // Connection context for this + // connection. + + uint tcb_bcountlow; // Low part of byte count. + uint tcb_bcounthi; // High part of bytecount. + uint tcb_totaltime; // Total number of ticks spent + // sending. + struct TCB *tcb_aonext; // Next pointer on AddrObj. + struct TCPConn *tcb_conn; // Back pointer to conn for TCB. + Queue tcb_delayq; // Queue linkage for delay queue. + uchar tcb_closereason; // Reason we're closing. + uchar tcb_bhprobecnt; // BH probe count. + ushort tcb_swstimer; // Timer for SWS override. + void *tcb_rcvind; // Receive indication handler. + void *tcb_ricontext; // Receive indication context. + // Miscellaneous info, for IP. + IPOptInfo tcb_opt; // Option information. + RouteCacheEntry *tcb_rce; // RCE for this connection. + struct TCPConnReq *tcb_discwait; // Disc-Wait req., if there is one. + struct TCPRcvReq *tcb_exprcv; // Head of expedited recv. buffer + // queue. + struct IPRcvBuf *tcb_urgpending; // Urgent data queue. + uint tcb_urgcnt; // Byte count of data on urgent q. + uint tcb_urgind; // Urgent bytes indicated. + SeqNum tcb_urgstart; // Start of urgent data. + SeqNum tcb_urgend; // End of urgent data. + uint tcb_walkcount; // Count of number of people + // 'walking' this TCB. +#if FAST_RETRANSMIT + ushort tcb_dup; // For Fast recovery algorithm + ushort tcb_force; // Force next send after fast send +#endif + +}; + +//* Definitions for TCP states. +#define TCB_CLOSED 0 // Closed. +#define TCB_LISTEN 1 // Listening. +#define TCB_SYN_SENT 2 // SYN Sent. +#define TCB_SYN_RCVD 3 // SYN received. +#define TCB_ESTAB 4 // Established. +#define TCB_FIN_WAIT1 5 // FIN-WAIT-1 +#define TCB_FIN_WAIT2 6 // FIN-WAIT-2 +#define TCB_CLOSE_WAIT 7 // Close waiting. +#define TCB_CLOSING 8 // Closing state. +#define TCB_LAST_ACK 9 // Last ack state. +#define TCB_TIME_WAIT 10 // Time wait state. + +typedef struct TCB TCB; + +#define SYNC_STATE(s) ((s) > TCB_SYN_RCVD) +#define GRACEFUL_CLOSED_STATE(s) ((s) >= TCB_LAST_ACK) +#define DATA_RCV_STATE(s) ((s) >= TCB_ESTAB && (s) <= TCB_FIN_WAIT2) +#define DATA_SEND_STATE(s) ((s) == TCB_ESTAB || (s) == TCB_CLOSE_WAIT) + +//* Definitions for flags. +#define WINDOW_SET 0x00000001 // Window explictly set. +#define CLIENT_OPTIONS 0x00000002 // Have client IP options on conn. +#define CONN_ACCEPTED 0x00000004 // Connection was accepted. +#define ACTIVE_OPEN 0x00000008 // Connection came from an active + // open. +#define DISC_NOTIFIED 0x00000010 // Client has been notified of a + // disconnect. +#define IN_DELAY_Q 0x00000020 // We're in the delayed action Q. +#define RCV_CMPLTING 0x00000040 // We're completeing rcvs. +#define IN_RCV_IND 0x00000080 // We're calling a rcv. indicate + // handler. +#define NEED_RCV_CMPLT 0x00000100 // We need to have recvs. completed. +#define NEED_ACK 0x00000200 // We need to send an ACK. +#define NEED_OUTPUT 0x00000400 // We need to output. + +#define DELAYED_FLAGS (NEED_RCV_CMPLT | NEED_ACK | NEED_OUTPUT) + + +#define ACK_DELAYED 0x00000800 // We've delayed sending an ACK. + +#define PMTU_BH_PROBE 0x00001000 // We're probing for a PMTU BH. +#define BSD_URGENT 0x00002000 // We're using BSD urgent semantics. +#define IN_DELIV_URG 0x00004000 // We're in the DeliverUrgent routine. +#define URG_VALID 0x00008000 // We've seen urgent data, and + // the urgent data fields are valid. + +#define FIN_NEEDED 0x00010000 // We need to send a FIN. +#define NAGLING 0x00020000 // We are using Nagle's algorithm. +#define IN_TCP_SEND 0x00040000 // We're in TCPSend. +#define FLOW_CNTLD 0x00080000 // We've received a zero window + // from our peer. +#define DISC_PENDING 0x00100000 // A disconnect notification is + // pending. +#define TW_PENDING 0x00200000 // We're waiting to finish going + // to TIME-WAIT. +#define FORCE_OUTPUT 0x00400000 // Output is being forced. +#define FORCE_OUT_SHIFT 22 // Shift to get FORCE_OUTPUT into + // low bit. +#define SEND_AFTER_RCV 0x00800000 // We need to send after we get out + // of recv. +#define GC_PENDING 0x01000000 // A graceful close is pending. +#define KEEPALIVE 0x02000000 // Doing keepalives on this TCB. +#define URG_INLINE 0x04000000 // Urgent data to be processed + // inline. + +#ifdef RASAUTODIAL +#define ACD_CONN_NOTIF 0x08000000 // inform automatic connection + // driver about this connection +#endif // RASAUTODIAL + +#define FIN_OUTSTANDING 0x10000000 // We've sent a FIN 'recently', i.e. + // since the last retransmit. When + // this flag is set sendnext == + // sendmax. + +#define FIN_OUTS_SHIFT 28 // Shift to FIN_OUTSTANDING bit into + // low bit. +#define FIN_SENT 0x20000000 // We've sent a FIN that hasn't + // been acknowledged. Once this + // bit has been turned on in + // FIN-WAIT-1 the sequence number + // of the FIN will be sendmax-1. +#define NEED_RST 0x40000000 // We need to send a RST when + // closing. +#define IN_TCB_TABLE 0x80000000 // TCB is in the TCB table. + +//* The defintion of the 'slow flags'. If any of these flags are set we'll +// be forced off of the fast path. + +#define TCP_SLOW_FLAGS (URG_VALID | FLOW_CNTLD | GC_PENDING | \ + TW_PENDING | DISC_NOTIFIED | IN_DELIV_URG | \ + FIN_NEEDED | FIN_SENT | FIN_OUTSTANDING | \ + DISC_PENDING | PMTU_BH_PROBE) + +//* Close reasons. +#define TCB_CLOSE_RST 0x80 // Received a RST segment. +#define TCB_CLOSE_ABORTED 0x40 // Had a local abort. +#define TCB_CLOSE_TIMEOUT 0x20 // Connection timed out. +#define TCB_CLOSE_REFUSED 0x10 // Connect attempt was refused. +#define TCB_CLOSE_UNREACH 0x08 // Remote destination unreachable. +#define TCB_CLOSE_SUCCESS 0x01 // Successfull close. + +//* TCB Timer macros. +#define START_TCB_TIMER(t, v) (t) = (v) +#define STOP_TCB_TIMER(t) (t) = 0 +#define TCB_TIMER_RUNNING(t) ((t) != 0) + +// Macro to compute retransmit timeout. +#define REXMIT_TO(t) ((((t)->tcb_smrtt >> 2) + (t)->tcb_delta) >> 1) + +//* Definitons for pending actions. We define a PENDING_ACTION macro +// that can be used to decide whether or not we can proceed with an +// activity. The only pending action we really care about is DELETE - others +// are low priority and can be put off. +#define PENDING_ACTION(t) ((t)->tcb_pending & DEL_PENDING) +#define DEL_PENDING 0x01 // Delete is pending. +#define OPT_PENDING 0x02 // Option set is pending. + +//* Macro to see if a TCB is closing. +#define CLOSING(t) ((t)->tcb_pending & DEL_PENDING) + +//* Structure of a TCP packet header. + +struct TCPHeader { + ushort tcp_src; // Source port. + ushort tcp_dest; // Destination port. + SeqNum tcp_seq; // Sequence number. + SeqNum tcp_ack; // Ack number. + ushort tcp_flags; // Flags and data offset. + ushort tcp_window; // Window offered. + ushort tcp_xsum; // Checksum. + ushort tcp_urgent; // Urgent pointer. +}; + +typedef struct TCPHeader TCPHeader; + +//* Definitions for header flags. +#define TCP_FLAG_FIN 0x00000100 +#define TCP_FLAG_SYN 0x00000200 +#define TCP_FLAG_RST 0x00000400 +#define TCP_FLAG_PUSH 0x00000800 +#define TCP_FLAG_ACK 0x00001000 +#define TCP_FLAG_URG 0x00002000 + +#define TCP_FLAGS_ALL (TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | \ + TCP_FLAG_ACK | TCP_FLAG_URG) + +//* Flags in the tcb_fastchk field that are not in the TCP header proper. +// Setting these flags forces us off the fast path. +#define TCP_FLAG_SLOW 0x00000001 // Need to be on slow path. +#define TCP_FLAG_IN_RCV 0x00000002 // In recv. path already. + +#if FAST_RETRANSMIT +#define TCP_FLAG_FASTREC 0x00000004 // This is used to mark tcb +#endif // when Fast retransmit is in progress + // Debugging purpose only + +#define TCP_OFFSET_MASK 0xf0 +#define TCP_HDR_SIZE(t) (uint)(((*(uchar *)&(t)->tcp_flags) & TCP_OFFSET_MASK) >> 2) + +#define MAKE_TCP_FLAGS(o, f) ((f) | ((o) << 4)) + +#define TCP_OPT_EOL 0 +#define TCP_OPT_NOP 1 +#define TCP_OPT_MSS 2 +#define MSS_OPT_SIZE 4 + +//* Convenient byte swapped structure for receives. +struct TCPRcvInfo { + SeqNum tri_seq; // Sequence number. + SeqNum tri_ack; // Ack number. + uint tri_window; // Window. + uint tri_urgent; // Urgent pointer. + uint tri_flags; // Flags. +}; + +typedef struct TCPRcvInfo TCPRcvInfo; + + + +//* General structure, at the start of all command specific request structures. + +#define tr_signature 0x20205254 // 'TR ' + +struct TCPReq { +#ifdef DEBUG + ulong tr_sig; +#endif + struct Queue tr_q; // Q linkage. + CTEReqCmpltRtn tr_rtn; // Completion routine. + PVOID tr_context; // User context. + int tr_status; // Final complete status. +}; + +typedef struct TCPReq TCPReq; + + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'tPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'tPCT') + +#endif // POOL_TAGGING + +// +// TCP endpoint context structure allocated for each open of TCP/UDP. +// A pointer to this structure is stored in FileObject->FsContext. +// +typedef struct _TCP_CONTEXT { + union { + HANDLE AddressHandle; + CONNECTION_CONTEXT ConnectionContext; + HANDLE ControlChannel; + } Handle; + ULONG ReferenceCount; + BOOLEAN CancelIrps; +#if DBG + LIST_ENTRY PendingIrpList; + LIST_ENTRY CancelledIrpList; +#endif + KEVENT CleanupEvent; +} TCP_CONTEXT, *PTCP_CONTEXT; + +#endif // NT + + +#include "tcpdeb.h" + +#endif // _TCP_INCLUDED_ + + diff --git a/private/ntos/tdi/tcpip/tcp/tcpcfg.h b/private/ntos/tdi/tcpip/tcp/tcpcfg.h new file mode 100644 index 000000000..e3ee3ab01 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpcfg.h @@ -0,0 +1,86 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//* TCPCFG.H - Definitions of configuration information for TCP. +// + +/*NOINC*/ +extern uint DeadGWDetect; +extern uint PMTUDiscovery; +extern uint PMTUBHDetect; +extern uint KeepAliveTime; +extern uint KAInterval; +extern uint DefaultRcvWin; +extern uint MaxConnections; +extern uint MaxConnectRexmitCount; +extern uint MaxConnectResponseRexmitCount; +extern uint MaxDataRexmitCount; + +#ifdef SYN_ATTACK + +extern BOOLEAN SynAttackProtect; +extern uint TCPHalfOpen; +extern uint TCPHalfOpenRetried; +extern uint TCPMaxHalfOpen; +extern uint TCPMaxHalfOpenRetried; +extern uint TCPMaxHalfOpenRetriedLW; +extern uint TCPPortsExhausted; +extern uint TCPMaxPortsExhausted; +extern uint TCPMaxPortsExhaustedLW; +extern uint MaxConnectResponseRexmitCountTmp; +EXTERNAL_LOCK(SynAttLock) +#endif + + +extern uint BSDUrgent; +extern uint PreloadCount; +extern uint FinWait2TO; +extern uint NTWMaxConnectCount; +extern uint NTWMaxConnectTime; +extern uint MaxUserPort; + +#ifdef SECFLTR +extern uint SecurityFilteringEnabled; +#endif // SECFLTR + +/*INC*/ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define DEFAULT_DEAD_GW_DETECT TRUE +#define DEFAULT_PMTU_DISCOVERY TRUE +#define DEFAULT_PMTU_BHDETECT FALSE +#define DEFAULT_KA_TIME 7200000 +#define DEFAULT_KA_INTERVAL 1000 +#define DEFAULT_RCV_WIN 8192 +#define DEFAULT_PRELOAD_COUNT 0 +#define MAX_PRELOAD_COUNT 32 +#define PRELOAD_BLOCK_SIZE 16384 + +/*NOINC*/ +#ifndef VXD +#define DEFAULT_MAX_CONNECTIONS (INVALID_CONN_INDEX - 1) +#define NTW_MAX_CONNECT_TIME 600 +#define NTW_MAX_CONNECT_COUNT 15 +#else + +/*INC*/ +#define DEFAULT_MAX_CONNECTIONS 100 + +/*NOINC*/ +#endif +/*INC*/ + +#define DEFAULT_CONNECT_REXMIT_CNT 3 +#define DEFAULT_DATA_REXMIT_CNT 5 +#define DEFAULT_BSD_URGENT TRUE + diff --git a/private/ntos/tdi/tcpip/tcp/tcpconn.c b/private/ntos/tdi/tcpip/tcp/tcpconn.c new file mode 100644 index 000000000..daeadf85c --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpconn.c @@ -0,0 +1,2344 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPCONN.C - TCP connection mgmt code. +// +// This file contains the code handling TCP connection related requests, +// such as connecting and disconnecting. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "tcpdeliv.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" + +#define CONN_INDEX(c) ((c) & 0xffffff) +#define CONN_INST(c) ((uchar)((c) >> 24)) +#define MAKE_CONN_ID(i, s) ((((uint)(s)) << 24) | ((uint)(i))) +#define GROW_DELTA 16 +#define INVALID_CONN_ID MAKE_CONN_ID(INVALID_CONN_INDEX, 0xff) + + +#ifndef NT +TCPConnReq *ConnReqFree; // Connection request free list. +#else +SLIST_HEADER ConnReqFree; // Connection request free list. +extern PDRIVER_OBJECT TCPDriverObject; +#endif + +DEFINE_LOCK_STRUCTURE(ConnReqFreeLock) // Lock to protect conn req free list. +uint NumConnReq; // Current number of ConnReqs in system. +uint MaxConnReq = 0xffffffff; // Maximum allowed number of ConnReqs. + +TCPConnTable *ConnTable = NULL; // The current connection table. + +uint ConnTableSize; // Current number of entries in the + // ConnTable. +uchar ConnInst; // Current conn inst in use. +uint NextConnIndex; // Next conn. index to use. + +DEFINE_LOCK_STRUCTURE(ConnTableLock) +EXTERNAL_LOCK(AddrObjTableLock) +EXTERNAL_LOCK(TCBTableLock) + +TCPAddrCheckElement *AddrCheckTable = NULL; // The current check table + +extern IPInfo LocalNetInfo; +extern void RemoveConnFromAO(AddrObj *AO, TCPConn *Conn); + + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitTCPConn(void); +void UnInitTCPConn(void); +void NotifyConnLimitProc(CTEEvent *Event, void *Context); + +typedef struct ConnLimitExceededStruct { + CTEEvent cle_event; + IPAddr cle_addr; + ulong cle_port; +} ConnLimitExceededStruct; + + +#pragma alloc_text(INIT, InitTCPConn) +#pragma alloc_text(INIT, UnInitTCPConn) +#pragma alloc_text(PAGE, NotifyConnLimitProc) + +#endif // ALLOC_PRAGMA +#endif + +void CompleteConnReq(TCB *CmpltTCB, IPOptInfo *OptInfo, TDI_STATUS Status); + + +//** Routines for handling conn refcount going to 0. + +//* DummyDone - Called when nothing to do. +// +// Input: Conn - Conn goint to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +DummyDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEFreeLock(&ConnTableLock, Handle); +} + +//* DummyCmplt - Dummy close completion routine. +void +DummyCmplt(PVOID Dummy1, uint Dummy2, uint Dummy3) +{ +} + +//* CloseDone - Called when we need to complete a close. +// +// Input: Conn - Conn going to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +CloseDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEReqCmpltRtn Rtn; // Completion routine. + PVOID Context; // User context for completion routine. + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + AddrObj *AO; + + CTEAssert(Conn->tc_flags & CONN_CLOSING); + + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + CTEFreeLock(&ConnTableLock, Handle); + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + if ((AO = Conn->tc_ao) != NULL) { + + CTEStructAssert(AO, ao); + + // It's associated. + CTEGetLock(&AO->ao_lock, &AOHandle); + RemoveConnFromAO(AO, Conn); + // We've pulled him from the AO, we can free the lock now. + CTEFreeLock(&AO->ao_lock, AOHandle); + } + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + CTEFreeMem(Conn); + + (*Rtn)(Context, TDI_SUCCESS, 0); + +} + +//* DisassocDone - Called when we need to complete a disassociate. +// +// Input: Conn - Conn going to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +DisassocDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEReqCmpltRtn Rtn; // Completion routine. + PVOID Context; // User context for completion routine. + AddrObj *AO; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + uint NeedClose = FALSE; + + CTEAssert(Conn->tc_flags & CONN_DISACC); + CTEAssert(!(Conn->tc_flags & CONN_CLOSING)); + CTEAssert(Conn->tc_refcnt == 0); + + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + Conn->tc_refcnt = 1; + CTEFreeLock(&ConnTableLock, Handle); + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + if (!(Conn->tc_flags & CONN_CLOSING)) { + + AO = Conn->tc_ao; + if (AO != NULL) { + CTEGetLock(&AO->ao_lock, &AOHandle); + RemoveConnFromAO(AO, Conn); + CTEFreeLock(&AO->ao_lock, AOHandle); + } + + CTEAssert(Conn->tc_refcnt == 1); + Conn->tc_flags &= ~CONN_DISACC; + } else + NeedClose = TRUE; + + Conn->tc_refcnt = 0; + CTEFreeLock(&AddrObjTableLock, ConnTableHandle); + + if (NeedClose) { + CloseDone(Conn, AOTableHandle); + } else { + CTEFreeLock(&ConnTableLock, AOTableHandle); + (*Rtn)(Context, TDI_SUCCESS, 0); + } + +} + + +//* FreeConnReq - Free a connection request structure. +// +// Called to free a connection request structure. +// +// Input: FreedReq - Connection request structure to be freed. +// +// Returns: Nothing. +// +void +FreeConnReq(TCPConnReq *FreedReq) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(FreedReq, tcr); + + BufferLink = STRUCT_OF( + SINGLE_LIST_ENTRY, + &(FreedReq->tcr_req.tr_q.q_next), + Next + ); + + ExInterlockedPushEntrySList( + &ConnReqFree, + BufferLink, + &ConnReqFreeLock + ); + +#else // NT + TCPConnReq **Temp; + + CTEStructAssert(FreedReq, tcr); + + Temp = (TCPConnReq **)&FreedReq->tcr_req.tr_q.q_next; + *Temp = ConnReqFree; + ConnReqFree = FreedReq; + +#endif // NT +} + +//* GetConnReq - Get a connection request structure. +// +// Called to get a connection request structure. +// +// Input: Nothing. +// +// Returns: Pointer to ConnReq structure, or NULL if none. +// +TCPConnReq * +GetConnReq(void) +{ + TCPConnReq *Temp; + +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + Queue *QueuePtr; + TCPReq *ReqPtr; + + BufferLink = ExInterlockedPopEntrySList( + &ConnReqFree, + &ConnReqFreeLock + ); + + if (BufferLink != NULL) { + QueuePtr = STRUCT_OF(Queue, BufferLink, q_next); + ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q); + Temp = STRUCT_OF(TCPConnReq, ReqPtr, tcr_req); + CTEStructAssert(Temp, tcr); + } + else { + if (NumConnReq < MaxConnReq) + Temp = CTEAllocMem(sizeof(TCPConnReq)); + else + Temp = NULL; + + if (Temp != NULL) { + ExInterlockedAddUlong(&NumConnReq, 1, &ConnReqFreeLock); +#ifdef DEBUG + Temp->tcr_req.tr_sig = tr_signature; + Temp->tcr_sig = tcr_signature; +#endif + } + } + +#else // NT + + Temp = ConnReqFree; + if (Temp != NULL) + ConnReqFree = (TCPConnReq *)Temp->tcr_req.tr_q.q_next; + else { + if (NumConnReq < MaxConnReq) + Temp = CTEAllocMem(sizeof(TCPConnReq)); + else + Temp = NULL; + + if (Temp != NULL) { + NumConnReq++; +#ifdef DEBUG + Temp->tcr_req.tr_sig = tr_signature; + Temp->tcr_sig = tcr_signature; +#endif + } + } + +#endif // NT + + return Temp; +} + +//* GetConnFromConnID - Get a Connection from a connection ID. +// +// Called to obtain a Connection pointer from a ConnID. We don't actually +// check the connection pointer here, but we do bounds check the input ConnID +// and make sure the instance fields match. +// We assume the caller has taken the ConnTable lock. +// +// Input: ConnID - Connection ID to find a pointer for. +// +// Returns: Pointer to the TCPConn, or NULL. +// +TCPConn * +GetConnFromConnID(uint ConnID) +{ + uint ConnIndex = CONN_INDEX(ConnID); + TCPConn *MatchingConn; + + if (ConnIndex < ConnTableSize) { + MatchingConn = (*ConnTable)[ConnIndex]; + if (MatchingConn != NULL) { + CTEStructAssert(MatchingConn, tc); + if (MatchingConn->tc_inst != CONN_INST(ConnID)) + MatchingConn = NULL; + } + } else + MatchingConn = NULL; + + return MatchingConn; + + +} + +//* GetConnID - Get a ConnTable slot. +// +// Called during OpenConnection to find a free slot in the ConnTable and +// set it up with a connection. We assume the caller holds the lock on the +// TCB ConnTable when we are called. +// +// Input: NewConn - Connection to enter into slot.. +// +// Returns: A ConnId to use. +// +uint +GetConnID(TCPConn *NewConn) +{ + uint CurrConnID; + uint i; // Index variable. + + // Keep doing this until it works. + for (;;) { + CurrConnID = NextConnIndex; + + for (i = 0; i < ConnTableSize; i++ ) { + if (CurrConnID == ConnTableSize) + CurrConnID = 0; // Wrapped, start at 0. + + if ((*ConnTable)[CurrConnID] == NULL) + break; // Found a free one. + + ++CurrConnID; + } + + if (i < ConnTableSize) { + // We found a free slot. + (*ConnTable)[CurrConnID] = NewConn; + NextConnIndex = CurrConnID + 1; + ConnInst++; + NewConn->tc_inst = ConnInst; + return MAKE_CONN_ID(CurrConnID, ConnInst); + } + + // Didn't find a free slot. Grow the table. + if (ConnTableSize != MaxConnections) { + uint NewTableSize; + TCPConnTable *NewTable; + + NewTableSize = MIN(ConnTableSize + GROW_DELTA, MaxConnections); + NewTable = CTEAllocMem(NewTableSize * sizeof(TCPConn *)); + if (NewTable != NULL) { + TCPConnTable *OldTable; + // We allocated it. Copy the old table in, and update ptrs and + // size. + CTEMemSet(NewTable, 0, NewTableSize * sizeof(TCPConn *)); + CTEMemCopy(NewTable, ConnTable, ConnTableSize * + (sizeof (TCPConn *))); + OldTable = ConnTable; + ConnTable = NewTable; + ConnTableSize = NewTableSize; + if (OldTable != NULL) + CTEFreeMem(OldTable); + // Try it again, from the top. + continue; + } else { + // Couldn't grow the table. + return INVALID_CONN_ID; + } + + } else { + // Table's already at the maximum allowable size. + return INVALID_CONN_ID; + } + } + + +} + +//* FreeConnID - Free a ConnTable slot. +// +// Called when we're done with a ConnID. We assume the caller holds the lock +// on the TCB ConnTable when we are called. +// +// Input: ConnID - Connection ID to be freed. +// +// Returns: Nothing. +// +void +FreeConnID(uint ConnID) +{ + uint Index = CONN_INDEX(ConnID); // Index into conn table. + + CTEAssert(Index < ConnTableSize); + CTEAssert((*ConnTable)[Index] != NULL); + CTEStructAssert((*ConnTable)[Index], tc); + + FREE_CONN_INDEX(Index); + +} + +//* MapIPError - Map an IP error to a TDI error. +// +// Called to map an input IP error code to a TDI error code. If we can't, +// we return the provided default. +// +// Input: IPError - Error code to be mapped. +// Default - Default error code to return. +// +// Returns: Mapped TDI error. +// +TDI_STATUS +MapIPError(IP_STATUS IPError, TDI_STATUS Default) +{ + switch (IPError) { + + case IP_DEST_NET_UNREACHABLE: + return TDI_DEST_NET_UNREACH; + case IP_DEST_HOST_UNREACHABLE: + return TDI_DEST_HOST_UNREACH; + case IP_DEST_PROT_UNREACHABLE: + return TDI_DEST_PROT_UNREACH; + case IP_DEST_PORT_UNREACHABLE: + return TDI_DEST_PORT_UNREACH; + default: + return Default; + } +} + +//* FinishRemoveTCBFromConn - Finish removing a TCB from a conn structure. +// +// Called when we have the locks we need and we just want to pull the +// TCB off the connection. The caller must hold the ConnTableLock before +// calling this. +// +// Input: RemovedTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +FinishRemoveTCBFromConn(TCB *RemovedTCB) +{ + TCPConn *Conn; + CTELockHandle AOHandle, TCBHandle; + AddrObj *AO; + + if ((( Conn = RemovedTCB->tcb_conn ) != NULL ) && + ( Conn->tc_tcb == RemovedTCB ) ) { + CTEStructAssert(Conn, tc); + + AO = Conn->tc_ao; + + if (AO != NULL) { + CTEGetLockAtDPC(&AO->ao_lock, &AOHandle); + CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &TCBHandle); + + // Need to double check this is still correct. + + if (Conn == RemovedTCB->tcb_conn) { + // Everything still looks good. + REMOVEQ(&Conn->tc_q); + ENQUEUE(&AO->ao_idleq, &Conn->tc_q); + } else + Conn = RemovedTCB->tcb_conn; + + CTEFreeLockFromDPC(&AO->ao_lock, TCBHandle); + } else { + CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &AOHandle); + Conn = RemovedTCB->tcb_conn; + } + + if (Conn != NULL) { + if (Conn->tc_tcb == RemovedTCB) + Conn->tc_tcb = NULL; + else + CTEAssert(Conn->tc_tcb == NULL); + } + + CTEFreeLockFromDPC(&RemovedTCB->tcb_lock, AOHandle); + } +} + +//* RemoveTCBFromConn - Remove a TCB from a Conn structure. +// +// Called when we need to disassociate a TCB from a connection structure. +// All we do is get the appropriate locks and call FinishRemoveTCBFromConn. +// +// Input: RemovedTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +RemoveTCBFromConn(TCB *RemovedTCB) +{ + CTELockHandle ConnHandle, TCBHandle; + + CTEStructAssert(RemovedTCB, tcb); + + CTEGetLock(&ConnTableLock, &ConnHandle); + + FinishRemoveTCBFromConn(RemovedTCB); + + CTEFreeLock(&ConnTableLock, ConnHandle); + +} + +//* RemoveConnFromTCB - Remove a conn from a TCB. +// +// Called when we want to break the final association between a connection +// and a TCB. +// +// Input: RemoveTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +RemoveConnFromTCB(TCB *RemoveTCB) +{ + ConnDoneRtn DoneRtn = NULL; + CTELockHandle ConnHandle, TCBHandle; + TCPConn *Conn; + + CTEGetLock(&ConnTableLock, &ConnHandle); + CTEGetLock(&RemoveTCB->tcb_lock, &TCBHandle); + + + if ((Conn = RemoveTCB->tcb_conn) != NULL) { + + CTEStructAssert(Conn, tc); + + if (--(Conn->tc_refcnt) == 0) + DoneRtn = Conn->tc_donertn; + + RemoveTCB->tcb_conn = NULL; + } + + CTEFreeLock(&RemoveTCB->tcb_lock, TCBHandle); + + if (DoneRtn != NULL) + (*DoneRtn)(Conn, ConnHandle); + else + CTEFreeLock(&ConnTableLock, ConnHandle); +} + + +//* CloseTCB - Close a TCB. +// +// Called when we are done with a TCB, and want to free it. We'll remove +// him from any tables that he's in, and destroy any outstanding requests. +// +// Input: ClosedTCB - TCB to be closed. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +CloseTCB(TCB *ClosedTCB, CTELockHandle Handle) +{ + CTELockHandle ConnTableHandle, TCBTableHandle; + uchar OrigState = ClosedTCB->tcb_state; + TDI_STATUS Status; + uint OKToFree; + + + CTEStructAssert(ClosedTCB, tcb); + CTEAssert(ClosedTCB->tcb_refcnt == 0); + CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED); + CTEAssert(ClosedTCB->tcb_pending & DEL_PENDING); + + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + + // We need to get the ConnTable, TCBTable, and TCB locks to pull + // this guy from all the appropriate tables. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + // We'll check to make sure that our state isn't CLOSED. This should never + // happen, since nobody should call TryToCloseTCB when the state is + // closed, or take the reference count if we're closing. Nevertheless, + // we'll double check as a safety measure. + if (ClosedTCB->tcb_state == TCB_CLOSED) { + DEBUGCHK; + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return; + } + + // Update SNMP counters. If we're in SYN-SENT or SYN-RCVD, this is a failed + // connection attempt. If we're in ESTABLISED or CLOSE-WAIT, treat this + // as an 'Established Reset' event. + if (ClosedTCB->tcb_state == TCB_SYN_SENT || + ClosedTCB->tcb_state == TCB_SYN_RCVD) + TStats.ts_attemptfails++; + else + if (ClosedTCB->tcb_state == TCB_ESTAB || + ClosedTCB->tcb_state == TCB_CLOSE_WAIT) { + TStats.ts_estabresets++; + TStats.ts_currestab--; + CTEAssert(*(int *)&TStats.ts_currestab >= 0); + } + + ClosedTCB->tcb_state = TCB_CLOSED; + + + // Remove the TCB from it's associated TCPConn structure, if it has one. + FinishRemoveTCBFromConn(ClosedTCB); + + CTEGetLockAtDPC(&TCBTableLock, &TCBTableHandle); + CTEGetLockAtDPC(&ClosedTCB->tcb_lock, &Handle); + + OKToFree = RemoveTCB(ClosedTCB); + + // He's been pulled from the appropriate places so nobody can find him. + // Free the locks, and proceed to destroy any requests, etc. + CTEFreeLockFromDPC(&ClosedTCB->tcb_lock, Handle); + CTEFreeLockFromDPC(&TCBTableLock, TCBTableHandle); + CTEFreeLock(&ConnTableLock, ConnTableHandle); + + if (SYNC_STATE(OrigState) && !GRACEFUL_CLOSED_STATE(OrigState)) { + if (ClosedTCB->tcb_flags & NEED_RST) + SendRSTFromTCB(ClosedTCB); + } + + (*LocalNetInfo.ipi_freeopts)(&ClosedTCB->tcb_opt); + (*LocalNetInfo.ipi_closerce)(ClosedTCB->tcb_rce); + + if (ClosedTCB->tcb_closereason & TCB_CLOSE_RST) + Status = TDI_CONNECTION_RESET; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_ABORTED) + Status = TDI_CONNECTION_ABORTED; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_TIMEOUT) + Status = MapIPError(ClosedTCB->tcb_error, TDI_TIMED_OUT); + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_REFUSED) + Status = TDI_CONN_REFUSED; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_UNREACH) + Status = MapIPError(ClosedTCB->tcb_error, TDI_DEST_UNREACHABLE); + else + Status = TDI_SUCCESS; + + // Now complete any outstanding requests on the TCB. + if (ClosedTCB->tcb_connreq != NULL) { + TCPConnReq *ConnReq = ClosedTCB->tcb_connreq; + CTEStructAssert(ConnReq, tcr); + + (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0); + FreeConnReq(ConnReq); + } + + if (ClosedTCB->tcb_discwait != NULL) { + TCPConnReq *ConnReq = ClosedTCB->tcb_discwait; + CTEStructAssert(ConnReq, tcr); + + (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0); + FreeConnReq(ConnReq); + } + + while (!EMPTYQ(&ClosedTCB->tcb_sendq)) { + TCPReq *Req; + TCPSendReq *SendReq; + long Result; + + DEQUEUE(&ClosedTCB->tcb_sendq, Req, TCPReq, tr_q); + + CTEStructAssert(Req, tr); + SendReq = (TCPSendReq *)Req; + CTEStructAssert(SendReq, tsr); + + // Decrement the initial reference put on the buffer when it was + // allocated. This reference would have been decremented if the + // send had been acknowledged, but then the send would not still + // be on the tcb_sendq. + Result = CTEInterlockedDecrementLong( + &(SendReq->tsr_refcnt) + ); + + CTEAssert(Result >= 0); + + if (Result <= 0) { + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (SendReq->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(SendReq->tsr_lastbuf) = NULL; + SendReq->tsr_lastbuf = NULL; + } + + (*Req->tr_rtn)(Req->tr_context, Status, 0); + FreeSendReq(SendReq); + } else { + // The send request will be freed when all outstanding references + // to it have completed. + SendReq->tsr_req.tr_status = Status; + } + } + + while (ClosedTCB->tcb_rcvhead != NULL) { + TCPRcvReq *RcvReq; + + RcvReq = ClosedTCB->tcb_rcvhead; + CTEStructAssert(RcvReq, trr); + ClosedTCB->tcb_rcvhead = RcvReq->trr_next; + (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0); + FreeRcvReq(RcvReq); + } + + while (ClosedTCB->tcb_exprcv != NULL) { + TCPRcvReq *RcvReq; + + RcvReq = ClosedTCB->tcb_exprcv; + CTEStructAssert(RcvReq, trr); + ClosedTCB->tcb_exprcv = RcvReq->trr_next; + (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0); + FreeRcvReq(RcvReq); + } + + if (ClosedTCB->tcb_pendhead != NULL) + FreeRBChain(ClosedTCB->tcb_pendhead); + + if (ClosedTCB->tcb_urgpending != NULL) + FreeRBChain(ClosedTCB->tcb_urgpending); + + while (ClosedTCB->tcb_raq != NULL) { + TCPRAHdr *Hdr; + + Hdr = ClosedTCB->tcb_raq; + CTEStructAssert(Hdr, trh); + ClosedTCB->tcb_raq = Hdr->trh_next; + if (Hdr->trh_buffer != NULL) + FreeRBChain(Hdr->trh_buffer); + + CTEFreeMem(Hdr); + } + + RemoveConnFromTCB(ClosedTCB); + + if (OKToFree) { + FreeTCB(ClosedTCB); + } else { + CTEGetLock(&TCBTableLock, &TCBTableHandle); + ClosedTCB->tcb_walkcount--; + if (ClosedTCB->tcb_walkcount == 0) { + FreeTCB(ClosedTCB); + } + CTEFreeLock(&TCBTableLock, TCBTableHandle); + } + +} + +//* TryToCloseTCB - Try to close a TCB. +// +// Called when we need to close a TCB, but don't know if we can. If +// the reference count is 0, we'll call CloseTCB to deal with it. +// Otherwise we'll set the DELETE_PENDING bit and deal with it when +// the ref. count goes to 0. We assume the TCB is locked when we are called. +// +// Input: ClosedTCB - TCB to be closed. +// Reason - Reason we're closing. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +TryToCloseTCB(TCB *ClosedTCB, uchar Reason, CTELockHandle Handle) +{ + CTEStructAssert(ClosedTCB, tcb); + CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED); + + ClosedTCB->tcb_closereason |= Reason; + + if (ClosedTCB->tcb_pending & DEL_PENDING) { + DEBUGCHK; + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + return; + } + + ClosedTCB->tcb_pending |= DEL_PENDING; + ClosedTCB->tcb_slowcount++; + ClosedTCB->tcb_fastchk |= TCP_FLAG_SLOW; + + if (ClosedTCB->tcb_refcnt == 0) + CloseTCB(ClosedTCB, Handle); + else { + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + } +} + + +//* DerefTCB - Dereference a TCB. +// +// Called when we're done with a TCB, and want to let exclusive user +// have a shot. We dec. the refcount, and if it goes to zero and there +// are pending actions, we'll perform one of the pending actions. +// +// Input: DoneTCB - TCB to be dereffed. +// Handle - Lock handle to be used when freeing TCB lock. +// +// Returns: Nothing. +// +void +DerefTCB(TCB *DoneTCB, CTELockHandle Handle) +{ + + CTEAssert(DoneTCB->tcb_refcnt != 0); + if (--DoneTCB->tcb_refcnt == 0) { + if (DoneTCB->tcb_pending == 0) { + CTEFreeLock(&DoneTCB->tcb_lock, Handle); + return; + } else { + // BUGBUG handle pending actions. + if (DoneTCB->tcb_pending & DEL_PENDING) + CloseTCB(DoneTCB, Handle); + else + DEBUGCHK; + return; + } + } + + CTEFreeLock(&DoneTCB->tcb_lock, Handle); + return; +} + + +//** TdiOpenConnection - Open a connection. +// +// This is the TDI Open Connection entry point. We open a connection, +// and save the caller's connection context. A TCPConn structure is allocated +// here, but a TCB isn't allocated until the Connect or Listen is done. +// +// Input: Request - Pointed to a TDI request structure. +// Context - Connection context to be saved for connection. +// +// Returns: Status of attempt to open the connection. +// +TDI_STATUS +TdiOpenConnection(PTDI_REQUEST Request, PVOID Context) +{ + TCPConn *NewConn; // The newly opened connection. + CTELockHandle Handle; // Lock handle for TCPConnTable. + uint ConnID; // New ConnID. + TDI_STATUS Status; // Status of this request. + + NewConn = CTEAllocMem(sizeof(TCPConn)); + + if (NewConn != NULL) { // We allocated a connection. + CTEMemSet(NewConn, 0, sizeof(TCPConn)); +#ifdef DEBUG + NewConn->tc_sig = tc_signature; +#endif + NewConn->tc_tcb = NULL; + NewConn->tc_ao = NULL; + NewConn->tc_context = Context; + + CTEGetLock(&ConnTableLock, &Handle); + ConnID = GetConnID(NewConn); + if (ConnID != INVALID_CONN_ID) { + // We successfully got a ConnID. + Request->Handle.ConnectionContext = (CONNECTION_CONTEXT)ConnID; + NewConn->tc_refcnt = 0; + NewConn->tc_flags = 0; + NewConn->tc_tcbflags = NAGLING | (BSDUrgent ? BSD_URGENT : 0); + if (DefaultRcvWin != 0) { + NewConn->tc_window = DefaultRcvWin; + NewConn->tc_flags |= CONN_WINSET; + } else + NewConn->tc_window = DEFAULT_RCV_WIN; + + NewConn->tc_donertn = DummyDone; + Status = TDI_SUCCESS; + } else { + CTEFreeMem(NewConn); + Status = TDI_NO_RESOURCES; + } + + CTEFreeLock(&ConnTableLock, Handle); + return Status; + } + + // Couldn't get a connection. + return TDI_NO_RESOURCES; + +} + +//* RemoveConnFromAO - Remove a connection from an AddrObj. +// +// A little utility routine to remove a connection from an AddrObj. +// We run down the connections on the AO, and when we find him we splice +// him out. We assume the caller holds the locks on the AddrObj and the +// TCPConnTable lock. +// +// Input: AO - AddrObj to remove from. +// Conn - Conn to remove. +// +// Returns: Nothing. +// +void +RemoveConnFromAO(AddrObj *AO, TCPConn *Conn) +{ + + CTEStructAssert(AO, ao); + CTEStructAssert(Conn, tc); + + REMOVEQ(&Conn->tc_q); + Conn->tc_ao = NULL; + + +} + +//* TdiCloseConnection - Close a connection. +// +// Called when the user is done with a connection, and wants to close it. +// We look the connection up in our table, and if we find it we'll remove +// the connection from the AddrObj it's associate with (if any). If there's +// a TCB associated with the connection we'll close it also. +// +// There are some interesting wrinkles related to closing while a TCB +// is still referencing the connection (i.e. tc_refcnt != 0) or while a +// disassociate address is in progress. See below for more details. +// +// Input: Request - Request identifying connection to be closed. +// +// Returns: Status of attempt to close. +// +TDI_STATUS +TdiCloseConnection(PTDI_REQUEST Request) +{ + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle TableHandle; + TCPConn *Conn; + TDI_STATUS Status; + + CTEGetLock(&ConnTableLock, &TableHandle); + + // We have the locks we need. Try to find a connection. + Conn = GetConnFromConnID(ConnID); + + if (Conn != NULL) { + CTELockHandle TCBHandle; + TCB *ConnTCB; + + // We found the connection. Free the ConnID and mark the connection + // as closing. + + CTEStructAssert(Conn, tc); + + FreeConnID(ConnID); + + Conn->tc_flags |= CONN_CLOSING; + + // See if there's a TCB referencing this connection. + // If there is, we'll need to wait until he's done before closing him. + // We'll hurry the process along if we still have a pointer to him. + + if (Conn->tc_refcnt != 0) { + CTEReqCmpltRtn Rtn; + PVOID Context; + + // A connection still references him. Save the current rtn stuff + // in case we are in the middle of disassociating him from an + // address, and store the caller's callback routine and our done + // routine. + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + + Conn->tc_rtn = Request->RequestNotifyObject; + Conn->tc_rtncontext = Request->RequestContext; + Conn->tc_donertn = CloseDone; + + // See if we're in the middle of disassociating him + if (Conn->tc_flags & CONN_DISACC) { + + // We are disassociating him. We'll free the conn table lock + // now and fail the disassociate request. Note that when + // we free the lock the refcount could go to zero. This is + // OK, because we've already stored the neccessary info. in + // the connection so the caller will get called back if it + // does. From this point out we return PENDING, so a callback + // is OK. We've marked him as closing, so the disassoc done + // routine will bail out if we've interrupted him. If the ref. + // count does go to zero, Conn->tc_tcb would have to be NULL, + // so in that case we'll just fall out of this routine. + + CTEFreeLock(&ConnTableLock, TableHandle); + (*Rtn)(Context, (uint) TDI_REQ_ABORTED, 0); + CTEGetLock(&ConnTableLock, &TableHandle); + } + + + ConnTCB = Conn->tc_tcb; + if (ConnTCB != NULL) { + CTEStructAssert(ConnTCB, tcb); + // We have a TCB. Take the lock on him and get ready to + // close him. + CTEGetLock(&ConnTCB->tcb_lock, &TCBHandle); + if (ConnTCB->tcb_state != TCB_CLOSED) { + ConnTCB->tcb_flags |= NEED_RST; + CTEFreeLock(&ConnTableLock, TCBHandle); + if (!CLOSING(ConnTCB)) + TryToCloseTCB(ConnTCB, TCB_CLOSE_ABORTED, TableHandle); + else + CTEFreeLock(&ConnTCB->tcb_lock, TableHandle); + return TDI_PENDING; + } else { + // He's already closing. This should be harmless, but check + // this case. + DEBUGCHK; + CTEFreeLock(&ConnTCB->tcb_lock, TCBHandle); + } + } + Status = TDI_PENDING; + + } else { + // We have a connection that we can close. Finish the close. + Conn->tc_rtn = DummyCmplt; + CloseDone(Conn, TableHandle); + return TDI_SUCCESS; + } + + + } else + Status = TDI_INVALID_CONNECTION; + + // We're done with the connection. Go ahead and free him. + CTEFreeLock(&ConnTableLock, TableHandle); + + return Status; + +} + + +//* TdiAssociateAddress - Associate an address with a connection. +// +// Called to associate an address with a connection. We do a minimal +// amount of sanity checking, and then put the connection on the AddrObj's +// list. +// +// Input: Request - Pointer to request structure for this request. +// AddrHandle - Address handle to associate connection with. +// +// Returns: Status of attempt to associate. +// +TDI_STATUS +TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle) +{ + CTELockHandle TableHandle, AOHandle; + AddrObj *AO; + uint ConnID = (uint)Request->Handle.ConnectionContext; + TCPConn *Conn; + TDI_STATUS Status; + +#ifdef VXD + AO = GetIndexedAO((uint)AddrHandle); + if (AO == NULL) + return TDI_ADDR_INVALID; +#else + AO = (AddrObj *)AddrHandle; +#endif + CTEStructAssert(AO, ao); + + CTEGetLock(&ConnTableLock, &TableHandle); + CTEGetLock(&AO->ao_lock, &AOHandle); + + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + if (Conn->tc_ao != NULL) { + // It's already associated. Error out. + DEBUGCHK; + Status = TDI_ALREADY_ASSOCIATED; + } else { + Conn->tc_ao = AO; + CTEAssert(Conn->tc_tcb == NULL); + ENQUEUE(&AO->ao_idleq, &Conn->tc_q); + Status = TDI_SUCCESS; + } + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&AO->ao_lock, AOHandle); + CTEFreeLock(&ConnTableLock, TableHandle); + return Status; +} + +//* TdiDisAssociateAddress - Disassociate a connection from an address. +// +// The TDI entry point to disassociate a connection from an address. The +// connection must actually be associated and not connected to anything. +// +// Input: Request - Pointer to the request structure for this +// command. +// +// Returns: Status of request. +// +TDI_STATUS +TdiDisAssociateAddress(PTDI_REQUEST Request) +{ + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + TCPConn *Conn; + AddrObj *AO; + TDI_STATUS Status; + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + + if (Conn != NULL) { + // The connection actually exists! + + CTEStructAssert(Conn, tc); + AO = Conn->tc_ao; + if (AO != NULL) { + CTEStructAssert(AO, ao); + // And it's associated. + CTEGetLock(&AO->ao_lock, &AOHandle); + // If there's no connection currently active, go ahead and remove + // him from the AddrObj. If a connection is active error the + // request out. + if (Conn->tc_tcb == NULL) { + if (Conn->tc_refcnt == 0) { + RemoveConnFromAO(AO, Conn); + Status = TDI_SUCCESS; + } else { + // He shouldn't be closing, or we couldn't have found him. + CTEAssert(!(Conn->tc_flags & CONN_CLOSING)); + + Conn->tc_rtn = Request->RequestNotifyObject; + Conn->tc_rtncontext = Request->RequestContext; + Conn->tc_donertn = DisassocDone; + Conn->tc_flags |= CONN_DISACC; + Status = TDI_PENDING; + } + + } else + Status = TDI_CONNECTION_ACTIVE; + CTEFreeLock(&AO->ao_lock, AOHandle); + } else + Status = TDI_NOT_ASSOCIATED; + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + return Status; + +} + +//* ProcessUserOptions - Process options from the user. +// +// A utility routine to process options from the user. We fill in the +// optinfo structure, and if we have options we call ip to check on them. +// +// Input: Info - Info structure containing options to be processed. +// OptInfo - Info structure to be filled in. +// +// Returns: TDI_STATUS of attempt. +// +TDI_STATUS +ProcessUserOptions(PTDI_CONNECTION_INFORMATION Info, IPOptInfo *OptInfo) +{ + TDI_STATUS Status; + + (*LocalNetInfo.ipi_initopts)(OptInfo); + + if (Info != NULL && Info->Options != NULL) { + IP_STATUS OptStatus; + + OptStatus = (*LocalNetInfo.ipi_copyopts)(Info->Options, + Info->OptionsLength, OptInfo); + if (OptStatus != IP_SUCCESS) { + if (OptStatus == IP_NO_RESOURCES) + Status = TDI_NO_RESOURCES; + else + Status = TDI_BAD_OPTION; + } else + Status = TDI_SUCCESS; + } else { + Status = TDI_SUCCESS; + } + + return Status; + + +} + +//* InitTCBFromConn - Initialize a TCB from information in a Connection. +// +// Called from Connect and Listen processing to initialize a new TCB from +// information in the connection. We assume the AddrObjTableLock and +// ConnTableLocks are held when we are called, or that the caller has some +// other way of making sure that the referenced AO doesn't go away in the middle +// of operation. +// +// Input: Conn - Connection to initialize from. +// NewTCB - TCB to be initialized. +// Addr - Remote addressing and option info for NewTCB. +// AOLocked - True if the called has the address object locked. +// +// Returns: TDI_STATUS of init attempt. +// +TDI_STATUS +InitTCBFromConn(TCPConn *Conn, TCB *NewTCB, + PTDI_CONNECTION_INFORMATION Addr, uint AOLocked) +{ + CTELockHandle AOHandle; + TDI_STATUS Status; + + CTEStructAssert(Conn, tc); + + // We have a connection. Make sure it's associated with an address and + // doesn't already have a TCB attached. + + if (Conn->tc_flags & CONN_INVALID) + return TDI_INVALID_CONNECTION; + + if (Conn->tc_tcb == NULL) { + AddrObj *ConnAO; + + ConnAO = Conn->tc_ao; + if (ConnAO != NULL) { + CTEStructAssert(ConnAO, ao); + + if (!AOLocked) { + CTEGetLock(&ConnAO->ao_lock, &AOHandle); + } + NewTCB->tcb_saddr = ConnAO->ao_addr; + NewTCB->tcb_sport = ConnAO->ao_port; + NewTCB->tcb_rcvind = ConnAO->ao_rcv; + NewTCB->tcb_ricontext = ConnAO->ao_rcvcontext; + if (NewTCB->tcb_rcvind == NULL) + NewTCB->tcb_rcvhndlr = PendData; + else + NewTCB->tcb_rcvhndlr = IndicateData; + + NewTCB->tcb_conncontext = Conn->tc_context; + NewTCB->tcb_flags |= Conn->tc_tcbflags; + NewTCB->tcb_defaultwin = Conn->tc_window; + NewTCB->tcb_rcvwin = Conn->tc_window; + + if (Conn->tc_flags & CONN_WINSET) + NewTCB->tcb_flags |= WINDOW_SET; + + if (NewTCB->tcb_flags & KEEPALIVE) { + NewTCB->tcb_alive = TCPTime; + NewTCB->tcb_kacount = 0; + } + + if (!AOLocked) { + CTEFreeLock(&ConnAO->ao_lock, AOHandle); + } + + // If we've been given options, we need to process them now. + if (Addr != NULL && Addr->Options != NULL) + NewTCB->tcb_flags |= CLIENT_OPTIONS; + Status = ProcessUserOptions(Addr, &NewTCB->tcb_opt); + + return Status; + } else + return TDI_NOT_ASSOCIATED; + } else + return TDI_CONNECTION_ACTIVE; + +} + +//* TdiConnect - Establish a connection. +// +// The TDI connection establishment routine. Called when the client wants to +// establish a connection, we validate his incoming parameters and kick +// things off by sending a SYN. +// +// Input: Request - The request structure for this command. +// Timeout - How long to wait for the request. The format +// of this time is system specific - we use +// a macro to convert to ticks. +// RequestAddr - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing the destination. +// ReturnAddr - Pointer to where to return information. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiConnect(PTDI_REQUEST Request, void *TO, + PTDI_CONNECTION_INFORMATION RequestAddr, + PTDI_CONNECTION_INFORMATION ReturnAddr) +{ + TCPConnReq *ConnReq; // Connection request to use. + IPAddr DestAddr; + ushort DestPort; + uchar AddrType; + TCPConn *Conn; + TCB *NewTCB; + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + AddrObj *AO; + TDI_STATUS Status; + CTELockHandle TCBHandle; + IPAddr SrcAddr; + ushort MSS; + TCP_TIME *Timeout; + + // First, get and validate the remote address. + if (RequestAddr == NULL || RequestAddr->RemoteAddress == NULL || + !GetAddress((PTRANSPORT_ADDRESS)RequestAddr->RemoteAddress, &DestAddr, + &DestPort)) + return TDI_BAD_ADDR; + + AddrType = (*LocalNetInfo.ipi_getaddrtype)(DestAddr); + + if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType) || DestPort == 0) + return TDI_BAD_ADDR; + + // Now get a connection request. If we can't, bail out now. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; + + // Get a TCB, assuming we'll need one. + NewTCB = AllocTCB(); + if (NewTCB == NULL) { + // Couldn't get a TCB. + FreeConnReq(ConnReq); + return TDI_NO_RESOURCES; + } + + Timeout = (TCP_TIME *)TO; + + if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) { + ulong Ticks = TCP_TIME_TO_TICKS(*Timeout); + if (Ticks > MAX_CONN_TO_TICKS) + Ticks = MAX_CONN_TO_TICKS; + else + Ticks++; + ConnReq->tcr_timeout = (ushort)Ticks; + } else + ConnReq->tcr_timeout = 0; + + ConnReq->tcr_flags = 0; + ConnReq->tcr_conninfo = ReturnAddr; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + NewTCB->tcb_daddr = DestAddr; + NewTCB->tcb_dport = DestPort; + + // Now find the real connection. If we find it, we'll make sure it's + // associated. + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + uint Inserted; + + CTEStructAssert(Conn, tc); + + AO = Conn->tc_ao; + + if (AO != NULL) { + CTEGetLock(&AO->ao_lock, &AOHandle); + + CTEStructAssert(AO, ao); + Status = InitTCBFromConn(Conn, NewTCB, RequestAddr, TRUE); + if (Status == TDI_SUCCESS) { + + // We've processed the options, and we know the destination + // address is good, and we have all the resources we need, + // so we can go ahead and open an RCE. If this works we'll + // put the TCB into the Connection and send a SYN. + + // We're done with the AddrObjTable now, so we can free it's + // lock. + NewTCB->tcb_flags |= ACTIVE_OPEN; + + CTEFreeLock(&AddrObjTableLock, AOHandle); + + SrcAddr = (*LocalNetInfo.ipi_openrce)(DestAddr, + NewTCB->tcb_saddr, &NewTCB->tcb_rce, &AddrType, &MSS, + &NewTCB->tcb_opt); + + if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) { + // The request failed. We know the destination is good + // (we verified it above), so it must be unreachable. + CTEFreeLock(&AO->ao_lock, ConnTableHandle); + Status = TDI_DEST_UNREACHABLE; + goto error; + } + + // OK, the RCE open worked. Enter the TCB into the connection. + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + Conn->tc_tcb = NewTCB; + Conn->tc_refcnt++; + NewTCB->tcb_conn = Conn; + REMOVEQ(&Conn->tc_q); + ENQUEUE(&AO->ao_activeq, &Conn->tc_q); + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEFreeLock(&AO->ao_lock, ConnTableHandle); + + + // If the caller didn't specify a local address, use what + // IP provided. + if (IP_ADDR_EQUAL(NewTCB->tcb_saddr, NULL_IP_ADDR)) + NewTCB->tcb_saddr = SrcAddr; + + // Until we have MTU discovery in place, hold the MSS down + // to 536 if we're going off net. + MSS -= sizeof(TCPHeader); + + if (!PMTUDiscovery && IS_OFFNET_DEST(AddrType)) { + NewTCB->tcb_mss = MIN(MSS, MAX_REMOTE_MSS) - + NewTCB->tcb_opt.ioi_optlength; + + CTEAssert(NewTCB->tcb_mss > 0); + } + else { + if (PMTUDiscovery) + NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF; + NewTCB->tcb_mss = MSS - NewTCB->tcb_opt.ioi_optlength; + + CTEAssert(NewTCB->tcb_mss > 0); + } + + // + // Initialize the remote mss in case we receive an MTU change + // from IP before the remote SYN arrives. The remmms will + // be replaced when the remote SYN is processed. + // + NewTCB->tcb_remmss = NewTCB->tcb_mss; + + // Now initialize our send state. + InitSendState(NewTCB); + NewTCB->tcb_refcnt = 1; + NewTCB->tcb_state = TCB_SYN_SENT; + TStats.ts_activeopens++; + + // Need to put the ConnReq on the TCB now, in case the timer fires + // after we've inserted. + + NewTCB->tcb_connreq = ConnReq; + CTEFreeLock(&NewTCB->tcb_lock, AOTableHandle); + + Inserted = InsertTCB(NewTCB); + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + + if (!Inserted) { + // Insert failed. We must already have a connection. Pull + // the connreq from the TCB first, so we can return the correct + // error code for it. + NewTCB->tcb_connreq = NULL; + NewTCB->tcb_refcnt--; + TryToCloseTCB(NewTCB, TCB_CLOSE_ABORTED, TCBHandle); + FreeConnReq(ConnReq); + return TDI_ADDR_IN_USE; + } + + // If it's closing somehow, stop now. It can't have gone to + // closed, as we hold a reference on it. It could have gone + // to some other state (for example SYN-RCVD) so we need to + // check that now too. + if (!CLOSING(NewTCB) && NewTCB->tcb_state == TCB_SYN_SENT) { + SendSYN(NewTCB, TCBHandle); + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + } + DerefTCB(NewTCB, TCBHandle); + + return TDI_PENDING; + } else + CTEFreeLock(&AO->ao_lock, AOHandle); + + } else + Status = TDI_NOT_ASSOCIATED; + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&AddrObjTableLock, ConnTableHandle); +error: + CTEFreeLock(&ConnTableLock, AOTableHandle); + FreeConnReq(ConnReq); + FreeTCB(NewTCB); + return Status; + + +} + +//* TdiListen - Listen for a connection. +// +// The TDI listen handling routine. Called when the client wants to +// post a listen, we validate his incoming parameters, allocate a TCB +// and return. +// +// Input: Request - The request structure for this command. +// Flags - Listen flags for the listen. +// AcceptableAddr - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing acceptable remote +// addresses. +// ConnectedAddr - Pointer to where to return information +// about the address we connected to. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiListen(PTDI_REQUEST Request, ushort Flags, + PTDI_CONNECTION_INFORMATION AcceptableAddr, + PTDI_CONNECTION_INFORMATION ConnectedAddr) +{ + TCPConnReq *ConnReq; // Connection request to use. + IPAddr RemoteAddr; // Remote address to take conn. from. + ushort RemotePort; // Acceptable remote port. + uchar AddrType; // Type of remote address. + TCPConn *Conn; // Pointer to the Connection being + // listened upon. + TCB *NewTCB; // Pointer to the new TCB we'll use. + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle; + TDI_STATUS Status; + + // If we've been given remote addressing criteria, check it out. + if (AcceptableAddr != NULL && AcceptableAddr->RemoteAddress != NULL) { + if (!GetAddress((PTRANSPORT_ADDRESS)AcceptableAddr->RemoteAddress, + &RemoteAddr, &RemotePort)) + return TDI_BAD_ADDR; + + if (!IP_ADDR_EQUAL(RemoteAddr, NULL_IP_ADDR)) { + AddrType = (*LocalNetInfo.ipi_getaddrtype)(RemoteAddr); + + if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType)) + return TDI_BAD_ADDR; + } + } else { + RemoteAddr = NULL_IP_ADDR; + RemotePort = 0; + } + + // The remote address is valid. Get a ConnReq, and maybe a TCB. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; // Couldn't get one. + + // Now try to get a TCB. + NewTCB = AllocTCB(); + if (NewTCB == NULL) { + // Couldn't get a TCB. Return an error. + FreeConnReq(ConnReq); + return TDI_NO_RESOURCES; + } + + // We have the resources we need. Initialize them, and then check the + // state of the connection. + ConnReq->tcr_flags = Flags; + ConnReq->tcr_conninfo = ConnectedAddr; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + NewTCB->tcb_connreq = ConnReq; + NewTCB->tcb_daddr = RemoteAddr; + NewTCB->tcb_dport = RemotePort; + NewTCB->tcb_state = TCB_LISTEN; + + // Now find the real connection. If we find it, we'll make sure it's + // associated. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTELockHandle AddrHandle; + AddrObj *ConnAO; + + CTEStructAssert(Conn, tc); + // We have a connection. Make sure it's associated with an address and + // doesn't already have a TCB attached. + ConnAO = Conn->tc_ao; + + if (ConnAO != NULL) { + CTEStructAssert(ConnAO, ao); + CTEGetLockAtDPC(&ConnAO->ao_lock, &AddrHandle); + + Status = InitTCBFromConn(Conn, NewTCB, AcceptableAddr, TRUE); + + if (Status == TDI_SUCCESS) { + + // The initialization worked. Assign the new TCB to the connection, + // and return. + + REMOVEQ(&Conn->tc_q); + ENQUEUE(&ConnAO->ao_listenq, &Conn->tc_q); + + Conn->tc_tcb = NewTCB; + NewTCB->tcb_conn = Conn; + Conn->tc_refcnt++; + + ConnAO->ao_listencnt++; + CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle); + + Status = TDI_PENDING; + } else { + FreeTCB(NewTCB); + CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle); + } + } else { + FreeTCB(NewTCB); + Status = TDI_NOT_ASSOCIATED; + } + } else { + FreeTCB(NewTCB); + Status = TDI_INVALID_CONNECTION; + } + + // We're all done. Free the locks and get out. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return Status; + +} + +//* InitRCE - Initialize an RCE. +// +// A utility routine to open and RCE and determine the maximum segment size +// for a connections. This function is called with the TCB lock held +// when transitioning out of the SYN_SENT or LISTEN states. +// +// Input: NewTCB - TCB for which an RCE is to be opened. +// +// Returns: Nothing. +// +void +InitRCE(TCB *NewTCB) +{ + uchar DType; + ushort MSS; + + // Open an RCE for this connection. + (*LocalNetInfo.ipi_openrce)(NewTCB->tcb_daddr, NewTCB->tcb_saddr, + &NewTCB->tcb_rce, &DType, &MSS, &NewTCB->tcb_opt); + + // Until we have Dynamic MTU discovery in place, force MTU down. + MSS -= sizeof(TCPHeader); + if (!PMTUDiscovery && (DType & DEST_OFFNET_BIT)) { + NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MIN(MSS, MAX_REMOTE_MSS) + - NewTCB->tcb_opt.ioi_optlength); + + CTEAssert(NewTCB->tcb_mss > 0); + } + else { + if (PMTUDiscovery) + NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF; + MSS -= NewTCB->tcb_opt.ioi_optlength; + NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MSS); + + CTEAssert(NewTCB->tcb_mss > 0); + + } + + +} + +//* AcceptConn - Accept a connection on a TCB. +// +// Called to accept a connection on a TCB, either from an incoming +// receive segment or via a user's accept. We initialize the RCE +// and the send state, and send out a SYN. We assume the TCB is locked +// and referenced when we get it. +// +// Input: AcceptTCB - TCB to accept on. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +AcceptConn(TCB *AcceptTCB, CTELockHandle Handle) +{ + CTEStructAssert(AcceptTCB, tcb); + CTEAssert(AcceptTCB->tcb_refcnt != 0); + + InitRCE(AcceptTCB); + InitSendState(AcceptTCB); + + AdjustRcvWin(AcceptTCB); + SendSYN(AcceptTCB, Handle); + + CTEGetLock(&AcceptTCB->tcb_lock, &Handle); + + DerefTCB(AcceptTCB, Handle); +} + +//* TdiAccept - Accept a connection. +// +// The TDI accept routine. Called when the client wants to +// accept a connection for which a listen had previously completed. We +// examine the state of the connection - it has to be in SYN-RCVD, with +// a TCB, with no pending connreq, etc. +// +// Input: Request - The request structure for this command. +// AcceptInfo - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing option information +// for this accept. +// ConnectedIndo - Pointer to where to return information +// about the address we connected to. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiAccept(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION AcceptInfo, + PTDI_CONNECTION_INFORMATION ConnectedInfo) +{ + TCPConnReq *ConnReq; // ConnReq we'll use for this connection. + uint ConnID = (uint)Request->Handle.ConnectionContext; + TCPConn *Conn; // Connection being accepted upon. + TCB *AcceptTCB; // TCB for Conn. + CTELockHandle ConnTableHandle; // Lock handle for connection table. + CTELockHandle TCBHandle; // Lock handle for TCB. + TDI_STATUS Status; + + // First, get the ConnReq we'll need. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; + + ConnReq->tcr_conninfo = ConnectedInfo; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + + // Now look up the connection. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + // We have the connection. Make sure is has a TCB, and that the + // TCB is in the SYN-RCVD state, etc. + AcceptTCB = Conn->tc_tcb; + + if (AcceptTCB != NULL) { + CTEStructAssert(AcceptTCB, tcb); + + CTEGetLock(&AcceptTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + + if (!CLOSING(AcceptTCB) && AcceptTCB->tcb_state == TCB_SYN_RCVD) { + // State is valid. Make sure this TCB had a delayed accept on + // it, and that there is currently no connect request pending. + if (!(AcceptTCB->tcb_flags & CONN_ACCEPTED) && + AcceptTCB->tcb_connreq == NULL) { + + // If the caller gave us options, they'll override any + // that are already present, if they're valid. + if (AcceptInfo != NULL && AcceptInfo->Options != NULL) { + IPOptInfo TempOptInfo; + + // We have options. Copy them to make sure they're valid. + Status = ProcessUserOptions(AcceptInfo, &TempOptInfo); + if (Status == TDI_SUCCESS) { + (*LocalNetInfo.ipi_freeopts)(&AcceptTCB->tcb_opt); + AcceptTCB->tcb_opt = TempOptInfo; + AcceptTCB->tcb_flags |= CLIENT_OPTIONS; + } else + goto connerror; + } + + AcceptTCB->tcb_connreq = ConnReq; + AcceptTCB->tcb_flags |= CONN_ACCEPTED; + AcceptTCB->tcb_refcnt++; + // Everything's set. Accept the connection now. + AcceptConn(AcceptTCB, ConnTableHandle); + return TDI_PENDING; + } + } +connerror: + CTEFreeLock(&AcceptTCB->tcb_lock, ConnTableHandle); + Status = TDI_INVALID_CONNECTION; + goto error; + } + } + Status = TDI_INVALID_CONNECTION; + CTEFreeLock(&ConnTableLock, ConnTableHandle); + +error: + FreeConnReq(ConnReq); + return Status; + +} + +//* TdiDisConnect - Disconnect a connection. +// +// The TDI disconnection routine. Called when the client wants to disconnect +// a connection. There are two types of disconnection we support, graceful +// and abortive. A graceful close will cause us to send a FIN and not complete +// the request until we get the ACK back. An abortive close causes us to send +// a RST. In that case we'll just get things going and return immediately. +// +// Input: Request - The request structure for this command. +// Timeout - How long to wait for the request. The format +// of this time is system specific - we use +// a macro to convert to ticks. +// Flags - Flags indicating type of disconnect. +// DiscConnInfo - Pointer to a TDI_CONNECTION_INFORMATION +// structure giving disconnection info. Ignored +// for this request. +// ReturnInfo - Pointer to where to return information. +// Ignored for this request. +// +// Returns: Status of attempt to disconnect. +// +TDI_STATUS +TdiDisconnect(PTDI_REQUEST Request, void *TO, ushort Flags, + PTDI_CONNECTION_INFORMATION DiscConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo) +{ + TCPConnReq *ConnReq; // Connection request to use. + TCPConn *Conn; + TCB *DiscTCB; + CTELockHandle ConnTableHandle, TCBHandle; + TDI_STATUS Status; + TCP_TIME *Timeout; + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + DiscTCB = Conn->tc_tcb; + if (DiscTCB != NULL) { + CTEStructAssert(DiscTCB, tcb); + CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle); + + // We have the TCB. See what kind of disconnect this is. + if (Flags & TDI_DISCONNECT_ABORT) { + // This is an abortive disconnect. If we're not already + // closed or closing, blow the connection away. + if (DiscTCB->tcb_state != TCB_CLOSED) { + CTEFreeLock(&ConnTableLock, TCBHandle); + + if (!CLOSING(DiscTCB)) { + DiscTCB->tcb_flags |= NEED_RST; + TryToCloseTCB(DiscTCB, TCB_CLOSE_ABORTED, + ConnTableHandle); + } else + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + + return TDI_SUCCESS; + } else { + // The TCB isn't connected. + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + DEBUGCHK; + return TDI_INVALID_STATE; + } + } else { + // This is not an abortive close. For graceful close we'll need + // a ConnReq. + CTEFreeLock(&ConnTableLock, TCBHandle); + + // Make sure we aren't in the middle of an abortive close. + if (CLOSING(DiscTCB)) { + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return TDI_INVALID_CONNECTION; + } + + ConnReq = GetConnReq(); + if (ConnReq != NULL) { + // Got the ConnReq. See if this is a DISCONNECT_WAIT + // primitive or not. + + ConnReq->tcr_flags = 0; + ConnReq->tcr_conninfo = NULL; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + + if (!(Flags & TDI_DISCONNECT_WAIT)) { + Timeout = (TCP_TIME *)TO; + + if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) { + ulong Ticks = TCP_TIME_TO_TICKS(*Timeout); + if (Ticks > MAX_CONN_TO_TICKS) + Ticks = MAX_CONN_TO_TICKS; + else + Ticks++; + ConnReq->tcr_timeout = (ushort)Ticks; + } else + ConnReq->tcr_timeout = 0; + + // OK, we're just about set. We need to update the TCB + // state, and send the FIN. + if (DiscTCB->tcb_state == TCB_ESTAB) { + DiscTCB->tcb_state = TCB_FIN_WAIT1; + + // Since we left established, we're off the fast + // receive path. + DiscTCB->tcb_slowcount++; + DiscTCB->tcb_fastchk |= TCP_FLAG_SLOW; + } else + if (DiscTCB->tcb_state == TCB_CLOSE_WAIT) + DiscTCB->tcb_state = TCB_LAST_ACK; + else { + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + FreeConnReq(ConnReq); + return TDI_INVALID_STATE; + } + + TStats.ts_currestab--; // Update SNMP info. + CTEAssert(*(int *)&TStats.ts_currestab >= 0); + + CTEAssert(DiscTCB->tcb_connreq == NULL); + DiscTCB->tcb_connreq = ConnReq; + DiscTCB->tcb_flags |= FIN_NEEDED; + DiscTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + TCPSend(DiscTCB); +#else + TCPSend(DiscTCB, ConnTableHandle); +#endif + return TDI_PENDING; + } else { + // This is a DISC_WAIT request. + ConnReq->tcr_timeout = 0; + if (DiscTCB->tcb_discwait == NULL) { + DiscTCB->tcb_discwait = ConnReq; + Status = TDI_PENDING; + } else + Status = TDI_INVALID_STATE; + + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return Status; + } + } else { + // Couldn't get a ConnReq. + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return TDI_NO_RESOURCES; + } + } + } + } + + // No Conn, or no TCB on conn. Return an error. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return TDI_INVALID_CONNECTION; +} + +//* OKToNotify - See if it's OK to notify about a DISC. +// +// A little utility function, called to see it it's OK to notify the client +// of an incoming FIN. +// +// Input: NotifyTCB - TCB to check. +// +// Returns: TRUE if it's OK, False otherwise. +// +uint +OKToNotify(TCB *NotifyTCB) +{ + CTEStructAssert(NotifyTCB, tcb); + if (NotifyTCB->tcb_pendingcnt == 0 && NotifyTCB->tcb_urgcnt == 0 && + NotifyTCB->tcb_rcvhead == NULL && NotifyTCB->tcb_exprcv == NULL) + return TRUE; + else + return FALSE; +} + +//* NotifyOfDisc - Notify a client that a TCB is being disconnected. +// +// Called when we're disconnecting a TCB because we've received a FIN or +// RST from the remote peer, or because we're aborting for some reason. +// We'll complete a DISCONNECT_WAIT request if we have one, or try and +// issue an indication otherwise. This is only done if we're in a synchronized +// state and not in TIMED-WAIT. +// +// Input: DiscTCB - Pointer to TCB we're notifying. +// Status - Status code for notification. +// +// Returns: Nothing. +// +void +NotifyOfDisc(TCB *DiscTCB, IPOptInfo *DiscInfo, TDI_STATUS Status) +{ + CTELockHandle TCBHandle, AOTHandle, ConnTHandle; + TCPConnReq *DiscReq; + TCPConn *Conn; + AddrObj *DiscAO; + PVOID ConnContext; + + CTEStructAssert(DiscTCB, tcb); + CTEAssert(DiscTCB->tcb_refcnt != 0); + + CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle); + if (SYNC_STATE(DiscTCB->tcb_state) && + !(DiscTCB->tcb_flags & DISC_NOTIFIED)) { + + // We can't notify him if there's still data to be taken. + if (Status == TDI_GRACEFUL_DISC && !OKToNotify(DiscTCB)) { + DiscTCB->tcb_flags |= DISC_PENDING; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + return; + } + + DiscTCB->tcb_flags |= DISC_NOTIFIED; + DiscTCB->tcb_flags &= ~DISC_PENDING; + + // We're in a state where a disconnect is meaningful, and we haven't + // already notified the client. + + // See if we have a DISC-WAIT request pending. + if ((DiscReq = DiscTCB->tcb_discwait) != NULL) { + // We have a disconnect wait request. Complete it and we're done. + DiscTCB->tcb_discwait = NULL; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + (*DiscReq->tcr_req.tr_rtn)(DiscReq->tcr_req.tr_context, Status, 0); + FreeConnReq(DiscReq); + return; + + } + + // No DISC-WAIT. Find the AddrObj for the connection, and see if there + // is a disconnect handler registered. + + ConnContext = DiscTCB->tcb_conncontext; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + + CTEGetLock(&AddrObjTableLock, &AOTHandle); + CTEGetLock(&ConnTableLock, &ConnTHandle); + if ((Conn = DiscTCB->tcb_conn) != NULL) { + CTEStructAssert(Conn, tc); + + DiscAO = Conn->tc_ao; + if (DiscAO != NULL) { + CTELockHandle AOHandle; + PDisconnectEvent DiscEvent; + PVOID DiscContext; + + + CTEStructAssert(DiscAO, ao); + CTEGetLock(&DiscAO->ao_lock, &AOHandle); + CTEFreeLock(&ConnTableLock, AOHandle); + CTEFreeLock(&AddrObjTableLock, ConnTHandle); + + DiscEvent = DiscAO->ao_disconnect; + DiscContext = DiscAO->ao_disconncontext; + + if (DiscEvent != NULL) { + uint InfoLength; + PVOID Info; + + REF_AO(DiscAO); + CTEFreeLock(&DiscAO->ao_lock, AOTHandle); + + if (DiscInfo != NULL) { + InfoLength = (uint)DiscInfo->ioi_optlength; + Info = DiscInfo->ioi_options; + } else { + InfoLength = 0; + Info = NULL; + } + + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(("TCP: indicating %s disconnect\n", + (Status == TDI_GRACEFUL_DISC) ? "graceful" : + "abortive" + )); + } + + (*DiscEvent)(DiscContext, + ConnContext, 0, + NULL, InfoLength, Info, (Status == TDI_GRACEFUL_DISC) ? + TDI_DISCONNECT_RELEASE : TDI_DISCONNECT_ABORT); + + DELAY_DEREF_AO(DiscAO); + return; + } else { + CTEFreeLock(&DiscAO->ao_lock, AOTHandle); + return; + } + } + } + + CTEFreeLock(&ConnTableLock, ConnTHandle); + CTEFreeLock(&AddrObjTableLock, AOTHandle); + return; + + } + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + +} + +//* GracefulClose - Complete the transition to a gracefully closed state. +// +// Called when we need to complete the transition to a gracefully closed +// state, either TIME_WAIT or CLOSED. This completion involves removing +// the TCB from it's associated connection (if it has one), notifying the +// upper layer client either via completing a request or calling a disc. +// notification handler, and actually doing the transition. +// +// The tricky part here is if we need to notify him (instead of completing +// a graceful disconnect request). We can't notify him if there is pending +// data on the connection, so in that case we have to pend the disconnect +// notification until we deliver the data. +// +// Input: CloseTCB - TCB to transition. +// ToTimeWait - True if we're going to TIME_WAIT, False if +// we're going to close the TCB. +// Notify - True if we're going to transition via notification, +// False if we're going to transition by completing +// a disconnect request. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +GracefulClose(TCB *CloseTCB, uint ToTimeWait, uint Notify, CTELockHandle Handle) +{ + + CTEStructAssert(CloseTCB, tcb); + + CTEAssert(CloseTCB->tcb_refcnt != 0); + + // First, see if we need to notify the client of a FIN. + if (Notify) { + // We do need to notify him. See if it's OK to do so. + if (OKToNotify(CloseTCB)) { + // We can notify him. Change his state, pull him from the conn., + // and notify him. + if (ToTimeWait) { + // Save the time we went into time wait, in case we need to + // scavenge. + CloseTCB->tcb_alive = CTESystemUpTime(); + CloseTCB->tcb_state = TCB_TIME_WAIT; + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // He's going to close. Mark him as closing with TryToCloseTCB + // (he won't actually close since we have a ref. on him). We + // do this so that anyone touching him after we free the + // lock will fail. + TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle); + } + + + RemoveTCBFromConn(CloseTCB); + NotifyOfDisc(CloseTCB, NULL, TDI_GRACEFUL_DISC); + + } else { + // Can't notify him now. Set the appropriate flags, and return. + CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0)); + DerefTCB(CloseTCB, Handle); + return; + } + } else { + // We're not notifying this guy, we just need to complete a conn. req. + // We need to check and see if he's been notified, and if not + // we'll complete the request and notify him later. + if (CloseTCB->tcb_flags & DISC_NOTIFIED) { + // He's been notified. + if (ToTimeWait) { + // Save the time we went into time wait, in case we need to + // scavenge. + CloseTCB->tcb_alive = CTESystemUpTime(); + CloseTCB->tcb_state = TCB_TIME_WAIT; + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // Mark him as closed. See comments above. + TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle); + } + + RemoveTCBFromConn(CloseTCB); + + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS); + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // He hasn't been notified. He should be pending already. + CTEAssert(CloseTCB->tcb_flags & DISC_PENDING); + CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0)); + + CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS); + + DerefTCB(CloseTCB, Handle); + return; + } + } + + // If we're going to TIME_WAIT, start the TIME_WAIT timer now. + // Otherwise close the TCB. + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + if (!CLOSING(CloseTCB) && ToTimeWait) { + START_TCB_TIMER(CloseTCB->tcb_rexmittimer, MAX_REXMIT_TO); + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + RemoveConnFromTCB(CloseTCB); + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + + } + + DerefTCB(CloseTCB, Handle); + +} + +//* ConnCheckPassed - Check to see if we have exceeded the connect limit +// +// Called when a SYN is received to determine whether we will accept +// the incoming connection. If the is an empty slot or if the IPAddr +// is already in the table, we accept it. +// +// Input: Source Address of incoming connection +// Destination port of incoming connection +// +// Returns: TRUE is connect is to be accepted +// FALSE if connection is rejected +// +int ConnCheckPassed(IPAddr Src, ulong Prt) + +{ + UNREFERENCED_PARAMETER(Src); + UNREFERENCED_PARAMETER(Prt); + + return TRUE; +} + +void InitAddrChecks() +{ + return; +} + +//* EnumerateConnectionList - Enumerate Connection List database. +// +// This routine enumerates the contents of the connection limit database +// +// Input: +// +// Buffer - A pointer to a buffer into which to put +// the returned connection list entries. +// +// BufferSize - On input, the size in bytes of Buffer. +// On output, the number of bytes written. +// +// EntriesAvailable - On output, the total number of connection entries +// available in the database. +// +// Returns: A TDI status code: +// +// TDI_SUCCESS otherwise. +// +// NOTES: +// +// This routine acquires AddrObjTableLock. +// +// Entries written to output buffer are in host byte order. +// +void +EnumerateConnectionList(uchar *Buffer, ulong BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(BufferSize); + + *EntriesAvailable = 0; + *EntriesReturned = 0; + + return; +} + + +#pragma BEGIN_INIT + +//* InitTCPConn - Initialize TCP connection management code. +// +// Called during init time to initialize our TCP connection mgmt.. +// +// Input: Nothing. +// +// Returns: TRUE. +// +int +InitTCPConn(void) +{ +#ifdef NT + ExInitializeSListHead(&ConnReqFree); +#endif + + CTEInitLock(&ConnReqFreeLock); + return TRUE; +} + +//* UnInitTCPConn - Uninitialize our connection management code. +// +// Called if initialization fails to uninitialize our conn mgmet. +// +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +UnInitTCPConn(void) +{ + +} + +#pragma END_INIT + diff --git a/private/ntos/tdi/tcpip/tcp/tcpconn.h b/private/ntos/tdi/tcpip/tcp/tcpconn.h new file mode 100644 index 000000000..e3eed2c8b --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpconn.h @@ -0,0 +1,124 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPCONN.H - TCP connection related definitions. +// +// This file contains the definitions for connection related structures, +// such as the TCPConnReq structure. +// + +#define INVALID_CONN_INDEX 0xffffff + +//* Structure used for tracking Connect/Listen/Accept/Disconnect requests. + +#define tcr_signature 0x20524354 // 'TCR ' + +struct TCPConnReq { + struct TCPReq tcr_req; // General request structure. +#ifdef DEBUG + ulong tcr_sig; +#endif + struct _TDI_CONNECTION_INFORMATION *tcr_conninfo; // Where to return info. + ushort tcr_flags; // Flags for this request. + ushort tcr_timeout; // Timeout value for this request. +}; + +typedef struct TCPConnReq TCPConnReq; + +typedef void (*ConnDoneRtn)(struct TCPConn *, CTELockHandle); + +//* Structure of a TCB Connection. A TCP Connection points to a TCP and an +// address object. + +#define tc_signature 0x20204354 // 'TC ' + +struct TCPConn { +#ifdef DEBUG + ulong tc_sig; +#endif + Queue tc_q; // Linkage on AO. + struct TCB *tc_tcb; // Pointer to TCB for connection. + struct AddrObj *tc_ao; // Back pointer to AddrObj. + uchar tc_inst; // Instance number. + uchar tc_flags; // Flags for connection. + ushort tc_refcnt; // Count of TCBs which reference this + // connection. + void *tc_context; // User's context. + CTEReqCmpltRtn tc_rtn; // Completion routine. + PVOID tc_rtncontext; // User context for completion routine. + ConnDoneRtn tc_donertn; // Routine to call when refcnt goes to 0. + uint tc_tcbflags; // Flags for TCB when it comes in. + uint tc_window; // Default window for TCB. + +}; /* TCPConn */ + +typedef struct TCPConn TCPConn; + +#define CONN_CLOSING 1 // Connection is closing. +#define CONN_DISACC 2 // Conn. is disassociating. +#define CONN_WINSET 4 // Window explictly set. + +#define CONN_INVALID (CONN_CLOSING | CONN_DISACC) + +//* Structure of a ConnTable. +typedef struct TCPConn *TCPConnTable[]; + +extern TCPConnTable *ConnTable; + +#define FREE_CONN_INDEX(i) (*ConnTable)[(i)] = NULL +EXTERNAL_LOCK(ConnTableLock) + +struct TCPAddrCheck { + IPAddr SourceAddress; + uint TickCount; +}; /* TCPAddrCheck */ + +typedef struct TCPAddrCheck TCPAddrCheckElement; + +extern TCPAddrCheckElement *AddrCheckTable; + +//* External definitions for TDI entry points. +extern TDI_STATUS TdiOpenConnection(PTDI_REQUEST Request, PVOID Context); +extern TDI_STATUS TdiCloseConnection(PTDI_REQUEST Request); +extern TDI_STATUS TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle); +extern TDI_STATUS TdiDisAssociateAddress(PTDI_REQUEST Request); +extern TDI_STATUS TdiConnect(PTDI_REQUEST Request, void *Timeout, + PTDI_CONNECTION_INFORMATION RequestAddr, + PTDI_CONNECTION_INFORMATION ReturnAddr); +extern TDI_STATUS TdiListen(PTDI_REQUEST Request, ushort Flags, + PTDI_CONNECTION_INFORMATION AcceptableAddr, + PTDI_CONNECTION_INFORMATION ConnectedAddr); +extern TDI_STATUS TdiAccept(PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION AcceptInfo, + PTDI_CONNECTION_INFORMATION ConnectedInfo); +extern TDI_STATUS TdiDisconnect(PTDI_REQUEST Request, void *TO, ushort Flags, + PTDI_CONNECTION_INFORMATION DiscConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo); + +extern struct TCPConnReq *GetConnReq(void); +extern void FreeConnReq(struct TCPConnReq *FreedReq); +extern void DerefTCB(struct TCB *DoneTCB, CTELockHandle Handle); +extern void InitRCE(struct TCB *NewTCB); +extern void AcceptConn(struct TCB *AcceptTCB, CTELockHandle Handle); +extern void FreeConnID(uint ConnID); +extern void NotifyOfDisc(struct TCB *DiscTCB, struct IPOptInfo *DiscInfo, + TDI_STATUS Status); +extern TCPConn *GetConnFromConnID(uint ConnID); + +extern void TryToCloseTCB(struct TCB *ClosedTCB, uchar Reason, + CTELockHandle Handle); +extern TDI_STATUS InitTCBFromConn(struct TCPConn *Conn, struct TCB *NewTCB, + PTDI_CONNECTION_INFORMATION Addr, uint AOLocked); + +extern void PushData(struct TCB *PushTCB); +extern TDI_STATUS MapIPError(IP_STATUS IPError, TDI_STATUS Default); +extern void GracefulClose(struct TCB *CloseTCB, uint ToTimeWait, uint Notify, + CTELockHandle Handle); +extern void RemoveTCBFromConn(struct TCB *RemovedTCB); +extern void InitAddrChecks(); +extern int ConnCheckPassed(IPAddr Src, ulong Prt); +extern void EnumerateConnectionList(uchar *Buffer, ulong BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable); diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeb.c b/private/ntos/tdi/tcpip/tcp/tcpdeb.c new file mode 100644 index 000000000..70478f6ee --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpdeb.c @@ -0,0 +1,169 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPDEB.C - TCP debug code. +// +// This file contains the code for various TCP specific debug routines. +// + +#include "oscfg.h" + + +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "tcp.h" +#include "tcpsend.h" +#include "tlcommon.h" + + +#ifdef DEBUG + +#ifdef NT + +ULONG TCPDebug = TCP_DEBUG_CANCEL; + +#endif + + +//* CheckRBList - Check a list of RBs for the correct size. +// +// A routine to walk a list of RBs, making sure the size is what we think +// it it. +// +// Input: RBList - List of RBs to check. +// Size - Size RBs should be. +// +// Returns: Nothing. +// +void +CheckRBList(IPRcvBuf *RBList, uint Size) +{ + uint SoFar = 0; + IPRcvBuf *List = RBList; + + while (List != NULL) { + SoFar += List->ipr_size; + List = List->ipr_next; + } + + CTEAssert(Size == SoFar); + +} + +//* CheckTCBRcv - Check receives on a TCB. +// +// Check the receive state of a TCB. +// +// Input: CheckTCB - TCB to check. +// +// Returns: Nothing. +// +void +CheckTCBRcv(TCB *CheckTCB) +{ + CTEStructAssert(CheckTCB, tcb); + + CTEAssert(!(CheckTCB->tcb_flags & FLOW_CNTLD) || + (CheckTCB->tcb_sendwin == 0)); + + if ((CheckTCB->tcb_fastchk & ~TCP_FLAG_IN_RCV) == TCP_FLAG_ACK) { + CTEAssert(CheckTCB->tcb_slowcount == 0); + CTEAssert(CheckTCB->tcb_state == TCB_ESTAB); + CTEAssert(CheckTCB->tcb_raq == NULL); + CTEAssert(!(CheckTCB->tcb_flags & TCP_SLOW_FLAGS)); + CTEAssert(!CLOSING(CheckTCB)); + } else { + CTEAssert(CheckTCB->tcb_slowcount != 0); + CTEAssert( (CheckTCB->tcb_state != TCB_ESTAB) || + (CheckTCB->tcb_raq != NULL) || + (CheckTCB->tcb_flags & TCP_SLOW_FLAGS) || + CLOSING(CheckTCB)); + } + +} + +//* CheckTCBSends - Check the send status of a TCB. +// +// A routine to check the send status of a TCB. We make sure that all +// of the SendReqs make sense, as well as making sure that the send seq. +// variables in the TCB are consistent. +// +// Input: CheckTCB - TCB to check. +// +// Returns: Nothing. +// +void +CheckTCBSends(TCB *CheckTCB) +{ + Queue *End, *Current; // End and current elements. + TCPSendReq *CurrentTSR; // Current send req we're + // examining. + uint Unacked; // Number of unacked bytes. + PNDIS_BUFFER CurrentBuffer; + TCPSendReq *TCBTsr; // Current send on TCB. + uint FoundSendReq; + + + CTEStructAssert(CheckTCB, tcb); + + // Don't check on unsynchronized TCBs. + if (!SYNC_STATE(CheckTCB->tcb_state)) + return; + + CTEAssert(SEQ_LTE(CheckTCB->tcb_senduna, CheckTCB->tcb_sendnext)); + CTEAssert(SEQ_LTE(CheckTCB->tcb_sendnext, CheckTCB->tcb_sendmax)); + CTEAssert(!(CheckTCB->tcb_flags & FIN_OUTSTANDING) || + (CheckTCB->tcb_sendnext == CheckTCB->tcb_sendmax)); + + if (CheckTCB->tcb_unacked == 0) { + CTEAssert(CheckTCB->tcb_cursend == NULL); + CTEAssert(CheckTCB->tcb_sendsize == 0); + } + + if (CheckTCB->tcb_sendbuf != NULL) + CTEAssert(CheckTCB->tcb_sendofs < NdisBufferLength(CheckTCB->tcb_sendbuf)); + + TCBTsr = CheckTCB->tcb_cursend; + FoundSendReq = (TCBTsr == NULL) ? TRUE : FALSE; + + End = QEND(&CheckTCB->tcb_sendq); + Current = QHEAD(&CheckTCB->tcb_sendq); + + Unacked = 0; + while (Current != End) { + CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q), + tsr_req); + CTEStructAssert(CurrentTSR, tsr); + + if (CurrentTSR == TCBTsr) + FoundSendReq = TRUE; + + CTEAssert(CurrentTSR->tsr_unasize <= CurrentTSR->tsr_size); + + CurrentBuffer = CurrentTSR->tsr_buffer; + CTEAssert(CurrentBuffer != NULL); + + CTEAssert(CurrentTSR->tsr_offset < NdisBufferLength(CurrentBuffer)); + + Unacked += CurrentTSR->tsr_unasize; + Current = QNEXT(Current); + } + + CTEAssert(FoundSendReq); + + CTEAssert(Unacked == CheckTCB->tcb_unacked); + Unacked += ((CheckTCB->tcb_flags & FIN_SENT) ? 1 : 0); + CTEAssert((CheckTCB->tcb_sendmax - CheckTCB->tcb_senduna) <= (int) Unacked); +} + +#endif diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeb.h b/private/ntos/tdi/tcpip/tcp/tcpdeb.h new file mode 100644 index 000000000..b22e18a1b --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpdeb.h @@ -0,0 +1,78 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPDEB.H - TCP debug definitions. +// +// This file contains the definitions for the debug code. +// + +#ifndef NO_TCP_DEFS +#ifdef DEBUG + +#ifndef UDP_ONLY +extern void CheckRBList(IPRcvBuf *RBList, uint Size); +extern void CheckTCBSends(TCB *SendTcb); +extern void CheckTCBRcv(TCB *RcvTCB); +#else +#define CheckRBList(R, S) +#define CheckTCBSends(T) +#define CheckTCBRcv(T) +#endif // UDP_ONLY + +#else + +#define CheckRBList(R, S) +#define CheckTCBSends(T) +#define CheckTCBRcv(T) +#endif // DEBUG +#endif // NO_TCP_DEFS + +// +// Additional debugging support for NT +// +#ifdef NT +#if DBG + +extern ULONG TCPDebug; + +#define TCP_DEBUG_OPEN 0x00000001 +#define TCP_DEBUG_CLOSE 0x00000002 +#define TCP_DEBUG_ASSOCIATE 0x00000004 +#define TCP_DEBUG_CONNECT 0x00000008 +#define TCP_DEBUG_SEND 0x00000010 +#define TCP_DEBUG_RECEIVE 0x00000020 +#define TCP_DEBUG_INFO 0x00000040 +#define TCP_DEBUG_IRP 0x00000080 +#define TCP_DEBUG_SEND_DGRAM 0x00000100 +#define TCP_DEBUG_RECEIVE_DGRAM 0x00000200 +#define TCP_DEBUG_EVENT_HANDLER 0x00000400 +#define TCP_DEBUG_CLEANUP 0x00000800 +#define TCP_DEBUG_CANCEL 0x00001000 +#define TCP_DEBUG_RAW 0x00002000 +#define TCP_DEBUG_OPTIONS 0x00004000 + + +#define TCPTRACE(many_args) DbgPrint many_args + +#define IF_TCPDBG(flag) if (TCPDebug & flag) + + +#else // DBG + + +#define TCPTRACE(many_args) +#define IF_TCPDBG(flag) if (0) + + +#endif // DBG +#else // NT + + +#define TCPTRACE(many_args) +#define IF_TCPDBG(flag) if (0) + + +#endif // NT diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeliv.c b/private/ntos/tdi/tcpip/tcp/tcpdeliv.c new file mode 100644 index 000000000..36d686257 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpdeliv.c @@ -0,0 +1,1971 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPDELIV.C - TCP deliver data code. +// +// This file contains the code for delivering data to the user, including +// putting data into recv. buffers and calling indication handlers. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcprcv.h" +#include "tcpsend.h" +#include "tcpconn.h" +#include "tcpdeliv.h" +#include "tlcommon.h" + +EXTERNAL_LOCK(AddrObjTableLock) + +extern void +PutOnRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint Size); + +#ifndef NT +TCPRcvReq *TCPRcvReqFree = NULL; // Rcv req. free list. +#else +SLIST_HEADER TCPRcvReqFree; // Rcv req. free list. +#endif + +DEFINE_LOCK_STRUCTURE(TCPRcvReqFreeLock) // Protects rcv req free list. +uint NumTCPRcvReq = 0; // Current number of RcvReqs in system. +uint MaxRcvReq = 0xffffffff; // Maximum allowed number of SendReqs. + +#ifdef NT + +NTSTATUS +TCPPrepareIrpForCancel( + PTCP_CONTEXT TcpContext, + PIRP Irp, + PDRIVER_CANCEL CancelRoutine + ); + +ULONG +TCPGetMdlChainByteCount( + PMDL Mdl + ); + +void +TCPDataRequestComplete( + void *Context, + unsigned int Status, + unsigned int ByteCount + ); + +VOID +TCPCancelRequest( + PDEVICE_OBJECT Device, + PIRP Irp + ); + +#endif // NT + + +//* FreeRcvReq - Free a rcv request structure. +// +// Called to free a rcv request structure. +// +// Input: FreedReq - Rcv request structure to be freed. +// +// Returns: Nothing. +// +void +FreeRcvReq(TCPRcvReq *FreedReq) +{ +#ifdef NT + + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(FreedReq, trr); + + BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedReq->trr_next), Next); + + ExInterlockedPushEntrySList( + &TCPRcvReqFree, + BufferLink, + &TCPRcvReqFreeLock + ); + +#else // NT + + TCPRcvReq **Temp; + + CTEStructAssert(FreedReq, trr); + + FreedReq->trr_next = TCPRcvReqFree; + TCPRcvReqFree = FreedReq; + +#endif // NT +} + +//* GetRcvReq - Get a recv. request structure. +// +// Called to get a rcv. request structure. +// +// Input: Nothing. +// +// Returns: Pointer to RcvReq structure, or NULL if none. +// +TCPRcvReq * +GetRcvReq(void) +{ + TCPRcvReq *Temp; + +#ifdef NT + + PSINGLE_LIST_ENTRY BufferLink; + + + BufferLink = ExInterlockedPopEntrySList( + &TCPRcvReqFree, + &TCPRcvReqFreeLock + ); + + if (BufferLink != NULL) { + Temp = STRUCT_OF(TCPRcvReq, BufferLink, trr_next); + CTEStructAssert(Temp, trr); + } + else { + if (NumTCPRcvReq < MaxRcvReq) + Temp = CTEAllocMem(sizeof(TCPRcvReq)); + else + Temp = NULL; + + if (Temp != NULL) { + ExInterlockedAddUlong(&NumTCPRcvReq, 1, &TCPRcvReqFreeLock); +#ifdef DEBUG + Temp->trr_sig = trr_signature; +#endif + } + } + +#else // NT + + Temp = TCPRcvReqFree; + if (Temp != NULL) + TCPRcvReqFree = Temp->trr_next; + else { + if (NumTCPRcvReq < MaxRcvReq) + Temp = CTEAllocMem(sizeof(TCPRcvReq)); + else + Temp = NULL; + + if (Temp != NULL) { + NumTCPRcvReq++; +#ifdef DEBUG + Temp->trr_sig = trr_signature; +#endif + } + } + +#endif // NT + + return Temp; +} + + + +//* FindLastBuffer - Find the last buffer in a chain. +// +// A utility routine to find the last buffer in an rb chain. +// +// Input: Buf - Pointer to RB chain. +// +// Returns: Pointer to last buf in chain. +// +IPRcvBuf * +FindLastBuffer(IPRcvBuf *Buf) +{ + CTEAssert(Buf != NULL); + + while (Buf->ipr_next != NULL) + Buf = Buf->ipr_next; + + return Buf; +} + + +//* FreePartialRB - Free part of an RB chain. +// +// Called to adjust an free part of an RB chain. We walk down the chain, +// trying to free buffers. +// +// Input: RB - RB chain to be adjusted. +// Size - Size in bytes to be freed. +// +// Returns: Pointer to adjusted RB chain. +// +IPRcvBuf * +FreePartialRB(IPRcvBuf *RB, uint Size) +{ + while (Size != 0) { + IPRcvBuf *TempBuf; + + CTEAssert(RB != NULL); + + if (Size >= RB->ipr_size) { + Size -= RB->ipr_size; + TempBuf = RB; + RB = RB->ipr_next; + if (TempBuf->ipr_owner == IPR_OWNER_TCP) + CTEFreeMem(TempBuf); + } else { + RB->ipr_size -= Size; + RB->ipr_buffer += Size; + break; + } + } + + CTEAssert(RB != NULL); + return RB; + +} + +//* CopyRBChain - Copy a chain of IP rcv buffers. +// +// Called to copy a chain of IP rcv buffers. We don't copy a buffer if it's +// already owner by TCP. We assume that all non-TCP owned buffers start +// before any TCP owner buffers, so we quit when we copy to a TCP owner buffer. +// +// Input: OrigBuf - Buffer chain to copy from. +// LastBuf - Where to return pointer to last buffer in +// chain. +// Size - Maximum size in bytes to copy. +// +// Returns: Pointer to new buffer chain. +// +IPRcvBuf * +CopyRBChain(IPRcvBuf *OrigBuf, IPRcvBuf **LastBuf, uint Size) +{ + IPRcvBuf *FirstBuf, *EndBuf; + uint BytesToCopy; + + CTEAssert(OrigBuf != NULL); + CTEAssert(Size > 0); + + if (OrigBuf->ipr_owner != IPR_OWNER_TCP) { + + BytesToCopy = MIN(Size, OrigBuf->ipr_size); + FirstBuf = CTEAllocMem(sizeof(IPRcvBuf) + BytesToCopy); + if (FirstBuf != NULL) { + EndBuf = FirstBuf; + FirstBuf->ipr_next = NULL; + FirstBuf->ipr_owner = IPR_OWNER_TCP; + FirstBuf->ipr_size = BytesToCopy; + FirstBuf->ipr_buffer = (uchar *)(FirstBuf + 1); + CTEMemCopy(FirstBuf->ipr_buffer, OrigBuf->ipr_buffer, + BytesToCopy); + Size -= BytesToCopy; + OrigBuf = OrigBuf->ipr_next; + while (OrigBuf != NULL && OrigBuf->ipr_owner != IPR_OWNER_TCP + && Size != 0) { + IPRcvBuf *NewBuf; + + BytesToCopy = MIN(Size, OrigBuf->ipr_size); + NewBuf = CTEAllocMem(sizeof(IPRcvBuf) + BytesToCopy); + if (NewBuf != NULL) { + NewBuf->ipr_next = NULL; + NewBuf->ipr_owner = IPR_OWNER_TCP; + NewBuf->ipr_size = BytesToCopy; + NewBuf->ipr_buffer = (uchar *)(NewBuf + 1); + CTEMemCopy(NewBuf->ipr_buffer, OrigBuf->ipr_buffer, + BytesToCopy); + EndBuf->ipr_next = NewBuf; + EndBuf = NewBuf; + Size -= BytesToCopy; + OrigBuf = OrigBuf->ipr_next; + } else { + FreeRBChain(FirstBuf); + return NULL; + } + } + EndBuf->ipr_next = OrigBuf; + } else + return NULL; + } else { + FirstBuf = OrigBuf; + EndBuf = OrigBuf; + if (Size < OrigBuf->ipr_size) + OrigBuf->ipr_size = Size; + Size -= OrigBuf->ipr_size; + } + + // Now walk down the chain, until we run out of + // Size. At this point, Size is the bytes left to 'copy' (it may be 0), + // and the sizes in buffers FirstBuf...EndBuf are correct. + while (Size != 0) { + + EndBuf = EndBuf->ipr_next; + CTEAssert(EndBuf != NULL); + + if (Size < EndBuf->ipr_size) + EndBuf->ipr_size = Size; + + Size -= EndBuf->ipr_size; + } + + // If there's anything left in the chain, free it now. + if (EndBuf->ipr_next != NULL) { + FreeRBChain(EndBuf->ipr_next); + EndBuf->ipr_next = NULL; + } + + *LastBuf = EndBuf; + return FirstBuf; + +} + +//* PendData - Pend incoming data to a client. +// +// Called when we need to buffer data for a client because there's no receive +// down and we can't indicate. +// +// The TCB lock is held throughout this procedure. If this is to be changed, +// make sure consistency of tcb_pendingcnt is preserved. This routine is +// always called at DPC level. +// +// Input: RcvTCB - TCB on which to receive the data. +// RcvFlags - TCP flags for the incoming packet. +// InBuffer - Input buffer of packet. +// Size - Size in bytes of data in InBuffer. +// +// Returns: Number of bytes of data taken. +// +uint +PendData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size) +{ + + IPRcvBuf *NewBuf, *LastBuf; + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(Size > 0); + CTEAssert(InBuffer != NULL); + + CTEAssert(RcvTCB->tcb_refcnt != 0); + CTEAssert(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV); + CTEAssert(RcvTCB->tcb_currcv == NULL); + CTEAssert(RcvTCB->tcb_rcvhndlr == PendData); + + CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt); + + NewBuf = CopyRBChain(InBuffer, &LastBuf, Size); + if (NewBuf != NULL) { + // We have a duplicate chain. Put it on the end of the + // pending q. + if (RcvTCB->tcb_pendhead == NULL) { + RcvTCB->tcb_pendhead = NewBuf; + RcvTCB->tcb_pendtail = LastBuf; + } else { + RcvTCB->tcb_pendtail->ipr_next = NewBuf; + RcvTCB->tcb_pendtail = LastBuf; + } + RcvTCB->tcb_pendingcnt += Size; + } else { + FreeRBChain(InBuffer); + Size = 0; + } + + CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt); + + return Size; + +} + + + +//* BufferData - Put incoming data into client's buffer. +// +// Called when we believe we have a buffer into which we can put data. We put +// it in there, and if we've filled the buffer or the incoming data has the +// push flag set we'll mark the TCB to return the buffer. Otherwise we'll +// get out and return the data later. +// +// In NT, this routine is called with the TCB lock held, and holds it for +// the duration of the call. This is important to ensure consistency of +// the tcb_pendingcnt field. If we need to change this to free the lock +// partway through, make sure to take this into account. In particular, +// TdiReceive zeros pendingcnt before calling this routine, and this routine +// may update it. If the lock is freed in here there would be a window where +// we really do have pending data, but it's not on the list or reflected in +// pendingcnt. This could screw up our windowing computations, and we'd have +// to be careful not to end up with more data pending than our window allows. +// +// Input: RcvTCB - TCB on which to receive the data. +// RcvFlags - TCP rcv flags for the incoming packet. +// InBuffer - Input buffer of packet. +// Size - Size in bytes of data in InBuffer. +// +// Returns: Number of bytes of data taken. +// +uint +BufferData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size) + +{ + uchar *DestPtr; // Destination pointer. + uchar *SrcPtr; // Src pointer. + uint SrcSize, DestSize; // Sizes of current source and + // destination buffers. + uint Copied; // Total bytes to copy. + uint BytesToCopy; // Bytes of data to copy this time. + TCPRcvReq *DestReq; // Current receive request. + IPRcvBuf *SrcBuf; // Current source buffer. + PNDIS_BUFFER DestBuf; // Current receive buffer. + uint RcvCmpltd; + uint Flags; + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(Size > 0); + CTEAssert(InBuffer != NULL); + + CTEAssert(RcvTCB->tcb_refcnt != 0); + CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData); + + Copied = 0; + RcvCmpltd = 0; + + DestReq = RcvTCB->tcb_currcv; + + CTEAssert(DestReq != NULL); + CTEStructAssert(DestReq, trr); + + DestBuf = DestReq->trr_buffer; + + DestSize = MIN(NdisBufferLength(DestBuf) - DestReq->trr_offset, + DestReq->trr_size - DestReq->trr_amt); + DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf) + DestReq->trr_offset; + + SrcBuf = InBuffer; + SrcSize = SrcBuf->ipr_size; + SrcPtr = SrcBuf->ipr_buffer; + + Flags = (RcvFlags & TCP_FLAG_PUSH) ? TRR_PUSHED : 0; + RcvCmpltd = Flags; + DestReq->trr_flags |= Flags; + + do { + + BytesToCopy = MIN(Size - Copied, MIN(SrcSize, DestSize)); + + CTEMemCopy(DestPtr, SrcPtr, BytesToCopy); + Copied += BytesToCopy; + DestReq->trr_amt += BytesToCopy; + + // Update our source pointers. + if ((SrcSize -= BytesToCopy) == 0) { + IPRcvBuf *TempBuf; + + // We've copied everything in this buffer. + TempBuf = SrcBuf; + SrcBuf = SrcBuf->ipr_next; + if (Size != Copied) { + CTEAssert(SrcBuf != NULL); + SrcSize = SrcBuf->ipr_size; + SrcPtr = SrcBuf->ipr_buffer; + } + if (TempBuf->ipr_owner == IPR_OWNER_TCP) + CTEFreeMem(TempBuf); + } else + SrcPtr += BytesToCopy; + + // Now check the destination pointer, and update it if we need to. + if ((DestSize -= BytesToCopy) == 0) { + uint DestAvail; + + // Exhausted this buffer. See if there's another one. + DestAvail = DestReq->trr_size - DestReq->trr_amt; + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + + if (DestBuf != NULL && (DestAvail != 0)) { + // Have another buffer in the chain. Update things. + DestSize = MIN(NdisBufferLength(DestBuf), DestAvail); + DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf); + } else { + // No more buffers in the chain. See if we have another buffer + // on the list. + DestReq->trr_flags |= TRR_PUSHED; + + // If we've been told there's to be no back traffic, get an ACK + // going right away. + if (DestReq->trr_flags & TDI_RECEIVE_NO_RESPONSE_EXP) + DelayAction(RcvTCB, NEED_ACK); + + RcvCmpltd = TRUE; + DestReq = DestReq->trr_next; + if (DestReq != NULL) { + DestBuf = DestReq->trr_buffer; + DestSize = MIN(NdisBufferLength(DestBuf), DestReq->trr_size); + DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf); + + // If we have more to put into here, set the flags. + if (Copied != Size) + DestReq->trr_flags |= Flags; + + } else { + // All out of buffer space. Reset the data handler pointer. + break; + } + } + } else + // Current buffer not empty yet. + DestPtr += BytesToCopy; + + + // If we've copied all that we need to, we're done. + } while (Copied != Size); + + // We've finished copying, and have a few more things to do. We need to + // update the current rcv. pointer and possibly the offset in the + // recv. request. If we need to complete any receives we have to schedule + // that. If there's any data we couldn't copy we'll need to dispose of + // it. + RcvTCB->tcb_currcv = DestReq; + if (DestReq != NULL) { + DestReq->trr_buffer = DestBuf; + DestReq->trr_offset = DestPtr - (uchar *) NdisBufferVirtualAddress(DestBuf); + RcvTCB->tcb_rcvhndlr = BufferData; + } else + RcvTCB->tcb_rcvhndlr = PendData; + + RcvTCB->tcb_indicated -= MIN(Copied, RcvTCB->tcb_indicated); + + if (Size != Copied) { + IPRcvBuf *NewBuf, *LastBuf; + + CTEAssert(DestReq == NULL); + + // We have data to dispose of. Update the first buffer of the chain + // with the current src pointer and size, and copy it. + CTEAssert(SrcSize <= SrcBuf->ipr_size); + CTEAssert( + ((uint) (SrcPtr - SrcBuf->ipr_buffer)) == + (SrcBuf->ipr_size - SrcSize) + ); + + SrcBuf->ipr_buffer = SrcPtr; + SrcBuf->ipr_size = SrcSize; + + NewBuf = CopyRBChain(SrcBuf, &LastBuf, Size - Copied); + if (NewBuf != NULL) { + // We managed to copy the buffer. Push it on the pending queue. + if (RcvTCB->tcb_pendhead == NULL) { + RcvTCB->tcb_pendhead = NewBuf; + RcvTCB->tcb_pendtail = LastBuf; + } else { + LastBuf->ipr_next = RcvTCB->tcb_pendhead; + RcvTCB->tcb_pendhead = NewBuf; + } + RcvTCB->tcb_pendingcnt += Size - Copied; + Copied = Size; + + CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt); + + } else + FreeRBChain(SrcBuf); + } else { + // We copied Size bytes, but the chain could be longer than that. Free + // it if we need to. + if (SrcBuf != NULL) + FreeRBChain(SrcBuf); + } + + + if (RcvCmpltd != 0) { + DelayAction(RcvTCB, NEED_RCV_CMPLT); + } else { + START_TCB_TIMER(RcvTCB->tcb_pushtimer, PUSH_TO); + } + + return Copied; + +} + + +//* IndicateData - Indicate incoming data to a client. +// +// Called when we need to indicate data to an upper layer client. We'll pass +// up a pointer to whatever we have available, and the client may take some +// or all of it. +// +// Input: RcvTCB - TCB on which to receive the data. +// RcvFlags - TCP rcv flags for the incoming packet. +// InBuffer - Input buffer of packet. +// Size - Size in bytes of data in InBuffer. +// +// Returns: Number of bytes of data taken. +// +uint +IndicateData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size) +{ + + TDI_STATUS Status; + PRcvEvent Event; + PVOID EventContext, ConnContext; + uint BytesTaken = 0; +#ifdef NT + EventRcvBuffer *ERB = NULL; + PTDI_REQUEST_KERNEL_RECEIVE RequestInformation; + PIO_STACK_LOCATION IrpSp; +#else + EventRcvBuffer ERB; +#endif + TCPRcvReq *RcvReq; + IPRcvBuf *NewBuf; + ulong IndFlags; + + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(Size > 0); + CTEAssert(InBuffer != NULL); + + CTEAssert(RcvTCB->tcb_refcnt != 0); + CTEAssert(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV); + CTEAssert(RcvTCB->tcb_rcvind != NULL); + CTEAssert(RcvTCB->tcb_rcvhead == NULL); + CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData); + + RcvReq = GetRcvReq(); + if (RcvReq != NULL) { + // The indicate handler is saved in the TCB. Just call up into it. + Event = RcvTCB->tcb_rcvind; + EventContext = RcvTCB->tcb_ricontext; + ConnContext = RcvTCB->tcb_conncontext; + RcvTCB->tcb_indicated = Size; + RcvTCB->tcb_flags |= IN_RCV_IND; + +#ifndef VXD + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, NULL); +#endif + + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(( + "Indicating %lu bytes, %lu available\n", + InBuffer->ipr_size, Size + )); + } + +#if TCP_FLAG_PUSH >= TDI_RECEIVE_ENTIRE_MESSAGE + IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL | + TDI_RECEIVE_AT_DISPATCH_LEVEL | + ((RcvFlags & TCP_FLAG_PUSH) >> + ((TCP_FLAG_PUSH / TDI_RECEIVE_ENTIRE_MESSAGE) - 1)); +#else + IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL | + TDI_RECEIVE_AT_DISPATCH_LEVEL | + ((RcvFlags & TCP_FLAG_PUSH) << + ((TDI_RECEIVE_ENTIRE_MESSAGE / TCP_FLAG_PUSH) - 1)); +#endif + + Status = (*Event)(EventContext, ConnContext, + IndFlags, InBuffer->ipr_size, Size, &BytesTaken, + InBuffer->ipr_buffer, &ERB); + + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(("%lu bytes taken, status %lx\n", BytesTaken, Status)); + } + + // See what the client did. If the return status is MORE_PROCESSING, + // we've been given a buffer. In that case put it on the front of the + // buffer queue, and if all the data wasn't taken go ahead and copy + // it into the new buffer chain. + // + // Note that the size and buffer chain we're concerned with here is + // the one that we passed to the client. Since we're in a rcv. handler, + // any data that has come in would have been put on the reassembly + // queue. + if (Status == TDI_MORE_PROCESSING) { + +#ifdef NT + + CTEAssert(ERB != NULL); + + IrpSp = IoGetCurrentIrpStackLocation(ERB); + + Status = TCPPrepareIrpForCancel( + (PTCP_CONTEXT) IrpSp->FileObject->FsContext, + ERB, + TCPCancelRequest + ); + + if (NT_SUCCESS(Status)) { + + RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) + &(IrpSp->Parameters); + + RcvReq->trr_rtn = TCPDataRequestComplete; + RcvReq->trr_context = ERB; + RcvReq->trr_buffer = ERB->MdlAddress; + RcvReq->trr_size = RequestInformation->ReceiveLength; + RcvReq->trr_uflags = (ushort *) + &(RequestInformation->ReceiveFlags); + RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags); + RcvReq->trr_offset = 0; + RcvReq->trr_amt = 0; + + CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL); + +#else // NT + + RcvReq->trr_rtn = ERB.erb_rtn; + RcvReq->trr_context = ERB.erb_context; + RcvReq->trr_buffer = ERB.erb_buffer; + RcvReq->trr_size = ERB.erb_size; + RcvReq->trr_uflags = ERB.erb_flags; + CTEAssert(ERB.erb_flags != NULL); + RcvReq->trr_flags = (uint)(*ERB.erb_flags); + RcvReq->trr_offset = 0; + RcvReq->trr_amt = 0; + +#endif // NT + + RcvTCB->tcb_flags &= ~IN_RCV_IND; + + CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData); + + // Push him on the front of the rcv. queue. + CTEAssert((RcvTCB->tcb_currcv == NULL) || + (RcvTCB->tcb_currcv->trr_amt == 0)); + + if (RcvTCB->tcb_rcvhead == NULL) { + RcvTCB->tcb_rcvhead = RcvReq; + RcvTCB->tcb_rcvtail = RcvReq; + RcvReq->trr_next = NULL; + } else { + RcvReq->trr_next = RcvTCB->tcb_rcvhead; + RcvTCB->tcb_rcvhead = RcvReq; + } + + RcvTCB->tcb_currcv = RcvReq; + RcvTCB->tcb_rcvhndlr = BufferData; + + CTEAssert(BytesTaken <= Size); + + RcvTCB->tcb_indicated -= BytesTaken; + if ((Size -= BytesTaken) != 0) { + + // Not everything was taken. Adjust the buffer chain to point + // beyond what was taken. + InBuffer = FreePartialRB(InBuffer, BytesTaken); + + CTEAssert(InBuffer != NULL); + + // We've adjusted the buffer chain. Call the BufferData + // handler. + BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer, Size); + + } else { + // All of the data was taken. Free the buffer chain. + FreeRBChain(InBuffer); + } + + return BytesTaken; +#ifdef NT + } + else { + // + // The IRP was cancelled before it was handed back to us. + // We'll pretend we never saw it. TCPPrepareIrpForCancel + // already completed it. The client may have taken data, + // so we will act as if success was returned. + // + ERB = NULL; + Status = TDI_SUCCESS; + } + +#endif // NT + + } + + CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL); + + RcvTCB->tcb_flags &= ~IN_RCV_IND; + + // Status is not more processing. If it's not SUCCESS, the client + // didn't take any of the data. In either case we now need to + // see if all of the data was taken. If it wasn't, we'll try and + // push it onto the front of the pending queue. + + FreeRcvReq(RcvReq); // This won't be needed. + if (Status == TDI_NOT_ACCEPTED) + BytesTaken = 0; + + CTEAssert(BytesTaken <= Size); + + RcvTCB->tcb_indicated -= BytesTaken; + + CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData); + + // Check to see if a rcv. buffer got posted during the indication. + // If it did, reset the recv. handler now. + if (RcvTCB->tcb_rcvhead != NULL) + RcvTCB->tcb_rcvhndlr = BufferData; + + + // See if all of the data was taken. + if (BytesTaken == Size) { + CTEAssert(RcvTCB->tcb_indicated == 0); + + FreeRBChain(InBuffer); + return BytesTaken; // It was all taken. + } + + // It wasn't all taken. Adjust for what was taken, and push + // on the front of the pending queue. We also need to check to + // see if a receive buffer got posted during the indication. This + // would be weird, but not impossible. + InBuffer = FreePartialRB(InBuffer, BytesTaken); + if (RcvTCB->tcb_rcvhead == NULL) { + IPRcvBuf *LastBuf; + + RcvTCB->tcb_rcvhndlr = PendData; + NewBuf = CopyRBChain(InBuffer, &LastBuf, Size - BytesTaken); + if (NewBuf != NULL) { + // We have a duplicate chain. Push it on the front of the + // pending q. + if (RcvTCB->tcb_pendhead == NULL) { + RcvTCB->tcb_pendhead = NewBuf; + RcvTCB->tcb_pendtail = LastBuf; + } else { + LastBuf->ipr_next = RcvTCB->tcb_pendhead; + RcvTCB->tcb_pendhead = NewBuf; + } + RcvTCB->tcb_pendingcnt += Size - BytesTaken; + BytesTaken = Size; + } else { + FreeRBChain(InBuffer); + } + + return BytesTaken; + } else { + // Just great. There's now a rcv. buffer on the TCB. Call the + // BufferData handler now. + CTEAssert(RcvTCB->tcb_rcvhndlr = BufferData); + + BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer, + Size - BytesTaken); + + return BytesTaken; + } + + } else { + // Couldn't get a recv. request. We must be really low on resources, + // so just bail out now. + FreeRBChain(InBuffer); + return 0; + } + +} + + +//* IndicatePendingData - Indicate pending data to a client. +// +// Called when we need to indicate pending data to an upper layer client, +// usually because data arrived when we were in a state that it couldn't +// be indicated. +// +// Many of the comments in the BufferData header about the consistency of +// tcb_pendingcnt apply here also. +// +// Input: RcvTCB - TCB on which to indicate the data. +// RcvReq - Rcv. req. to use to indicate it. +// +// Returns: Nothing. +// +void +#ifdef VXD +IndicatePendingData(TCB *RcvTCB, TCPRcvReq *RcvReq) +#else +IndicatePendingData(TCB *RcvTCB, TCPRcvReq *RcvReq, CTELockHandle TCBHandle) +#endif +{ + + TDI_STATUS Status; + PRcvEvent Event; + PVOID EventContext, ConnContext; + uint BytesTaken = 0; +#ifdef NT + EventRcvBuffer *ERB = NULL; + PTDI_REQUEST_KERNEL_RECEIVE RequestInformation; + PIO_STACK_LOCATION IrpSp; +#else + EventRcvBuffer ERB; + CTELockHandle TCBHandle; // For debug builds. +#endif + IPRcvBuf *NewBuf; + uint Size; + + + CTEStructAssert(RcvTCB, tcb); + + CTEAssert(RcvTCB->tcb_refcnt != 0); + CTEAssert(RcvTCB->tcb_rcvind != NULL); + CTEAssert(RcvTCB->tcb_rcvhead == NULL); + CTEAssert(RcvTCB->tcb_pendingcnt != 0); + CTEAssert(RcvReq != NULL); + +#ifdef VXD + CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle); +#endif + + for (;;) { + CTEAssert(RcvTCB->tcb_rcvhndlr == PendData); + + // The indicate handler is saved in the TCB. Just call up into it. + Event = RcvTCB->tcb_rcvind; + EventContext = RcvTCB->tcb_ricontext; + ConnContext = RcvTCB->tcb_conncontext; + RcvTCB->tcb_indicated = RcvTCB->tcb_pendingcnt; + RcvTCB->tcb_flags |= IN_RCV_IND; + + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + + IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) { + TCPTRACE(( + "Indicating pending %d bytes, %d available\n", + RcvTCB->tcb_pendhead->ipr_size, RcvTCB->tcb_pendingcnt + )); + } + + Status = (*Event)(EventContext, ConnContext, + TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL | + TDI_RECEIVE_ENTIRE_MESSAGE, + RcvTCB->tcb_pendhead->ipr_size, RcvTCB->tcb_pendingcnt, + &BytesTaken, RcvTCB->tcb_pendhead->ipr_buffer, &ERB); + + IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) { + TCPTRACE(("%d bytes taken\n", BytesTaken)); + } + + // See what the client did. If the return status is MORE_PROCESSING, + // we've been given a buffer. In that case put it on the front of the + // buffer queue, and if all the data wasn't taken go ahead and copy + // it into the new buffer chain. + if (Status == TDI_MORE_PROCESSING) { + +#ifdef NT + + IF_TCPDBG(TCP_DEBUG_RECEIVE) { + TCPTRACE(("more processing on receive\n")); + } + + CTEAssert(ERB != NULL); + + IrpSp = IoGetCurrentIrpStackLocation(ERB); + + Status = TCPPrepareIrpForCancel( + (PTCP_CONTEXT) IrpSp->FileObject->FsContext, + ERB, + TCPCancelRequest + ); + + if (NT_SUCCESS(Status)) { + + RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) + &(IrpSp->Parameters); + + RcvReq->trr_rtn = TCPDataRequestComplete; + RcvReq->trr_context = ERB; + RcvReq->trr_buffer = ERB->MdlAddress; + RcvReq->trr_size = RequestInformation->ReceiveLength; + RcvReq->trr_uflags = (ushort *) + &(RequestInformation->ReceiveFlags); + RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags); + RcvReq->trr_offset = 0; + RcvReq->trr_amt = 0; + +#else // NT + + RcvReq->trr_rtn = ERB.erb_rtn; + RcvReq->trr_context = ERB.erb_context; + RcvReq->trr_buffer = ERB.erb_buffer; + RcvReq->trr_size = ERB.erb_size; + RcvReq->trr_uflags = ERB.erb_flags; + RcvReq->trr_flags = (uint)(*ERB.erb_flags); + RcvReq->trr_offset = 0; + RcvReq->trr_amt = 0; + +#endif // NT + + CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle); + RcvTCB->tcb_flags &= ~IN_RCV_IND; + + // Push him on the front of the rcv. queue. + CTEAssert((RcvTCB->tcb_currcv == NULL) || + (RcvTCB->tcb_currcv->trr_amt == 0)); + + if (RcvTCB->tcb_rcvhead == NULL) { + RcvTCB->tcb_rcvhead = RcvReq; + RcvTCB->tcb_rcvtail = RcvReq; + RcvReq->trr_next = NULL; + } else { + RcvReq->trr_next = RcvTCB->tcb_rcvhead; + RcvTCB->tcb_rcvhead = RcvReq; + } + + RcvTCB->tcb_currcv = RcvReq; + RcvTCB->tcb_rcvhndlr = BufferData; + + // Have to pick up the new size and pointers now, since things could + // have changed during the upcall. + Size = RcvTCB->tcb_pendingcnt; + NewBuf = RcvTCB->tcb_pendhead; + + RcvTCB->tcb_pendingcnt = 0; + RcvTCB->tcb_pendhead = NULL; + + CTEAssert(BytesTaken <= Size); + + RcvTCB->tcb_indicated -= BytesTaken; + if ((Size -= BytesTaken) != 0) { + + // Not everything was taken. Adjust the buffer chain to point + // beyond what was taken. + NewBuf = FreePartialRB(NewBuf, BytesTaken); + + CTEAssert(NewBuf != NULL); + + // We've adjusted the buffer chain. Call the BufferData + // handler. +#ifdef VXD + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + (void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, Size); +#else + (void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, Size); + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); +#endif + + } else { + // All of the data was taken. Free the buffer chain. Since + // we were passed a buffer chain which we put on the head of + // the list, leave the rcvhandler pointing at BufferData. + CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData); + CTEAssert(RcvTCB->tcb_indicated == 0); + CTEAssert(RcvTCB->tcb_rcvhead != NULL); + + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + FreeRBChain(NewBuf); + } + + return; +#ifdef NT + } + else { + // + // The IRP was cancelled before it was handed back to us. + // We'll pretend we never saw it. TCPPrepareIrpForCancel + // already completed it. The client may have taken data, + // so we will act as if success was returned. + // + ERB = NULL; + Status = TDI_SUCCESS; + } + +#endif // NT + + } + + CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle); + + RcvTCB->tcb_flags &= ~IN_RCV_IND; + + // Status is not more processing. If it's not SUCCESS, the client + // didn't take any of the data. In either case we now need to + // see if all of the data was taken. If it wasn't, we're done. + + if (Status == TDI_NOT_ACCEPTED) + BytesTaken = 0; + + CTEAssert(RcvTCB->tcb_rcvhndlr == PendData); + + RcvTCB->tcb_indicated -= BytesTaken; + Size = RcvTCB->tcb_pendingcnt; + NewBuf = RcvTCB->tcb_pendhead; + + CTEAssert(BytesTaken <= Size); + + // See if all of the data was taken. + if (BytesTaken == Size) { + // It was all taken. Zap the pending data information. + RcvTCB->tcb_pendingcnt = 0; + RcvTCB->tcb_pendhead = NULL; + + CTEAssert(RcvTCB->tcb_indicated == 0); + if (RcvTCB->tcb_rcvhead == NULL) { + if (RcvTCB->tcb_rcvind != NULL) + RcvTCB->tcb_rcvhndlr = IndicateData; + } else + RcvTCB->tcb_rcvhndlr = BufferData; + + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + FreeRBChain(NewBuf); + break; + } + + // It wasn't all taken. Adjust for what was taken, We also need to check + // to see if a receive buffer got posted during the indication. This + // would be weird, but not impossible. + NewBuf = FreePartialRB(NewBuf, BytesTaken); + + CTEAssert(RcvTCB->tcb_rcvhndlr == PendData); + + if (RcvTCB->tcb_rcvhead == NULL) { + RcvTCB->tcb_pendhead = NewBuf; + RcvTCB->tcb_pendingcnt -= BytesTaken; + if (RcvTCB->tcb_indicated != 0 || RcvTCB->tcb_rcvind == NULL) { + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + break; + } + + // From here, we'll loop around and indicate the new data that + // presumably came in during the previous indication. + } else { + // Just great. There's now a rcv. buffer on the TCB. Call the + // BufferData handler now. + RcvTCB->tcb_rcvhndlr = BufferData; + RcvTCB->tcb_pendingcnt = 0; + RcvTCB->tcb_pendhead = NULL; +#ifdef VXD + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); + BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, + Size - BytesTaken); +#else + BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, + Size - BytesTaken); + CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle); +#endif + break; + } + + } // for (;;) + + FreeRcvReq(RcvReq); // This isn't needed anymore. + +} + +//* DeliverUrgent - Deliver urgent data to a client. +// +// Called to deliver urgent data to a client. We assume the input +// urgent data is in a buffer we can keep. The buffer can be NULL, in +// which case we'll just look on the urgent pending queue for data. +// +// Input: RcvTCB - TCB to deliver on. +// RcvBuf - RcvBuffer for urgent data. +// Size - Number of bytes of urgent data to deliver. +// +// Returns: Nothing. +// +void +DeliverUrgent(TCB *RcvTCB, IPRcvBuf *RcvBuf, uint Size, + CTELockHandle *TCBHandle) +{ + CTELockHandle AOHandle, AOTblHandle, ConnHandle; + TCPRcvReq *RcvReq, *PrevReq; + uint BytesTaken = 0; + IPRcvBuf *LastBuf; +#ifdef NT + EventRcvBuffer *ERB; +#else + EventRcvBuffer ERB; +#endif + PRcvEvent ExpRcv; + PVOID ExpRcvContext; + PVOID ConnContext; + TDI_STATUS Status; + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(RcvTCB->tcb_refcnt != 0); + + CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt); + + // See if we have new data, or are processing old data. + if (RcvBuf != NULL) { + // We have new data. If the pending queue is not NULL, or we're already + // in this routine, just put the buffer on the end of the queue. + if (RcvTCB->tcb_urgpending != NULL || (RcvTCB->tcb_flags & IN_DELIV_URG)) { + IPRcvBuf *PrevRcvBuf; + + // Put him on the end of the queue. + PrevRcvBuf = STRUCT_OF(IPRcvBuf, &RcvTCB->tcb_urgpending, ipr_next); + while (PrevRcvBuf->ipr_next != NULL) + PrevRcvBuf = PrevRcvBuf->ipr_next; + + PrevRcvBuf->ipr_next = RcvBuf; + return; + } + } else { + // The input buffer is NULL. See if we have existing data, or are in + // this routine. If we have no existing data or are in this routine + // just return. + if (RcvTCB->tcb_urgpending == NULL || + (RcvTCB->tcb_flags & IN_DELIV_URG)) { + return; + } else { + RcvBuf = RcvTCB->tcb_urgpending; + Size = RcvTCB->tcb_urgcnt; + RcvTCB->tcb_urgpending = NULL; + RcvTCB->tcb_urgcnt = 0; + } + } + + + CTEAssert(RcvBuf != NULL); + CTEAssert(!(RcvTCB->tcb_flags & IN_DELIV_URG)); + + // We know we have data to deliver, and we have a pointer and a size. + // Go into a loop, trying to deliver the data. On each iteration, we'll + // try to find a buffer for the data. If we find one, we'll copy and + // complete it right away. Otherwise we'll try and indicate it. If we + // can't indicate it, we'll put it on the pending queue and leave. + RcvTCB->tcb_flags |= IN_DELIV_URG; + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + + do { + CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt); + + BytesTaken = 0; + + // First check the expedited queue. + if ((RcvReq = RcvTCB->tcb_exprcv) != NULL) + RcvTCB->tcb_exprcv = RcvReq->trr_next; + else { + // Nothing in the expedited rcv. queue. Walk down the ordinary + // receive queue, looking for a buffer that we can steal. + PrevReq = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_rcvhead, trr_next); + RcvReq = PrevReq->trr_next; + while (RcvReq != NULL) { + CTEStructAssert(RcvReq, trr); + if (RcvReq->trr_flags & TDI_RECEIVE_EXPEDITED) { + // This is a candidate. + if (RcvReq->trr_amt == 0) { + + CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData); + + // And he has nothing currently in him. Pull him + // out of the queue. + if (RcvTCB->tcb_rcvtail == RcvReq) { + if (RcvTCB->tcb_rcvhead == RcvReq) + RcvTCB->tcb_rcvtail = NULL; + else + RcvTCB->tcb_rcvtail = PrevReq; + } + + PrevReq->trr_next = RcvReq->trr_next; + if (RcvTCB->tcb_currcv == RcvReq) { + RcvTCB->tcb_currcv = RcvReq->trr_next; + if (RcvTCB->tcb_currcv == NULL) { + // We've taken the last receive from the list. + // Reset the rcvhndlr. + if (RcvTCB->tcb_rcvind != NULL && + RcvTCB->tcb_indicated == 0) + RcvTCB->tcb_rcvhndlr = IndicateData; + else + RcvTCB->tcb_rcvhndlr = PendData; + } + } + + break; + } + } + PrevReq = RcvReq; + RcvReq = PrevReq->trr_next; + } + } + + // We've done our best to get a buffer. If we got one, copy into it + // now, and complete the request. + + if (RcvReq != NULL) { + // Got a buffer. + CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle); + BytesTaken = CopyRcvToNdis(RcvBuf, RcvReq->trr_buffer, Size, 0, 0); + (*RcvReq->trr_rtn)(RcvReq->trr_context, TDI_SUCCESS, BytesTaken); + FreeRcvReq(RcvReq); + CTEGetLock(&RcvTCB->tcb_lock, TCBHandle); + RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, BytesTaken); + + } else { + // No posted buffer. If we can indicate, do so. + if (RcvTCB->tcb_urgind == 0) { + TCPConn *Conn; + + // See if he has an expedited rcv handler. + ConnContext = RcvTCB->tcb_conncontext; + CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle); + CTEGetLock(&AddrObjTableLock, &AOTblHandle); + CTEGetLock(&ConnTableLock, &ConnHandle); + CTEGetLock(&RcvTCB->tcb_lock, TCBHandle); + if ((Conn = RcvTCB->tcb_conn) != NULL) { + CTEStructAssert(Conn, tc); + CTEAssert(Conn->tc_tcb == RcvTCB); + CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle); + if (Conn->tc_ao != NULL) { + AddrObj *AO; + + AO = Conn->tc_ao; + CTEGetLock(&AO->ao_lock, &AOHandle); + if (AO_VALID(AO) && (ExpRcv = AO->ao_exprcv) != NULL) { + ExpRcvContext = AO->ao_exprcvcontext; + CTEFreeLock(&AO->ao_lock, AOHandle); + + // We're going to indicate. + RcvTCB->tcb_urgind = Size; + CTEFreeLock(&ConnTableLock, ConnHandle); + CTEFreeLock(&AddrObjTableLock, AOTblHandle); + + Status = (*ExpRcv)(ExpRcvContext, ConnContext, + TDI_RECEIVE_COPY_LOOKAHEAD | + TDI_RECEIVE_ENTIRE_MESSAGE | + TDI_RECEIVE_EXPEDITED, + RcvBuf->ipr_size, Size, &BytesTaken, + RcvBuf->ipr_buffer, &ERB); + + CTEGetLock(&RcvTCB->tcb_lock, TCBHandle); + + // See what he did with it. + if (Status == TDI_MORE_PROCESSING) { + uint CopySize; + + // He gave us a buffer. + if (BytesTaken == Size) { + // He gave us a buffer, but took all of + // it. We'll just return it to him. + CopySize = 0; + } else { + // We have some data to copy in. + RcvBuf = FreePartialRB(RcvBuf, BytesTaken); + +#ifdef NT + CopySize = CopyRcvToNdis(RcvBuf, + ERB->MdlAddress, + TCPGetMdlChainByteCount(ERB->MdlAddress), + 0, 0); +#else // NT + CopySize = CopyRcvToNdis(RcvBuf, + ERB.erb_buffer, ERB.erb_size, 0, 0); +#endif // NT + + } + BytesTaken += CopySize; + RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, + BytesTaken); + CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle); + +#ifdef NT + + ERB->IoStatus.Status = TDI_SUCCESS; + ERB->IoStatus.Information = CopySize; + IoCompleteRequest(ERB, 2); + +#else // NT + (*ERB.erb_rtn)(ERB.erb_context, TDI_SUCCESS, + CopySize); +#endif // NT + + CTEGetLock(&RcvTCB->tcb_lock, TCBHandle); + + } else { + + // No buffer to deal with. + if (Status == TDI_NOT_ACCEPTED) + BytesTaken = 0; + + RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, + BytesTaken); + + } + goto checksize; + } else // No rcv. handler. + CTEFreeLock(&AO->ao_lock, AOHandle); + } + // Conn->tc_ao == NULL. + CTEFreeLock(&ConnTableLock, ConnHandle); + CTEFreeLock(&AddrObjTableLock, AOTblHandle); + CTEGetLock(&RcvTCB->tcb_lock, TCBHandle); + } else { + // RcvTCB has invalid index. + CTEFreeLock(&ConnTableLock, *TCBHandle); + CTEFreeLock(&AddrObjTableLock, ConnHandle); + *TCBHandle = AOTblHandle; + } + + } + + // For whatever reason we couldn't indicate the data. At this point + // we hold the lock on the TCB. Push the buffer onto the pending + // queue and return. + CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt); + + LastBuf = FindLastBuffer(RcvBuf); + LastBuf->ipr_next = RcvTCB->tcb_urgpending; + RcvTCB->tcb_urgpending = RcvBuf; + RcvTCB->tcb_urgcnt += Size; + break; + } + +checksize: + // See how much we took. If we took it all, check the pending queue. + // At this point, we should hold the lock on the TCB. + if (Size == BytesTaken) { + // Took it all. + FreeRBChain(RcvBuf); + RcvBuf = RcvTCB->tcb_urgpending; + Size = RcvTCB->tcb_urgcnt; + } else { + // We didn't manage to take it all. Free what we did take, + // and then merge with the pending queue. + RcvBuf = FreePartialRB(RcvBuf, BytesTaken); + Size = Size - BytesTaken + RcvTCB->tcb_urgcnt; + if (RcvTCB->tcb_urgpending != NULL) { + + // Find the end of the current RcvBuf chain, so we can + // merge. + + LastBuf = FindLastBuffer(RcvBuf); + LastBuf->ipr_next = RcvTCB->tcb_urgpending; + } + } + + RcvTCB->tcb_urgpending = NULL; + RcvTCB->tcb_urgcnt = 0; + + } while (RcvBuf != NULL); + + CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt); + + RcvTCB->tcb_flags &= ~IN_DELIV_URG; + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + +} + +//* PushData - Push all data back to the client. +// +// Called when we've received a FIN and need to push data to the client. +// +// Input: PushTCB - TCB to be pushed. +// +// Returns: Nothing. +// +void +PushData(TCB *PushTCB) +{ + TCPRcvReq *RcvReq; + + CTEStructAssert(PushTCB, tcb); + + RcvReq = PushTCB->tcb_rcvhead; + while (RcvReq != NULL) { + CTEStructAssert(RcvReq, trr); + RcvReq->trr_flags |= TRR_PUSHED; + RcvReq = RcvReq->trr_next; + } + + RcvReq = PushTCB->tcb_exprcv; + while (RcvReq != NULL) { + CTEStructAssert(RcvReq, trr); + RcvReq->trr_flags |= TRR_PUSHED; + RcvReq = RcvReq->trr_next; + } + + if (PushTCB->tcb_rcvhead != NULL || PushTCB->tcb_exprcv != NULL) + DelayAction(PushTCB, NEED_RCV_CMPLT); + +} + + + +//* SplitRcvBuf - Split an IPRcvBuf into three pieces. +// +// This function takes an input IPRcvBuf and splits it into three pieces. +// The first piece is the input buffer, which we just skip over. The second +// and third pieces are actually copied, even if we already own them, so +// that the may go to different places. +// +// Input: RcvBuf - RcvBuf chain to be split. +// Size - Total size in bytes of rcvbuf chain. +// Offset - Offset to skip over. +// SecondSize - Size in bytes of second piece. +// SecondBuf - Where to return second buffer pointer. +// ThirdBuf - Where to return third buffer pointer. +// +// Returns: Nothing. *SecondBuf and *ThirdBuf are set to NULL if we can't +// get memory for them. +// +void +SplitRcvBuf(IPRcvBuf *RcvBuf, uint Size, uint Offset, uint SecondSize, + IPRcvBuf **SecondBuf, IPRcvBuf **ThirdBuf) +{ + IPRcvBuf *TempBuf; + uint ThirdSize; + + CTEAssert(Offset < Size); + CTEAssert(((Offset + SecondSize) < Size) || (((Offset + SecondSize) == Size) + && ThirdBuf == NULL)); + + CTEAssert(RcvBuf != NULL); + + // RcvBuf points at the buffer to copy from, and Offset is the offset into + // this buffer to copy from. + if (SecondBuf != NULL) { + // We need to allocate memory for a second buffer. + TempBuf = CTEAllocMem(sizeof(IPRcvBuf) + SecondSize); + if (TempBuf != NULL) { + TempBuf->ipr_size = SecondSize; + TempBuf->ipr_owner = IPR_OWNER_TCP; + TempBuf->ipr_buffer = (uchar *)(TempBuf + 1); + TempBuf->ipr_next = NULL; + CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, SecondSize, Offset); + *SecondBuf = TempBuf; + } else { + *SecondBuf = NULL; + if (ThirdBuf != NULL) + *ThirdBuf = NULL; + return; + } + } + + if (ThirdBuf != NULL) { + // We need to allocate memory for a third buffer. + ThirdSize = Size - (Offset + SecondSize); + TempBuf = CTEAllocMem(sizeof(IPRcvBuf) + ThirdSize); + + if (TempBuf != NULL) { + TempBuf->ipr_size = ThirdSize; + TempBuf->ipr_owner = IPR_OWNER_TCP; + TempBuf->ipr_buffer = (uchar *)(TempBuf + 1); + TempBuf->ipr_next = NULL; + CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, ThirdSize, + Offset + SecondSize); + *ThirdBuf = TempBuf; + } else + *ThirdBuf = NULL; + } + + +} + +//* HandleUrgent - Handle urgent data. +// +// Called when an incoming segment has urgent data in it. We make sure there +// really is urgent data in the segment, and if there is we try to dispose +// of it either by putting it into a posted buffer or calling an exp. rcv. +// indication handler. +// +// This routine is called at DPC level, and with the TCP locked. +// +// Urgent data handling is a little complicated. Each TCB has the starting +// and ending sequence numbers of the 'current' (last received) bit of urgent +// data. It is possible that the start of the current urgent data might be +// greater than tcb_rcvnext, if urgent data came in, we handled it, and then +// couldn't take the preceding normal data. The urgent valid flag is cleared +// when the next byte of data the user would read (rcvnext - pendingcnt) is +// greater than the end of urgent data - we do this so that we can correctly +// support SIOCATMARK. We always seperate urgent data out of the data stream. +// If the urgent valid field is set when we get into this routing we have +// to play a couple of games. If the incoming segment starts in front of the +// current urgent data, we truncate it before the urgent data, and put any +// data after the urgent data on the reassemble queue. These gyrations are +// done to avoid delivering the same urgent data twice. If the urgent valid +// field in the TCB is set and the segment starts after the current urgent +// data the new urgent information will replace the current urgent information. +// +// Input: RcvTCB - TCB to recv the data on. +// RcvInfo - RcvInfo structure for the incoming segment. +// RcvBuf - Pointer to IPRcvBuf train containing the +// incoming segment. +// Size - Pointer to size in bytes of data in the segment. +// +// Returns: Nothing. +// +void +HandleUrgent(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint *Size) +{ + uint BytesInFront, BytesInBack; // Bytes in front of and in + // back of the urgent data. + uint UrgSize; // Size in bytes of urgent data. + SeqNum UrgStart, UrgEnd; + IPRcvBuf *EndBuf, *UrgBuf; + TCPRcvInfo NewRcvInfo; + CTELockHandle TCBHandle; + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(RcvTCB->tcb_refcnt != 0); + CTEAssert(RcvInfo->tri_flags & TCP_FLAG_URG); + CTEAssert(SEQ_EQ(RcvInfo->tri_seq, RcvTCB->tcb_rcvnext)); + + // First, validate the urgent pointer. + if (RcvTCB->tcb_flags & BSD_URGENT) { + // We're using BSD style urgent data. We assume that the urgent + // data is one byte long, and that the urgent pointer points one + // after the urgent data instead of at the last byte of urgent data. + // See if the urgent data is in this segment. + + if (RcvInfo->tri_urgent == 0 || RcvInfo->tri_urgent > *Size) { + // Not in this segment. Clear the urgent flag and return. + RcvInfo->tri_flags &= ~TCP_FLAG_URG; + return; + } + + UrgSize = 1; + BytesInFront = RcvInfo->tri_urgent - 1; + + } else { + + // This is not BSD style urgent. We assume that the urgent data + // starts at the front of the segment and the last byte is pointed + // to by the urgent data pointer. + + BytesInFront = 0; + UrgSize = MIN(RcvInfo->tri_urgent + 1, *Size); + + } + + BytesInBack = *Size - BytesInFront - UrgSize; + + // UrgStart and UrgEnd are the first and last sequence numbers of the + // urgent data in this segment. + + UrgStart = RcvInfo->tri_seq + BytesInFront; + UrgEnd = UrgStart + UrgSize - 1; + + if (!(RcvTCB->tcb_flags & URG_INLINE)) { + + EndBuf = NULL; + + // Now see if this overlaps with any urgent data we've already seen. + if (RcvTCB->tcb_flags & URG_VALID) { + // We have some urgent data still around. See if we've advanced + // rcvnext beyond the urgent data. If we have, this is new urgent + // data, and we can go ahead and process it (although anyone doing + // an SIOCATMARK socket command might get confused). If we haven't + // consumed the data in front of the existing urgent data yet, we'll + // truncate this seg. to that amount and push the rest onto the + // reassembly queue. Note that rcvnext should never fall between + // tcb_urgstart and tcb_urgend. + + CTEAssert(SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart) || + SEQ_GT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgend)); + + if (SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart)) { + + // There appears to be some overlap in the data stream. Split + // the buffer up into pieces that come before the current urgent + // data and after the current urgent data, putting the latter + // on the reassembly queue. + + UrgSize = RcvTCB->tcb_urgend - RcvTCB->tcb_urgstart + 1; + + BytesInFront = MIN(RcvTCB->tcb_urgstart - RcvTCB->tcb_rcvnext, + (int) *Size); + + if (SEQ_GT(RcvTCB->tcb_rcvnext + *Size, RcvTCB->tcb_urgend)) { + // We have data after this piece of urgent data. + BytesInBack = RcvTCB->tcb_rcvnext + *Size - + RcvTCB->tcb_urgend; + } else + BytesInBack = 0; + + SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, NULL, + (BytesInBack ? &EndBuf : NULL)); + + if (EndBuf != NULL) { + NewRcvInfo.tri_seq = RcvTCB->tcb_urgend + 1; + NewRcvInfo.tri_flags = RcvInfo->tri_flags; + NewRcvInfo.tri_urgent = UrgEnd - NewRcvInfo.tri_seq; + if (RcvTCB->tcb_flags & BSD_URGENT) + NewRcvInfo.tri_urgent++; + NewRcvInfo.tri_ack = RcvInfo->tri_ack; + NewRcvInfo.tri_window = RcvInfo->tri_window; + PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack); + } + + *Size = BytesInFront; + RcvInfo->tri_flags &= ~TCP_FLAG_URG; + return; + } + } + + // We have urgent data we can process now. Split it into its component + // parts, the first part, the urgent data, and the stuff after the + // urgent data. + SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, &UrgBuf, + (BytesInBack ? &EndBuf : NULL)); + + // If we managed to split out the end stuff, put it on the queue now. + if (EndBuf != NULL) { + NewRcvInfo.tri_seq = RcvInfo->tri_seq + BytesInFront + UrgSize; + NewRcvInfo.tri_flags = RcvInfo->tri_flags & ~TCP_FLAG_URG; + NewRcvInfo.tri_ack = RcvInfo->tri_ack; + NewRcvInfo.tri_window = RcvInfo->tri_window; + PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack); + } + + + if (UrgBuf != NULL) { + // We succesfully split the urgent data out. + if (!(RcvTCB->tcb_flags & URG_VALID)) { + RcvTCB->tcb_flags |= URG_VALID; + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + RcvTCB->tcb_urgstart = UrgStart; + RcvTCB->tcb_urgend = UrgEnd; +#ifdef VXD +#ifdef DEBUG + TCBHandle = DEFAULT_SIMIRQL; +#endif +#else + TCBHandle = DISPATCH_LEVEL; +#endif + DeliverUrgent(RcvTCB, UrgBuf, UrgSize, &TCBHandle); + } + + *Size = BytesInFront; + + } else { + // Urgent data is to be processed inline. We just need to remember + // where it is and treat it as normal data. If there's already urgent + // data, we remember the latest urgent data. + + RcvInfo->tri_flags &= ~TCP_FLAG_URG; + + if (RcvTCB->tcb_flags & URG_VALID) { + // There is urgent data. See if this stuff comes after the existing + // urgent data. + + if (SEQ_LTE(UrgEnd, RcvTCB->tcb_urgend)) { + // The existing urgent data completely overlaps this stuff, + // so ignore this. + return; + } + } else { + RcvTCB->tcb_flags |= URG_VALID; + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + + RcvTCB->tcb_urgstart = UrgStart; + RcvTCB->tcb_urgend = UrgEnd; + } + + return; +} + +//* TdiReceive - Process a receive request. +// +// This is the main TDI receive request handler. We validate the connection +// and make sure that we have a TCB in the proper state, then we try to +// allocate a receive request structure. If that succeeds, we'll look and +// see what's happening on the TCB - if there's pending data, we'll put it +// in the buffer. Otherwise we'll just queue the receive for later. +// +// Input: Request - TDI_REQUEST structure for this request. +// Flags - Pointer to flags word. +// RcvLength - Pointer to length in bytes of receive buffer. +// Buffer - Pointer to buffer to take data. +// +// Returns: TDI_STATUS of request. +// +TDI_STATUS +TdiReceive(PTDI_REQUEST Request, ushort *Flags, uint *RcvLength, + PNDIS_BUFFER Buffer) +{ + TCPConn *Conn; + TCB *RcvTCB; + TCPRcvReq *RcvReq; + CTELockHandle ConnTableHandle, TCBHandle; + TDI_STATUS Error; + ushort UFlags; + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + RcvTCB = Conn->tc_tcb; + if (RcvTCB != NULL) { + CTEStructAssert(RcvTCB, tcb); + CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + UFlags = *Flags; + + if ((DATA_RCV_STATE(RcvTCB->tcb_state) || + (RcvTCB->tcb_pendingcnt != 0 && (UFlags & TDI_RECEIVE_NORMAL)) || + (RcvTCB->tcb_urgcnt != 0 && (UFlags & TDI_RECEIVE_EXPEDITED))) + && !CLOSING(RcvTCB)) { + // We have a TCB, and it's valid. Get a receive request now. + + CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt); + + RcvReq = GetRcvReq(); + if (RcvReq != NULL) { + + RcvReq->trr_rtn = Request->RequestNotifyObject; + RcvReq->trr_context = Request->RequestContext; + RcvReq->trr_buffer = Buffer; + RcvReq->trr_size = *RcvLength; + RcvReq->trr_uflags = Flags; + RcvReq->trr_offset = 0; + RcvReq->trr_amt = 0; + RcvReq->trr_flags = (uint)UFlags; + if ((UFlags & (TDI_RECEIVE_NORMAL | TDI_RECEIVE_EXPEDITED)) + != TDI_RECEIVE_EXPEDITED) { + // This is not an expedited only receive. Put him + // on the normal receive queue. + RcvReq->trr_next = NULL; + if (RcvTCB->tcb_rcvhead == NULL) { + // The receive queue is empty. Put him on the front. + RcvTCB->tcb_rcvhead = RcvReq; + RcvTCB->tcb_rcvtail = RcvReq; + } else { + RcvTCB->tcb_rcvtail->trr_next = RcvReq; + RcvTCB->tcb_rcvtail = RcvReq; + } + + // If this recv. can't hold urgent data or there isn't + // any pending urgent data continue processing. + if (!(UFlags & TDI_RECEIVE_EXPEDITED) || + RcvTCB->tcb_urgcnt == 0) { + // If tcb_currcv is NULL, there is no currently + // active receive. In this case, check to see if + // there is pending data and that we are not + // currently in a receive indication handler. If + // both of these are true then deal with the + // pending data. + if (RcvTCB->tcb_currcv == NULL) { + RcvTCB->tcb_currcv = RcvReq; + // No currently active receive. + if (!(RcvTCB->tcb_flags & IN_RCV_IND)) { + // Not in a rcv. indication. + RcvTCB->tcb_rcvhndlr = BufferData; + if (RcvTCB->tcb_pendhead == NULL) { + CTEFreeLock(&RcvTCB->tcb_lock, + ConnTableHandle); + return TDI_PENDING; + } else { + IPRcvBuf *PendBuffer; + uint PendSize; + uint OldRcvWin; + + // We have pending data to deal with. + PendBuffer = RcvTCB->tcb_pendhead; + PendSize = RcvTCB->tcb_pendingcnt; + RcvTCB->tcb_pendhead = NULL; + RcvTCB->tcb_pendingcnt = 0; + RcvTCB->tcb_refcnt++; + + // We assume that BufferData holds + // the lock (does not yield) during + // this call. If this changes for some + // reason, we'll have to fix the code + // below that does the window update + // check. See the comments in the + // BufferData() routine for more info. +#ifdef VXD + CTEFreeLock(&RcvTCB->tcb_lock, + ConnTableHandle); + (void)BufferData(RcvTCB, TCP_FLAG_PUSH, + PendBuffer, PendSize); + CTEGetLock(&RcvTCB->tcb_lock, + &ConnTableHandle); +#else + (void)BufferData(RcvTCB, TCP_FLAG_PUSH, + PendBuffer, PendSize); +#endif + CheckTCBRcv(RcvTCB); + // Now we need to see if the window + // has changed. If it has, send an + // ACK. + OldRcvWin = RcvTCB->tcb_rcvwin; + if (OldRcvWin != RcvWin(RcvTCB)) { + // The window has changed, so send + // an ACK. + + DelayAction(RcvTCB, NEED_ACK); + } + + DerefTCB(RcvTCB, ConnTableHandle); + ProcessTCBDelayQ(); + return TDI_PENDING; + } + } + // In a receive indication. The recv. request + // is now on the queue, so just fall through + // to the return. + + } + // A rcv. is currently active. No need to do + // anything else. + CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle); + return TDI_PENDING; + } else { + // This buffer can hold urgent data and we have + // some pending. Deliver it now. + RcvTCB->tcb_refcnt++; + DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle); + DerefTCB(RcvTCB, ConnTableHandle); + return TDI_PENDING; + } + } else { + TCPRcvReq *Temp; + + // This is an expedited only receive. Just put him + // on the end of the expedited receive queue. + Temp = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_exprcv, + trr_next); + while (Temp->trr_next != NULL) + Temp = Temp->trr_next; + + RcvReq->trr_next = NULL; + Temp->trr_next = RcvReq; + if (RcvTCB->tcb_urgpending != NULL) { + RcvTCB->tcb_refcnt++; + DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle); + DerefTCB(RcvTCB, ConnTableHandle); + return TDI_PENDING; + } else + Error = TDI_PENDING; + } + } else { + // Couldn't get a rcv. req. + Error = TDI_NO_RESOURCES; + } + } else { + // The TCB is in an invalid state. + Error = TDI_INVALID_STATE; + } + CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle); + return Error; + } else // No TCB for connection. + Error = TDI_INVALID_STATE; + } else // No connection. + Error = TDI_INVALID_CONNECTION; + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return Error; + +} diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeliv.h b/private/ntos/tdi/tcpip/tcp/tcpdeliv.h new file mode 100644 index 000000000..94fe73a7d --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpdeliv.h @@ -0,0 +1,44 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPDELIV.H - TCP data delivery definitions. +// +// This file contains the definitions for structures used by the data +// delivery code. +// + +extern void FreeRcvReq(struct TCPRcvReq *FreedReq); + +extern uint IndicateData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, + uint Size); +extern uint BufferData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, + uint Size); +extern uint PendData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, + uint Size); + +#ifdef VXD +extern void IndicatePendingData(struct TCB *RcvTCB, struct TCPRcvReq *RcvReq); +#else +extern void IndicatePendingData(struct TCB *RcvTCB, struct TCPRcvReq *RcvReq, + CTELockHandle TCBHandle); +#endif + +extern void HandleUrgent(struct TCB *RcvTCB, struct TCPRcvInfo *RcvInfo, + IPRcvBuf *RcvBuf, uint *Size); + +extern TDI_STATUS TdiReceive(PTDI_REQUEST Request, ushort *Flags, + uint *RcvLength, PNDIS_BUFFER Buffer); +extern IPRcvBuf *FreePartialRB(IPRcvBuf *RB, uint Size); +extern void PushData(struct TCB *PushTCB); + +EXTERNAL_LOCK(TCPRcvReqFreeLock) // Protects rcv req free list. + +#ifdef NT +extern SLIST_HEADER TCPRcvReqFree; +#endif + + + diff --git a/private/ntos/tdi/tcpip/tcp/tcpip.def b/private/ntos/tdi/tcpip/tcp/tcpip.def new file mode 100644 index 000000000..c02c158c1 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpip.def @@ -0,0 +1,8 @@ + +NAME TCPIP.SYS + +DESCRIPTION 'TCPIP.SYS' + +EXPORTS + IPAddInterface + IPDelInterface diff --git a/private/ntos/tdi/tcpip/tcp/tcpip.rc b/private/ntos/tdi/tcpip/tcp/tcpip.rc new file mode 100644 index 000000000..bd0b7a4ad --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpip.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "TCP/IP driver" + +#define VER_INTERNALNAME_STR "tcpip.sys" +#define VER_ORIGINALFILENAME_STR "tcpip.sys" + +#include diff --git a/private/ntos/tdi/tcpip/tcp/tcprcv.c b/private/ntos/tdi/tcpip/tcp/tcprcv.c new file mode 100644 index 000000000..46698a2b0 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcprcv.c @@ -0,0 +1,3397 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPRCV.C - TCP receive protocol code. +// +// This file contains the code for handling incoming TCP packets. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "tcpdeliv.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + +uint RequestCompleteFlags; + +Queue ConnRequestCompleteQ; +Queue SendCompleteQ; + +Queue TCBDelayQ; + +#ifdef SYN_ATTACK +DEFINE_LOCK_STRUCTURE(SynAttLock) +#endif +DEFINE_LOCK_STRUCTURE(RequestCompleteLock) +DEFINE_LOCK_STRUCTURE(TCBDelayLock) + +ulong TCBDelayRtnCount; +ulong TCBDelayRtnLimit; +#define TCB_DELAY_RTN_LIMIT 4 + +EXTERNAL_LOCK(TCBTableLock) +EXTERNAL_LOCK(AddrObjTableLock) +EXTERNAL_LOCK(ConnTableLock) + +extern IPInfo LocalNetInfo; + +#define PERSIST_TIMEOUT MS_TO_TICKS(500) + + +void ResetSendNext(TCB *SeqTCB, SeqNum NewSeq); + +#if FAST_RETRANSMIT +extern uint MaxDupAcks; +void ResetAndFastSend(TCB *SeqTCB, SeqNum NewSeq); +#endif + + +#ifdef NT + +NTSTATUS +TCPPrepareIrpForCancel( + PTCP_CONTEXT TcpContext, + PIRP Irp, + PDRIVER_CANCEL CancelRoutine + ); + +extern void +TCPRequestComplete( + void *Context, + unsigned int Status, + unsigned int UnUsed + ); + +VOID +TCPCancelRequest( + PDEVICE_OBJECT Device, + PIRP Irp + ); + +// +// All of the init code can be discarded. +// +#ifdef ALLOC_PRAGMA + +int InitTCPRcv(void); +void UnInitTCPRcv(void); + +#pragma alloc_text(INIT, InitTCPRcv) +#pragma alloc_text(INIT, UnInitTCPRcv) + +#endif // ALLOC_PRAGMA + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +#endif + +#endif // NT + +//* AdjustRcvWin - Adjust the receive window on a TCB. +// +// A utility routine that adjusts the receive window to an even multiple of +// the local segment size. We round it up to the next closest multiple, or +// leave it alone if it's already an event multiple. We assume we have +// exclusive access to the input TCB. +// +// Input: WinTCB - TCB to be adjusted. +// +// Returns: Nothing. +// +void +AdjustRcvWin(TCB *WinTCB) +{ + ushort LocalMSS; + uchar FoundMSS; + ulong SegmentsInWindow; + + CTEAssert(WinTCB->tcb_defaultwin != 0); + CTEAssert(WinTCB->tcb_rcvwin != 0); + CTEAssert(WinTCB->tcb_remmss != 0); + + if (WinTCB->tcb_flags & WINDOW_SET) + return; + + // First, get the local MSS by calling IP. + + FoundMSS = (*LocalNetInfo.ipi_getlocalmtu)(WinTCB->tcb_saddr, &LocalMSS); + + // If we didn't find it, error out. + if (!FoundMSS) { + CTEAssert(FALSE); + return; + } + + LocalMSS -= sizeof(TCPHeader); + LocalMSS = MIN(LocalMSS, WinTCB->tcb_remmss); + + SegmentsInWindow = WinTCB->tcb_defaultwin / (ulong)LocalMSS; + + // Make sure we have at least 4 segments in window, if that wouldn't make + // the window too big. + if (SegmentsInWindow < 4) { + + // We have fewer than four segments in the window. Round up to 4 + // if we can do so without exceeding the maximum window size; otherwise + // use the maximum multiple that we can fit in 64K. The exception is if + // we can only fit one integral multiple in the window - in that case + // we'll use a window of 0xffff. + if (LocalMSS <= (0xffff/4)) { + WinTCB->tcb_defaultwin = (uint)(4 * LocalMSS); + } else { + ulong SegmentsInMaxWindow; + + // Figure out the maximum number of segments we could possibly + // fit in a window. If this is > 1, use that as the basis for + // our window size. Otherwise use a maximum size window. + + SegmentsInMaxWindow = 0xffff/(ulong)LocalMSS; + if (SegmentsInMaxWindow != 1) + WinTCB->tcb_defaultwin = SegmentsInMaxWindow * (ulong)LocalMSS; + else + WinTCB->tcb_defaultwin = 0xffff; + } + + WinTCB->tcb_rcvwin = WinTCB->tcb_defaultwin; + + } else + // If it's not already an even multiple, bump the default and current + // windows to the nearest multiple. + if ((SegmentsInWindow * (ulong)LocalMSS) != WinTCB->tcb_defaultwin) { + ulong NewWindow; + + NewWindow = (SegmentsInWindow + 1) * (ulong)LocalMSS; + + // Don't let the new window be > 64K. + if (NewWindow <= 0xffff) { + WinTCB->tcb_defaultwin = (uint)NewWindow; + WinTCB->tcb_rcvwin = (uint)NewWindow; + } + } + +} + +//* CompleteRcvs - Complete rcvs on a TCB. +// +// Called when we need to complete rcvs on a TCB. We'll pull things from +// the TCB's rcv queue, as long as there are rcvs that have the PUSH bit +// set. +// +// Input: CmpltTCB - TCB to complete on. +// +// Returns: Nothing. +// +void +CompleteRcvs(TCB *CmpltTCB) +{ + CTELockHandle TCBHandle; + TCPRcvReq *CurrReq, *NextReq, *IndReq; + + CTEStructAssert(CmpltTCB, tcb); + CTEAssert(CmpltTCB->tcb_refcnt != 0); + + CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle); + + if (!CLOSING(CmpltTCB) && !(CmpltTCB->tcb_flags & RCV_CMPLTING) + && (CmpltTCB->tcb_rcvhead != NULL)) { + + CmpltTCB->tcb_flags |= RCV_CMPLTING; + + for (;;) { + + CurrReq = CmpltTCB->tcb_rcvhead; + IndReq = NULL; + do { + CTEStructAssert(CurrReq, trr); + + if (CurrReq->trr_flags & TRR_PUSHED) { + // Need to complete this one. If this is the current rcv + // advance the current rcv to the next one in the list. + // Then set the list head to the next one in the list. + + CTEAssert(CurrReq->trr_amt != 0 || + !DATA_RCV_STATE(CmpltTCB->tcb_state)); + + NextReq = CurrReq->trr_next; + if (CmpltTCB->tcb_currcv == CurrReq) + CmpltTCB->tcb_currcv = NextReq; + + CmpltTCB->tcb_rcvhead = NextReq; + + if (NextReq == NULL) { + // We've just removed the last buffer. Set the + // rcvhandler to PendData, in case something + // comes in during the callback. + CTEAssert(CmpltTCB->tcb_rcvhndlr != IndicateData); + CmpltTCB->tcb_rcvhndlr = PendData; + } + + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + if (CurrReq->trr_uflags != NULL) + *(CurrReq->trr_uflags) = + TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE; + + (*CurrReq->trr_rtn)(CurrReq->trr_context, TDI_SUCCESS, + CurrReq->trr_amt); + if (IndReq != NULL) + FreeRcvReq(CurrReq); + else + IndReq = CurrReq; + CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle); + CurrReq = CmpltTCB->tcb_rcvhead; + + } else + // This one isn't to be completed, so bail out. + break; + } while (CurrReq != NULL); + + // Now see if we've completed all of the requests. If we have, we + // may need to deal with pending data and/or reset the rcv. handler. + if (CurrReq == NULL) { + // We've completed everything that can be, so stop the push + // timer. We don't stop it if CurrReq isn't NULL because we + // want to make sure later data is eventually pushed. + STOP_TCB_TIMER(CmpltTCB->tcb_pushtimer); + + CTEAssert(IndReq != NULL); + // No more recv. requests. + if (CmpltTCB->tcb_pendhead == NULL) { + FreeRcvReq(IndReq); + // No pending data. Set the rcv. handler to either PendData + // or IndicateData. + if (!(CmpltTCB->tcb_flags & (DISC_PENDING | GC_PENDING))) { + if (CmpltTCB->tcb_rcvind != NULL && + CmpltTCB->tcb_indicated == 0) + CmpltTCB->tcb_rcvhndlr = IndicateData; + else + CmpltTCB->tcb_rcvhndlr = PendData; + } else { + goto Complete_Notify; + } + + } else { + // We have pending data to deal with. + if (CmpltTCB->tcb_rcvind != NULL && + CmpltTCB->tcb_indicated == 0) { + // There's a rcv. indicate handler on this TCB. Call + // the indicate handler with the pending data. +#ifdef VXD + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + IndicatePendingData(CmpltTCB, IndReq); + SendACK(CmpltTCB); + CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle); +#else + IndicatePendingData(CmpltTCB, IndReq, TCBHandle); + SendACK(CmpltTCB); + CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle); +#endif + // See if a buffer has been posted. If so, we'll need + // to check and see if it needs to be completed. + if (CmpltTCB->tcb_rcvhead != NULL) + continue; + else { + // If the pending head is now NULL, we've used up + // all the data. + if (CmpltTCB->tcb_pendhead == NULL && + (CmpltTCB->tcb_flags & + (DISC_PENDING | GC_PENDING))) + goto Complete_Notify; + } + + } else { + // No indicate handler, so nothing to do. The rcv. + // handler should already be set to PendData. + FreeRcvReq(IndReq); + CTEAssert(CmpltTCB->tcb_rcvhndlr == PendData); + } + } + } else { + if (IndReq != NULL) + FreeRcvReq(IndReq); + CTEAssert(CmpltTCB->tcb_rcvhndlr == BufferData); + } + + break; + } + CmpltTCB->tcb_flags &= ~RCV_CMPLTING; + } + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + return; + +Complete_Notify: + // Something is pending. Figure out what it is, and do + // it. + if (CmpltTCB->tcb_flags & GC_PENDING) { + CmpltTCB->tcb_flags &= ~RCV_CMPLTING; + // Bump the refcnt, because GracefulClose will + // deref the TCB and we're not really done with + // it yet. + CmpltTCB->tcb_refcnt++; + GracefulClose(CmpltTCB, + CmpltTCB->tcb_flags & TW_PENDING, TRUE, + TCBHandle); + + } else + if (CmpltTCB->tcb_flags & DISC_PENDING) { + CmpltTCB->tcb_flags &= ~DISC_PENDING; + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + NotifyOfDisc(CmpltTCB, NULL, TDI_GRACEFUL_DISC); + + CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle); + CmpltTCB->tcb_flags &= ~RCV_CMPLTING; + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + } else { + CTEAssert(FALSE); + CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle); + } + + return; + +} + +//* ProcessTCBDelayQ - Process TCBs on the delayed Q. +// +// Called at various times to process TCBs on the delayed Q. +// +// Entry: Nothing. +// +// Returns: Nothing. +// +void +ProcessTCBDelayQ(void) +{ + CTELockHandle QHandle; + TCB *DelayTCB; + CTELockHandle TCBHandle; + + CTEGetLock(&TCBDelayLock, &QHandle); + + // Check for recursion. We do not stop recursion completely, only + // limit it. This is done to allow multiple threads to process the + // TCBDelayQ simultaneously. + + TCBDelayRtnCount++; + if (TCBDelayRtnCount > TCBDelayRtnLimit) { + TCBDelayRtnCount--; + CTEFreeLock(&TCBDelayLock, QHandle); + return; + } + + while (!EMPTYQ(&TCBDelayQ)) { + + DEQUEUE(&TCBDelayQ, DelayTCB, TCB, tcb_delayq); + CTEStructAssert(DelayTCB, tcb); + CTEAssert(DelayTCB->tcb_refcnt != 0); + CTEAssert(DelayTCB->tcb_flags & IN_DELAY_Q); + CTEFreeLock(&TCBDelayLock, QHandle); + + CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle); + + while (!CLOSING(DelayTCB) && (DelayTCB->tcb_flags & DELAYED_FLAGS)) { + + if (DelayTCB->tcb_flags & NEED_RCV_CMPLT) { + DelayTCB->tcb_flags &= ~NEED_RCV_CMPLT; + CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle); + CompleteRcvs(DelayTCB); + CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle); + } + + if (DelayTCB->tcb_flags & NEED_OUTPUT) { + DelayTCB->tcb_flags &= ~NEED_OUTPUT; + DelayTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle); + TCPSend(DelayTCB); +#else + TCPSend(DelayTCB, TCBHandle); +#endif + CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle); + } + + if (DelayTCB->tcb_flags & NEED_ACK) { + DelayTCB->tcb_flags &= ~NEED_ACK; + CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle); + SendACK(DelayTCB); + CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle); + } + + } + + DelayTCB->tcb_flags &= ~IN_DELAY_Q; + DerefTCB(DelayTCB, TCBHandle); + CTEGetLock(&TCBDelayLock, &QHandle); + + } + + TCBDelayRtnCount--; + CTEFreeLock(&TCBDelayLock, QHandle); + +} + +//* DelayAction - Put a TCB on the queue for a delayed action. +// +// Called when we want to put a TCB on the DelayQ for a delayed action at +// rcv. complete or some other time. The lock on the TCB must be held when +// this is called. +// +// Input: DelayTCB - TCB which we're going to sched. +// Action - Action we're scheduling. +// +// Returns: Nothing. +// +void +DelayAction(TCB *DelayTCB, uint Action) +{ + CTELockHandle DQHandle; + + // Schedule the completion. + CTEGetLockAtDPC(&TCBDelayLock, &DQHandle); + DelayTCB->tcb_flags |= Action; + if (!(DelayTCB->tcb_flags & IN_DELAY_Q)) { + DelayTCB->tcb_flags |= IN_DELAY_Q; + DelayTCB->tcb_refcnt++; // Reference this for later. + ENQUEUE(&TCBDelayQ, &DelayTCB->tcb_delayq); + } + CTEFreeLockFromDPC(&TCBDelayLock, DQHandle); + +} + +//* TCPRcvComplete - Handle a receive complete. +// +// Called by the lower layers when we're done receiving. We look to see if +// we have and pending requests to complete. If we do, we complete them. Then +// we look to see if we have any TCBs pending for output. If we do, we +// get them going. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +TCPRcvComplete(void) +{ + CTELockHandle CompleteHandle; + TCPReq *Req; + + if (RequestCompleteFlags & ANY_REQUEST_COMPLETE) { + CTEGetLock(&RequestCompleteLock, &CompleteHandle); + if (!(RequestCompleteFlags & IN_RCV_COMPLETE)) { + RequestCompleteFlags |= IN_RCV_COMPLETE; + do { + if (RequestCompleteFlags & CONN_REQUEST_COMPLETE) { + if (!EMPTYQ(&ConnRequestCompleteQ)) { + DEQUEUE(&ConnRequestCompleteQ, Req, TCPReq, tr_q); + CTEStructAssert(Req, tr); + CTEStructAssert(*(TCPConnReq **)&Req, tcr); + + CTEFreeLock(&RequestCompleteLock, CompleteHandle); + (*Req->tr_rtn)(Req->tr_context, Req->tr_status, 0); + FreeConnReq((TCPConnReq *)Req); + CTEGetLock(&RequestCompleteLock, &CompleteHandle); + + } else + RequestCompleteFlags &= ~CONN_REQUEST_COMPLETE; + } + + if (RequestCompleteFlags & SEND_REQUEST_COMPLETE) { + if (!EMPTYQ(&SendCompleteQ)) { + TCPSendReq *SendReq; + + DEQUEUE(&SendCompleteQ, Req, TCPReq, tr_q); + CTEStructAssert(Req, tr); + SendReq = (TCPSendReq *)Req; + CTEStructAssert(SendReq, tsr); + + CTEFreeLock(&RequestCompleteLock, CompleteHandle); + (*Req->tr_rtn)(Req->tr_context, Req->tr_status, + Req->tr_status == TDI_SUCCESS ? SendReq->tsr_size + : 0); + FreeSendReq((TCPSendReq *)Req); + CTEGetLock(&RequestCompleteLock, &CompleteHandle); + + } else + RequestCompleteFlags &= ~SEND_REQUEST_COMPLETE; + } + + } while (RequestCompleteFlags & ANY_REQUEST_COMPLETE); + + RequestCompleteFlags &= ~IN_RCV_COMPLETE; + } + CTEFreeLock(&RequestCompleteLock, CompleteHandle); + } + + ProcessTCBDelayQ(); + +} + +//* CompleteConnReq - Complete a connection request on a TCB. +// +// A utility function to complete a connection request on a TCB. We remove +// the connreq, and put it on the ConnReqCmpltQ where it will be picked +// off later during RcvCmplt processing. We assume the TCB lock is held when +// we're called. +// +// Input: CmpltTCB - TCB from which to complete. +// OptInfo - IP OptInfo for completeion. +// Status - Status to complete with. +// +// Returns: Nothing. +// +void +CompleteConnReq(TCB *CmpltTCB, IPOptInfo *OptInfo, TDI_STATUS Status) +{ + TCPConnReq *ConnReq; + CTELockHandle QueueHandle; + + CTEStructAssert(CmpltTCB, tcb); + + ConnReq = CmpltTCB->tcb_connreq; + if (ConnReq != NULL) { + + // There's a connreq on this TCB. Fill in the connection information + // before returning it. + + CmpltTCB->tcb_connreq = NULL; + UpdateConnInfo(ConnReq->tcr_conninfo, OptInfo, CmpltTCB->tcb_daddr, + CmpltTCB->tcb_dport); + + ConnReq->tcr_req.tr_status = Status; + CTEGetLockAtDPC(&RequestCompleteLock, &QueueHandle); + RequestCompleteFlags |= CONN_REQUEST_COMPLETE; + ENQUEUE(&ConnRequestCompleteQ, &ConnReq->tcr_req.tr_q); + CTEFreeLockFromDPC(&RequestCompleteLock, QueueHandle); + } else + DEBUGCHK; + +} + + +#ifdef SYN_ATTACK +void +SynAttChk ( AddrObj *ListenAO ) +// +// function to check whether certain thresholds relevant to containing a +// SYN attack are being crossed. +// +// This function is called from FindListenConn when a connection has been +// found to handle the SYN request +// +{ + BOOLEAN RexmitCntChanged = FALSE; + CTELockHandle Handle; + + CTEGetLockAtDPC(&SynAttLock, &Handle); + + // + // We are putting a connection in the syn_rcvd state. Check + // if we have reached the threshold. If we have reduce the + // number of retries to a lower value. + // + if ((++TCPHalfOpen >= TCPMaxHalfOpen) && (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + if (TCPHalfOpenRetried >= TCPMaxHalfOpenRetried) { + MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT; + RexmitCntChanged = TRUE; + } + } + + // + // if this connection limit for a port was reached earlier. + // Check if the lower watermark is getting hit now. + // + + if (ListenAO->ConnLimitReached) + { + ListenAO->ConnLimitReached = FALSE; + if (!RexmitCntChanged && (MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + + CTEAssert(TCPPortsExhausted > 0); + // + // The fact that FindListenConn found a connection on the port + // indicates that we had a connection available. This port + // was therefore not exhausted of connections. Set state + // appropriately. If the port has no more connections now, + // it will get added to the Exhausted count next time a syn for + // the port comes along. + // + if (--TCPPortsExhausted <= TCPMaxPortsExhaustedLW) { + MaxConnectResponseRexmitCountTmp = + MAX_CONNECT_RESPONSE_REXMIT_CNT; + } + } + } + + CTEFreeLockFromDPC(&SynAttLock, Handle); + return; +} +#endif + + +//* FindListenConn - Find (or fabricate) a listening connection. +// +// Called by our Receive handler to decide what to do about an incoming +// SYN. We walk down the list of connections associated with the destination +// address, and if we find any in the listening state that can be used for +// the incoming request we'll take them, possibly returning a listen in the +// process. If we don't find any appropriate listening connections, we'll +// call the Connect Event handler if one is registerd. If all else fails, +// we'll return NULL and the SYN will be RST. +// +// The caller must hold the AddrObjTableLock before calling this routine, +// and that lock must have been taken at DPC level. This routine will free +// that lock back to DPC level. +// +// Input: ListenAO - Pointer to AddrObj for local address. +// Src - Source IP address of SYN. +// SrcPort - Source port of SYN. +// OptInfo - IP options info from SYN. +// +// Returns: Pointer to found TCB, or NULL if we can't find one. +// +TCB * +FindListenConn(AddrObj *ListenAO, IPAddr Src, ushort SrcPort, IPOptInfo *OptInfo) +{ + CTELockHandle Handle; // Lock handle on AO, TCB. + TCB *CurrentTCB = NULL; + TCPConn *CurrentConn = NULL; + TCPConnReq *ConnReq = NULL; + CTELockHandle ConnHandle; + Queue *Temp; + uint FoundConn = FALSE; + + CTEStructAssert(ListenAO, ao); + + CTEGetLockAtDPC(&ConnTableLock, &ConnHandle); + CTEGetLockAtDPC(&ListenAO->ao_lock, &Handle); + +#ifdef NT + CTEFreeLockFromDPC(&AddrObjTableLock, DISPATCH_LEVEL); +#endif + + + // We have the lock on the AddrObj. Walk down it's list, looking + // for connections in the listening state. + + if (AO_VALID(ListenAO)) { + if (ListenAO->ao_listencnt != 0) { + CTELockHandle TCBHandle; + + Temp = QHEAD(&ListenAO->ao_listenq); + while (Temp != QEND(&ListenAO->ao_listenq)) { + + CurrentConn = QSTRUCT(TCPConn, Temp, tc_q); + CTEStructAssert(CurrentConn, tc); + + // If this TCB is in the listening state, with no delete + // pending, it's a candidate. Look at the pending listen + // info. to see if we should take it. + if ((CurrentTCB = CurrentConn->tc_tcb) != NULL) { + + CTEStructAssert(CurrentTCB, tcb); + CTEAssert(CurrentTCB->tcb_state == TCB_LISTEN); + + CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle); + + if (CurrentTCB->tcb_state == TCB_LISTEN && + !PENDING_ACTION(CurrentTCB)) { + + // Need to see if we can take it. + // See if the addresses specifed in the ConnReq + // match. + if ((IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, + NULL_IP_ADDR) || + IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, + Src)) && + (CurrentTCB->tcb_dport == 0 || + CurrentTCB->tcb_dport == SrcPort)) { + FoundConn = TRUE; + break; + } + + // Otherwise, this didn't match, so we'll check the + // next one. + } + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + } + + Temp = QNEXT(Temp);; + } + + // See why we've exited the loop. + if (FoundConn) { + CTEStructAssert(CurrentTCB, tcb); + + // We exited because we found a TCB. If it's pre-accepted, + // we're done. + CurrentTCB->tcb_refcnt++; + + CTEAssert(CurrentTCB->tcb_connreq != NULL); + + ConnReq = CurrentTCB->tcb_connreq; + // If QUERY_ACCEPT isn't set, turn on the CONN_ACCEPTED bit. + if (!(ConnReq->tcr_flags & TDI_QUERY_ACCEPT)) + CurrentTCB->tcb_flags |= CONN_ACCEPTED; + + CurrentTCB->tcb_state = TCB_SYN_RCVD; + + ListenAO->ao_listencnt--; + + // Since he's no longer listening, remove him from the listen + // queue and put him on the active queue. + REMOVEQ(&CurrentConn->tc_q); + ENQUEUE(&ListenAO->ao_activeq, &CurrentConn->tc_q); +#ifdef SYN_ATTACK + if (SynAttackProtect) { + SynAttChk(ListenAO); + } +#endif + + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle); + CTEFreeLockFromDPC(&ConnTableLock, ConnHandle); + return CurrentTCB; + } else { + // Since we have a listening count, this should never happen + // if that count was non-zero initially. + CTEAssert(FALSE); + } + } + + // We didn't find a matching TCB. If there's a connect indication + // handler, call it now to find a connection to accept on. + + CTEAssert(FoundConn == FALSE); + + if (ListenAO->ao_connect != NULL) { + uchar TAddress[TCP_TA_SIZE]; + PVOID ConnContext; + PConnectEvent Event; + PVOID EventContext; + TDI_STATUS Status; + TCB *AcceptTCB; + TCPConnReq *ConnReq; +#ifdef NT + ConnectEventInfo *EventInfo; +#else + ConnectEventInfo EventInfo; +#endif + + + // He has a connect handler. Put the transport address together, + // and call him. We also need to get the necessary resources + // first. + AcceptTCB = AllocTCB(); + ConnReq = GetConnReq(); + + if (AcceptTCB != NULL && ConnReq != NULL) { + Event = ListenAO->ao_connect; + EventContext = ListenAO->ao_conncontext; + + BuildTDIAddress(TAddress, Src, SrcPort); + REF_AO(ListenAO); + + AcceptTCB->tcb_state = TCB_LISTEN; + AcceptTCB->tcb_connreq = ConnReq; + AcceptTCB->tcb_flags |= CONN_ACCEPTED; + + CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle); + CTEFreeLockFromDPC(&ConnTableLock, ConnHandle); + + IF_TCPDBG(TCP_DEBUG_CONNECT) { + TCPTRACE(("indicating connect request\n")); + } + + Status = (*Event)(EventContext, TCP_TA_SIZE, + (PTRANSPORT_ADDRESS)TAddress, 0, NULL, + OptInfo->ioi_optlength, OptInfo->ioi_options, + &ConnContext, &EventInfo); + + if (Status == TDI_MORE_PROCESSING) { +#ifdef NT + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_ACCEPT AcceptRequest; + + IrpSp = IoGetCurrentIrpStackLocation(EventInfo); + + Status = TCPPrepareIrpForCancel( + (PTCP_CONTEXT) IrpSp->FileObject->FsContext, + EventInfo, + TCPCancelRequest + ); + + if (!NT_SUCCESS(Status)) { + Status = TDI_NOT_ACCEPTED; + EventInfo = NULL; + goto AcceptIrpCancelled; + } + +#endif // NT + + // He accepted it. Find the connection on the AddrObj. + CTEGetLockAtDPC(&ConnTableLock, &ConnHandle); + CTEGetLockAtDPC(&ListenAO->ao_lock, &Handle); +#ifdef NT + { + + IF_TCPDBG(TCP_DEBUG_CONNECT) { + TCPTRACE(( + "connect indication accepted, queueing request\n" + )); + } + + AcceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT) + &(IrpSp->Parameters); + ConnReq->tcr_conninfo = + AcceptRequest->ReturnConnectionInformation; + ConnReq->tcr_req.tr_rtn = TCPRequestComplete; + ConnReq->tcr_req.tr_context = EventInfo; + + } +#else // NT + ConnReq->tcr_req.tr_rtn = EventInfo.cei_rtn; + ConnReq->tcr_req.tr_context = EventInfo.cei_context; + ConnReq->tcr_conninfo = EventInfo.cei_conninfo; +#endif // NT + Temp = QHEAD(&ListenAO->ao_idleq);; + CurrentTCB = NULL; + Status = TDI_INVALID_CONNECTION; + + while (Temp != QEND(&ListenAO->ao_idleq)) { + + CurrentConn = QSTRUCT(TCPConn, Temp, tc_q); + + CTEStructAssert(CurrentConn, tc); + if ((CurrentConn->tc_context == ConnContext) && + !(CurrentConn->tc_flags & CONN_INVALID)) { + + // We think we have a match. The connection + // shouldn't have a TCB associated with it. If it + // does, it's an error. InitTCBFromConn will + // handle all this. + + AcceptTCB->tcb_refcnt = 1; +#ifdef NT + Status = InitTCBFromConn(CurrentConn, AcceptTCB, + AcceptRequest->RequestConnectionInformation, + TRUE); +#else // NT + Status = InitTCBFromConn(CurrentConn, AcceptTCB, + EventInfo.cei_acceptinfo, + TRUE); +#endif // NT + + if (Status == TDI_SUCCESS) { + FoundConn = TRUE; + AcceptTCB->tcb_state = TCB_SYN_RCVD; + AcceptTCB->tcb_conn = CurrentConn; + CurrentConn->tc_tcb = AcceptTCB; + CurrentConn->tc_refcnt++; + + // Move him from the idle q to the active + // queue. + REMOVEQ(&CurrentConn->tc_q); + ENQUEUE(&ListenAO->ao_activeq, &CurrentConn->tc_q); + } + + // In any case, we're done now. + break; + + } + Temp = QNEXT(Temp); + } + + if (!FoundConn) { + // Didn't find a match, or had an error. Status + // code is set. + // Complete the ConnReq and free the resources. + CompleteConnReq(AcceptTCB, OptInfo, Status); + FreeTCB(AcceptTCB); + AcceptTCB = NULL; + } +#ifdef SYN_ATTACK + else { + if (SynAttackProtect) { + SynAttChk(ListenAO); + } + } +#endif + + LOCKED_DELAY_DEREF_AO(ListenAO); + CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle); + CTEFreeLockFromDPC(&ConnTableLock, ConnHandle); + + return AcceptTCB; + } +#ifdef SYN_ATTACK + + if (SynAttackProtect) { + CTELockHandle Handle; + + // + // If we need to Trigger to a lower retry count + // + + if (!ListenAO->ConnLimitReached) { + ListenAO->ConnLimitReached = TRUE; + CTEGetLockAtDPC(&SynAttLock, &Handle); + if ((++TCPPortsExhausted >= TCPMaxPortsExhausted) && + (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + + MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT; + } + CTEFreeLockFromDPC(&SynAttLock, Handle); + } + } +#endif + +#ifdef NT + +AcceptIrpCancelled: + +#endif // NT + // The event handler didn't take it. Dereference it, free + // the resources, and return NULL. + FreeConnReq(ConnReq); + FreeTCB(AcceptTCB); + DELAY_DEREF_AO(ListenAO); + return NULL; + + } else { + // We couldn't get a needed resource. Free any that we + // did get, and fall through to the 'return NULL' code. + if (ConnReq != NULL) + FreeConnReq(ConnReq); + if (AcceptTCB != NULL) + FreeTCB(AcceptTCB); + } + + } +#ifdef SYN_ATTACK + else { + if (SynAttackProtect) { + CTELockHandle Handle; + + // + // If we need to Trigger to a lower retry count + // + + if (!ListenAO->ConnLimitReached) { + ListenAO->ConnLimitReached = TRUE; + CTEGetLockAtDPC(&SynAttLock, &Handle); + if ((++TCPPortsExhausted >= TCPMaxPortsExhausted) && + (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + + MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT; + } + CTEFreeLockFromDPC(&SynAttLock, Handle); + } + } + } +#endif + + // No event handler, or no resource. Free the locks, and return NULL. + CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle); + CTEFreeLockFromDPC(&ConnTableLock, ConnHandle); + return NULL; + } + + // If we get here, the address object wasn't valid. + CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle); + CTEFreeLockFromDPC(&ConnTableLock, ConnHandle); + return NULL; +} + + +//* FindMSS - Find the MSS option in a segment. +// +// Called when a SYN is received to find the MSS option in a segment. If we +// don't find one, we assume the worst and return 536. +// +// Input: TCPH - TCP header to be searched. +// +// Returns: MSS to be used. +// +ushort +FindMSS(TCPHeader UNALIGNED *TCPH) +{ + uint OptSize; + uchar *OptPtr; + + OptSize = TCP_HDR_SIZE(TCPH) - sizeof(TCPHeader); + + OptPtr = (uchar *)(TCPH + 1); + + while (OptSize) { + + if (*OptPtr == TCP_OPT_EOL) + break; + + if (*OptPtr == TCP_OPT_NOP) { + OptPtr++; + OptSize--; + continue; + } + + if (*OptPtr == TCP_OPT_MSS) { + if (OptPtr[1] == MSS_OPT_SIZE) { + ushort TempMss = *(ushort UNALIGNED *)(OptPtr + 2); + if (TempMss != 0) + return net_short(TempMss); + else + break; // MSS size of 0, use default. + } else + break; // Bad option size, use default. + } else { + // Unknown option. + if (OptPtr[1] == 0 || OptPtr[1] > OptSize) + break; // Bad option length, bail out. + + OptSize -= OptPtr[1]; + OptPtr += OptPtr[1]; + } + } + + return MAX_REMOTE_MSS; + +} + +//* ACKAndDrop - Acknowledge a segment, and drop it. +// +// Called from within the receive code when we need to drop a segment that's +// outside the receive window. +// +// Input: RI - Receive info for incoming segment. +// RcvTCB - TCB for incoming segment. +// +// Returns: Nothing. +// +void +ACKAndDrop(TCPRcvInfo *RI, TCB *RcvTCB) +{ + CTELockHandle Handle; + +#ifdef VXD +#ifdef DEBUG + Handle = DEFAULT_SIMIRQL; +#endif +#else + Handle = DISPATCH_LEVEL; +#endif + + if (!(RI->tri_flags & TCP_FLAG_RST)) { + + if (RcvTCB->tcb_state == TCB_TIME_WAIT) + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, MAX_REXMIT_TO); + + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, Handle); + + SendACK(RcvTCB); + + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &Handle); + } + DerefTCB(RcvTCB, Handle); + +} + +//* ACKData - Acknowledge data. +// +// Called from the receive handler to acknowledge data. We're given the +// TCB and the new value of senduna. We walk down the send q. pulling +// off sends and putting them on the complete q until we hit the end +// or we acknowledge the specified number of bytes of data. +// +// NOTE: We manipulate the send refcnt and acked flag without taking a lock. +// This is OK in the VxD version where locks don't mean anything anyway, but +// in the port to NT we'll need to add locking. The lock will have to be +// taken in the transmit complete routine. We can't use a lock in the TCB, +// since the TCB could go away before the transmit complete happens, and a lock +// in the TSR would be overkill, so it's probably best to use a global lock +// for this. If that causes too much contention, we could use a set of locks +// and pass a pointer to the appropriate lock back as part of the transmit +// confirm context. This lock pointer would also need to be stored in the +// TCB. +// +// Input: ACKTcb - TCB from which to pull data. +// SendUNA - New value of send una. +// +// Returns: Nothing. +// +void +ACKData(TCB *ACKTcb, SeqNum SendUNA) +{ + Queue *End, *Current; // End and current elements. + Queue *TempQ, *EndQ; + Queue *LastCmplt; // Last one we completed. + TCPSendReq *CurrentTSR; // Current send req we're + // looking at. + PNDIS_BUFFER CurrentBuffer; // Current NDIS_BUFFER. + uint Updated = FALSE; + uint BufLength; + int Amount, OrigAmount; + long Result; + CTELockHandle Handle; + uint Temp; + + CTEStructAssert(ACKTcb, tcb); + + CheckTCBSends(ACKTcb); + + Amount = SendUNA - ACKTcb->tcb_senduna; + CTEAssert(Amount > 0); + + // Do a quick check to see if this acks everything that we have. If it does, + // handle it right away. We can only do this in the ESTABLISHED state, + // because we blindly update sendnext, and that can only work if we + // haven't sent a FIN. + if ((Amount == (int) ACKTcb->tcb_unacked) && ACKTcb->tcb_state == TCB_ESTAB) { + + // Everything is acked. + CTEAssert(!EMPTYQ(&ACKTcb->tcb_sendq)); + + TempQ = ACKTcb->tcb_sendq.q_next; + + INITQ(&ACKTcb->tcb_sendq); + + ACKTcb->tcb_sendnext = SendUNA; + ACKTcb->tcb_senduna = SendUNA; + + CTEAssert(ACKTcb->tcb_sendnext == ACKTcb->tcb_sendmax); + ACKTcb->tcb_cursend = NULL; + ACKTcb->tcb_sendbuf = NULL; + ACKTcb->tcb_sendofs = 0; + ACKTcb->tcb_sendsize = 0; + ACKTcb->tcb_unacked = 0; + + // Now walk down the list of send requests. If the reference count + // has gone to 0, put it on the send complete queue. + CTEGetLock(&RequestCompleteLock, &Handle); + EndQ = &ACKTcb->tcb_sendq; + do { + CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q), + tsr_req); + + CTEStructAssert(CurrentTSR, tsr); + + TempQ = CurrentTSR->tsr_req.tr_q.q_next; + + CurrentTSR->tsr_req.tr_status = TDI_SUCCESS; + Result = CTEInterlockedDecrementLong(&CurrentTSR->tsr_refcnt); + + CTEAssert(Result >= 0); + + + if (Result <= 0) { + // No more references are outstanding, the send can be + // completed. + + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (CurrentTSR->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(CurrentTSR->tsr_lastbuf) = NULL; + CurrentTSR->tsr_lastbuf = NULL; + } + ACKTcb->tcb_totaltime += (TCPTime - CurrentTSR->tsr_time); + Temp = ACKTcb->tcb_bcountlow; + ACKTcb->tcb_bcountlow += CurrentTSR->tsr_size; + ACKTcb->tcb_bcounthi += (Temp > ACKTcb->tcb_bcountlow ? 1 : 0); + + ENQUEUE(&SendCompleteQ, &CurrentTSR->tsr_req.tr_q); + } + + } while (TempQ != EndQ); + + RequestCompleteFlags |= SEND_REQUEST_COMPLETE; + CTEFreeLock(&RequestCompleteLock, Handle); + + CheckTCBSends(ACKTcb); + return; + } + + OrigAmount = Amount; + End = QEND(&ACKTcb->tcb_sendq); + Current = QHEAD(&ACKTcb->tcb_sendq); + + LastCmplt = NULL; + + while (Amount > 0 && Current != End) { + CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q), + tsr_req); + CTEStructAssert(CurrentTSR, tsr); + + + if (Amount >= (int) CurrentTSR->tsr_unasize) { + // This is completely acked. Just advance to the next one. + Amount -= CurrentTSR->tsr_unasize; + + LastCmplt = Current; + + Current = QNEXT(Current); + continue; + } + + // This one is only partially acked. Update his offset and NDIS buffer + // pointer, and break out. We know that Amount is < the unacked size + // in this buffer, we we can walk the NDIS buffer chain without fear + // of falling off the end. + CurrentBuffer = CurrentTSR->tsr_buffer; + CTEAssert(CurrentBuffer != NULL); + CTEAssert(Amount < (int) CurrentTSR->tsr_unasize); + CurrentTSR->tsr_unasize -= Amount; + + BufLength = NdisBufferLength(CurrentBuffer) - CurrentTSR->tsr_offset; + + if (Amount >= (int) BufLength) { + do { + Amount -= BufLength; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + CTEAssert(CurrentBuffer != NULL); + BufLength = NdisBufferLength(CurrentBuffer); + } while (Amount >= (int) BufLength); + + CurrentTSR->tsr_offset = Amount; + CurrentTSR->tsr_buffer = CurrentBuffer; + + } else + CurrentTSR->tsr_offset += Amount; + + Amount = 0; + + break; + } + +#ifdef DEBUG + // We should always be able to remove at least Amount bytes, except in + // the case where a FIN has been sent. In that case we should be off + // by exactly one. In the debug builds we'll check this. + if (Amount != 0 && (!(ACKTcb->tcb_flags & FIN_SENT) || Amount != 1)) + DEBUGCHK; +#endif + + if (SEQ_GT(SendUNA, ACKTcb->tcb_sendnext)) { + + if (Current != End) { + // Need to reevaluate CurrentTSR, in case we bailed out of the + // above loop after updating Current but before updating + // CurrentTSR. + CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q), + tsr_req); + CTEStructAssert(CurrentTSR, tsr); + ACKTcb->tcb_cursend = CurrentTSR; + ACKTcb->tcb_sendbuf = CurrentTSR->tsr_buffer; + ACKTcb->tcb_sendofs = CurrentTSR->tsr_offset; + ACKTcb->tcb_sendsize = CurrentTSR->tsr_unasize; + } else { + ACKTcb->tcb_cursend = NULL; + ACKTcb->tcb_sendbuf = NULL; + ACKTcb->tcb_sendofs = 0; + ACKTcb->tcb_sendsize = 0; + } + + ACKTcb->tcb_sendnext = SendUNA; + } + + // Now update tcb_unacked with the amount we tried to ack minus the + // amount we didn't ack (Amount should be 0 or 1 here). + CTEAssert(Amount == 0 || Amount == 1); + + ACKTcb->tcb_unacked -= OrigAmount - Amount; + CTEAssert(*(int *)&ACKTcb->tcb_unacked >= 0); + + ACKTcb->tcb_senduna = SendUNA; + + // If we've acked any here, LastCmplt will be non-null, and Current will + // point to the send that should be at the start of the queue. Splice + // out the completed ones and put them on the end of the send completed + // queue, and update the TCB send q. + if (LastCmplt != NULL) { + Queue *FirstCmplt; + TCPSendReq *FirstTSR, *EndTSR; + + CTEAssert(!EMPTYQ(&ACKTcb->tcb_sendq)); + + FirstCmplt = QHEAD(&ACKTcb->tcb_sendq); + + // If we've acked everything, just reinit the queue. + if (Current == End) { + INITQ(&ACKTcb->tcb_sendq); + } else { + // There's still something on the queue. Just update it. + ACKTcb->tcb_sendq.q_next = Current; + Current->q_prev = &ACKTcb->tcb_sendq; + } + + CheckTCBSends(ACKTcb); + + // Now walk down the lists of things acked. If the refcnt on the send + // is 0, go ahead and put him on the send complete Q. Otherwise set + // the ACKed bit in the send, and he'll be completed when the count + // goes to 0 in the transmit confirm. + // + // Note that we haven't done any locking here. This will probably + // need to change in the port to NT. + + // Set FirstTSR to the first TSR we'll complete, and EndTSR to be + // the first TSR that isn't completed. + + FirstTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, FirstCmplt, tr_q), + tsr_req); + EndTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q), + tsr_req); + + CTEStructAssert(FirstTSR, tsr); + CTEAssert(FirstTSR != EndTSR); + + // Now walk the list of ACKed TSRs. If we can complete one, put him + // on the complete queue. + CTEGetLockAtDPC(&RequestCompleteLock, &Handle); + while (FirstTSR != EndTSR) { + + + TempQ = QNEXT(&FirstTSR->tsr_req.tr_q); + + CTEStructAssert(FirstTSR, tsr); + FirstTSR->tsr_req.tr_status = TDI_SUCCESS; + + // The tsr_lastbuf->Next field is zapped to 0 when the tsr_refcnt + // goes to 0, so we don't need to do it here. + + // Decrement the reference put on the send buffer when it was + // initialized indicating the send has been acknowledged. + Result = CTEInterlockedDecrementLong(&(FirstTSR->tsr_refcnt)); + + CTEAssert(Result >= 0); + if (Result <= 0) { + // No more references are outstanding, the send can be + // completed. + + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (FirstTSR->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(FirstTSR->tsr_lastbuf) = NULL; + FirstTSR->tsr_lastbuf = NULL; + } + + ACKTcb->tcb_totaltime += (TCPTime - CurrentTSR->tsr_time); + Temp = ACKTcb->tcb_bcountlow; + ACKTcb->tcb_bcountlow += CurrentTSR->tsr_size; + ACKTcb->tcb_bcounthi += (Temp > ACKTcb->tcb_bcountlow ? 1 : 0); + ENQUEUE(&SendCompleteQ, &FirstTSR->tsr_req.tr_q); + } + + FirstTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q), + tsr_req); + } + RequestCompleteFlags |= SEND_REQUEST_COMPLETE; + CTEFreeLockFromDPC(&RequestCompleteLock, Handle); + } + +} + +//* TrimRcvBuf - Trim the front edge of a receive buffer. +// +// A utility routine to trim the front of a receive buffer. We take in a +// a count (which may be 0) and adjust the pointer in the first buffer in +// the chain by that much. If there isn't that much in the first buffer, +// we move onto the next one. If we run out of buffers we'll return a pointer +// to the last buffer in the chain, with a size of 0. It's the caller's +// responsibility to catch this. +// +// Input: RcvBuf - Buffer to be trimmed. +// Count - Amount to be trimmed. +// +// Returns: A pointer to the new start, or NULL. +// +IPRcvBuf * +TrimRcvBuf(IPRcvBuf *RcvBuf, uint Count) +{ + uint TrimThisTime; + + CTEAssert(RcvBuf != NULL); + + while (Count) { + CTEAssert(RcvBuf != NULL); + + TrimThisTime = MIN(Count, RcvBuf->ipr_size); + Count -= TrimThisTime; + RcvBuf->ipr_buffer += TrimThisTime; + if ((RcvBuf->ipr_size -= TrimThisTime) == 0) { + if (RcvBuf->ipr_next != NULL) + RcvBuf = RcvBuf->ipr_next; + else { + // Ran out of buffers. Just return this one. + break; + } + } + } + + return RcvBuf; + +} + +//* FreeRBChain - Free an RB chain. +// +// Called to free a chain of RBs. If we're the owner of each RB, we'll +// free it. +// +// Input: RBChain - RBChain to be freed. +// +// Returns: Nothing. +// +void +FreeRBChain(IPRcvBuf *RBChain) +{ + while (RBChain != NULL) { + + if (RBChain->ipr_owner == IPR_OWNER_TCP) { + IPRcvBuf *Temp; + + Temp = RBChain->ipr_next; + CTEFreeMem(RBChain); + RBChain = Temp; + } else + RBChain = RBChain->ipr_next; + } + +} + +IPRcvBuf DummyBuf; + +//* PullFromRAQ - Pull segments from the reassembly queue. +// +// Called when we've received frames out of order, and have some segments +// on the reassembly queue. We'll walk down the reassembly list, segments that +// are overlapped by the current rcv. next variable. When we get +// to one that doesn't completely overlap we'll trim it to fit the next +// rcv. seq. number, and pull it from the queue. +// +// Input: RcvTCB - TCB to pull from. +// RcvInfo - Pointer to TCPRcvInfo structure for current seg. +// Size - Pointer to size for current segment. We'll update +// this when we're done. +// +// Returns: Nothing. +// +IPRcvBuf * +PullFromRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, uint *Size) +{ + TCPRAHdr *CurrentTRH; // Current TCP RA Header being examined. + TCPRAHdr *TempTRH; // Temporary variable. + SeqNum NextSeq; // Next sequence number we want. + IPRcvBuf *NewBuf; + SeqNum NextTRHSeq; // Seq. number immediately after + // current TRH. + int Overlap; // Overlap between current TRH and + // NextSeq. + + CTEStructAssert(RcvTCB, tcb); + + CurrentTRH = RcvTCB->tcb_raq; + NextSeq = RcvTCB->tcb_rcvnext; + + while (CurrentTRH != NULL) { + CTEStructAssert(CurrentTRH, trh); + CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_SYN)); + + // If the flags for the current reassembly segment contains a FIN, + // it should be the last segment on the queue. This assert checks + // that. + CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_FIN) || + CurrentTRH->trh_next == NULL); + + if (SEQ_LT(NextSeq, CurrentTRH->trh_start)) { +#ifdef DEBUG + *Size = 0; +#endif + return NULL; // The next TRH starts too far down. + } + + + NextTRHSeq = CurrentTRH->trh_start + CurrentTRH->trh_size + + ((CurrentTRH->trh_flags & TCP_FLAG_FIN) ? 1 : 0); + + if (SEQ_GTE(NextSeq, NextTRHSeq)) { + // The current TRH is overlapped completely. Free it and continue. + FreeRBChain(CurrentTRH->trh_buffer); + TempTRH = CurrentTRH->trh_next; + CTEFreeMem(CurrentTRH); + CurrentTRH = TempTRH; + RcvTCB->tcb_raq = TempTRH; + if (TempTRH == NULL) { + // We've just cleaned off the RAQ. We can go back on the + // fast path now. + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + break; + } + } else { + Overlap = NextSeq - CurrentTRH->trh_start; + RcvInfo->tri_seq = NextSeq; + RcvInfo->tri_flags = CurrentTRH->trh_flags; + RcvInfo->tri_urgent = CurrentTRH->trh_urg; + + if (Overlap != (int) CurrentTRH->trh_size) { + NewBuf = FreePartialRB(CurrentTRH->trh_buffer, Overlap); + *Size = CurrentTRH->trh_size - Overlap; + } else { + // This completely overlaps the data in this segment, but the + // sequence number doesn't overlap completely. There must + // be a FIN in the TRH. If we called FreePartialRB with this + // we'd end up returning NULL, which is the signal for failure. + // Instead we'll just return some bogus value that nobody + // will look at with a size of 0. + FreeRBChain(CurrentTRH->trh_buffer); + CTEAssert(CurrentTRH->trh_flags & TCP_FLAG_FIN); + NewBuf = &DummyBuf; + *Size = 0; + } + + RcvTCB->tcb_raq = CurrentTRH->trh_next; + if (RcvTCB->tcb_raq == NULL) { + // We've just cleaned off the RAQ. We can go back on the + // fast path now. + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + + } + CTEFreeMem(CurrentTRH); + return NewBuf; + } + + + } + +#ifdef DEBUG + *Size = 0; +#endif + return NULL; + +} + +//* CreateTRH - Create a TCP reassembly header. +// +// This function tries to create a TCP reassembly header. We take as input +// a pointer to the previous TRH in the chain, the RcvBuffer to put on, +// etc. and try to create and link in a TRH. The caller must hold the lock +// on the TCB when this is called. +// +// Input: PrevTRH - Pointer to TRH to insert after. +// RcvBuf - Pointer to IP RcvBuf chain. +// RcvInfo - Pointer to RcvInfo for this TRH. +// Size - Size in bytes of data. +// +// Returns: TRUE if we created it, FALSE otherwise. +// +uint +CreateTRH(TCPRAHdr *PrevTRH, IPRcvBuf *RcvBuf, TCPRcvInfo *RcvInfo, int Size) +{ + TCPRAHdr *NewTRH; + IPRcvBuf *NewRcvBuf; + + CTEAssert((Size > 0) || (RcvInfo->tri_flags & TCP_FLAG_FIN)); + + NewTRH = CTEAllocMem(sizeof(TCPRAHdr)); + if (NewTRH == NULL) + return FALSE; + + NewRcvBuf = CTEAllocMem(sizeof(IPRcvBuf) + Size); + if (NewRcvBuf == NULL) { + CTEFreeMem(NewTRH); + return FALSE; + } + +#ifdef DEBUG + NewTRH->trh_sig = trh_signature; +#endif + NewRcvBuf->ipr_owner = IPR_OWNER_TCP; + NewRcvBuf->ipr_size = (uint)Size; + NewRcvBuf->ipr_next = NULL; + NewRcvBuf->ipr_buffer = (uchar *)(NewRcvBuf + 1); + if (Size != 0) + CopyRcvToBuffer(NewRcvBuf->ipr_buffer, RcvBuf, Size, 0); + + NewTRH->trh_start = RcvInfo->tri_seq; + NewTRH->trh_flags = RcvInfo->tri_flags; + NewTRH->trh_size = Size; + NewTRH->trh_urg = RcvInfo->tri_urgent; + NewTRH->trh_buffer = NewRcvBuf; + NewTRH->trh_end = NewRcvBuf; + + NewTRH->trh_next = PrevTRH->trh_next; + PrevTRH->trh_next = NewTRH; + return TRUE; + +} + +//* PutOnRAQ - Put a segment on the reassembly queue. +// +// Called during segment reception to put a segment on the reassembly +// queue. We try to use as few reassembly headers as possible, so if this +// segment has some overlap with an existing entry in the queue we'll just +// update the existing entry. If there is no overlap we'll create a new +// reassembly header. Combining URGENT data with non-URGENT data is tricky. +// If we get a segment that has urgent data that overlaps the front of a +// reassembly header we'll always mark the whole chunk as urgent - the value +// of the urgent pointer will mark the end of urgent data, so this is OK. If it +// only overlaps at the end, however, we won't combine, since we would have to +// mark previously non-urgent data as urgent. We'll trim the +// front of the incoming segment and create a new reassembly header. Also, +// if we have non-urgent data that overlaps at the front of a reassembly +// header containing urgent data we can't combine these two, since again we +// would mark non-urgent data as urgent. +// Our search will stop if we find an entry with a FIN. +// We assume that the TCB lock is held by the caller. +// +// Entry: RcvTCB - TCB on which to reassemble. +// RcvInfo - Pointer to RcvInfo for new segment. +// RcvBuf - IP RcvBuf chain for this segment. +// Size - Size in bytes of data in this segment. +// +// Returns: Nothing. +// +void +PutOnRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint Size) +{ + TCPRAHdr *PrevTRH, *CurrentTRH; // Prev. and current TRH + // pointers. + SeqNum NextSeq; // Seq. number of first byte + // after segment being + // reassembled. + SeqNum NextTRHSeq; // Seq. number of first byte + // after current TRH. + uint Created; + + CTEStructAssert(RcvTCB, tcb); + CTEAssert(RcvTCB->tcb_rcvnext != RcvInfo->tri_seq); + CTEAssert(!(RcvInfo->tri_flags & TCP_FLAG_SYN)); + NextSeq = RcvInfo->tri_seq + Size + + ((RcvInfo->tri_flags & TCP_FLAG_FIN) ? 1 : 0); + + PrevTRH = STRUCT_OF(TCPRAHdr, &RcvTCB->tcb_raq, trh_next); + CurrentTRH = PrevTRH->trh_next; + + // Walk down the reassembly queue, looking for the correct place to + // insert this, until we hit the end. + while (CurrentTRH != NULL) { + CTEStructAssert(CurrentTRH, trh); + + CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_SYN)); + NextTRHSeq = CurrentTRH->trh_start + CurrentTRH->trh_size + + ((CurrentTRH->trh_flags & TCP_FLAG_FIN) ? 1 : 0); + + // First, see if it starts beyond the end of the current TRH. + if (SEQ_LTE(RcvInfo->tri_seq, NextTRHSeq)) { + // We know the incoming segment doesn't start beyond the end + // of this TRH, so we'll either create a new TRH in front of + // this one or we'll merge the new segment onto this TRH. + // If the end of the current segment is in front of the start + // of the current TRH, we'll need to create a new TRH. Otherwise + // we'll merge these two. + if (SEQ_LT(NextSeq, CurrentTRH->trh_start)) + break; + else { + // There's some overlap. If there's actually data in the + // incoming segment we'll merge it. + if (Size != 0) { + int FrontOverlap, BackOverlap; + IPRcvBuf *NewRB; + + // We need to merge. If there's a FIN on the incoming + // segment that would fall inside this current TRH, we + // have a protocol violation from the remote peer. In this + // case just return, discarding the incoming segment. + if ((RcvInfo->tri_flags & TCP_FLAG_FIN) && + SEQ_LTE(NextSeq, NextTRHSeq)) + return; + + // We have some overlap. Figure out how much. + FrontOverlap = CurrentTRH->trh_start - RcvInfo->tri_seq; + if (FrontOverlap > 0) { + // Have overlap in front. Allocate an IPRcvBuf to + // to hold it, and copy it, unless we would have to + // combine non-urgent with urgent. + if (!(RcvInfo->tri_flags & TCP_FLAG_URG) && + (CurrentTRH->trh_flags & TCP_FLAG_URG)) { + if (CreateTRH(PrevTRH, RcvBuf, RcvInfo, + CurrentTRH->trh_start - RcvInfo->tri_seq)) { + PrevTRH = PrevTRH->trh_next; + CurrentTRH = PrevTRH->trh_next; + } + FrontOverlap = 0; + + } else { + NewRB = CTEAllocMem(sizeof(IPRcvBuf) + FrontOverlap); + if (NewRB == NULL) + return; // Couldn't get the buffer. + + NewRB->ipr_owner = IPR_OWNER_TCP; + NewRB->ipr_size = FrontOverlap; + NewRB->ipr_buffer = (uchar *)(NewRB + 1); + CopyRcvToBuffer(NewRB->ipr_buffer, RcvBuf, + FrontOverlap, 0); + CurrentTRH->trh_size += FrontOverlap; + NewRB->ipr_next = CurrentTRH->trh_buffer; + CurrentTRH->trh_buffer = NewRB; + CurrentTRH->trh_start = RcvInfo->tri_seq; + } + } + + // We've updated the starting sequence number of this TRH + // if we needed to. Now look for back overlap. There can't + // be any back overlap if the current TRH has a FIN. Also + // we'll need to check for urgent data if there is back + // overlap. + if (!(CurrentTRH->trh_flags & TCP_FLAG_FIN)) { + BackOverlap = RcvInfo->tri_seq + Size - NextTRHSeq; + if ((BackOverlap > 0) && + (RcvInfo->tri_flags & TCP_FLAG_URG) && + !(CurrentTRH->trh_flags & TCP_FLAG_URG) && + (FrontOverlap <= 0)) { + int AmountToTrim; + // The incoming segment has urgent data and overlaps + // on the back but not the front, and the current + // TRH has no urgent data. We can't combine into + // this TRH, so trim the front of the incoming + // segment to NextTRHSeq and move to the next + // TRH. + AmountToTrim = NextTRHSeq - RcvInfo->tri_seq; + CTEAssert(AmountToTrim >= 0); + CTEAssert(AmountToTrim < (int) Size); + RcvBuf = FreePartialRB(RcvBuf, (uint)AmountToTrim); + RcvInfo->tri_seq += AmountToTrim; + RcvInfo->tri_urgent -= AmountToTrim; + PrevTRH = CurrentTRH; + CurrentTRH = PrevTRH->trh_next; + continue; + } + + } else + BackOverlap = 0; + + // Now if we have back overlap, copy it. + if (BackOverlap > 0) { + // We have back overlap. Get a buffer to copy it into. + // If we can't get one, we won't just return, because + // we may have updated the front and may need to + // update the urgent info. + NewRB = CTEAllocMem(sizeof(IPRcvBuf) + BackOverlap); + if (NewRB != NULL) { + // Got the buffer. + NewRB->ipr_owner = IPR_OWNER_TCP; + NewRB->ipr_size = BackOverlap; + NewRB->ipr_buffer = (uchar *)(NewRB + 1); + CopyRcvToBuffer(NewRB->ipr_buffer, RcvBuf, + BackOverlap, NextTRHSeq - RcvInfo->tri_seq); + CurrentTRH->trh_size += BackOverlap; + NewRB->ipr_next = CurrentTRH->trh_end->ipr_next; + CurrentTRH->trh_end->ipr_next = NewRB; + CurrentTRH->trh_end = NewRB; + } + } + + // Everything should be consistent now. If there's an + // urgent data pointer in the incoming segment, update the + // one in the TRH now. + if (RcvInfo->tri_flags & TCP_FLAG_URG) { + SeqNum UrgSeq; + // Have an urgent pointer. If the current TRH already + // has an urgent pointer, see which is bigger. Otherwise + // just use this one. + UrgSeq = RcvInfo->tri_seq + RcvInfo->tri_urgent; + if (CurrentTRH->trh_flags & TCP_FLAG_URG) { + SeqNum TRHUrgSeq; + + TRHUrgSeq = CurrentTRH->trh_start + + CurrentTRH->trh_urg; + if (SEQ_LT(UrgSeq, TRHUrgSeq)) + UrgSeq = TRHUrgSeq; + } else + CurrentTRH->trh_flags |= TCP_FLAG_URG; + + CurrentTRH->trh_urg = UrgSeq - CurrentTRH->trh_start; + } + + } else { + // We have a 0 length segment. The only interesting thing + // here is if there's a FIN on the segment. If there is, + // and the seq. # of the incoming segment is exactly after + // the current TRH, OR matches the FIN in the current TRH, + // we note it. + if (RcvInfo->tri_flags & TCP_FLAG_FIN) { + if (!(CurrentTRH->trh_flags & TCP_FLAG_FIN)) { + if (SEQ_EQ(NextTRHSeq, RcvInfo->tri_seq)) + CurrentTRH->trh_flags |= TCP_FLAG_FIN; + else + DEBUGCHK; + } + else { + if ( !(SEQ_EQ((NextTRHSeq-1), RcvInfo->tri_seq)) ) { + DEBUGCHK; + } + } + } + } + return; + } + } else { + // Look at the next TRH, unless the current TRH has a FIN. If he + // has a FIN, we won't save any data beyond that anyway. + if (CurrentTRH->trh_flags & TCP_FLAG_FIN) + return; + + PrevTRH = CurrentTRH; + CurrentTRH = PrevTRH->trh_next; + } + } + + // When we get here, we need to create a new TRH. If we create one and + // there was previously nothing on the reassembly queue, we'll have to + // move off the fast receive path. + + CurrentTRH = RcvTCB->tcb_raq; + Created = CreateTRH(PrevTRH, RcvBuf, RcvInfo, (int)Size); + + if (Created && CurrentTRH == NULL) { + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + + +} + + +//* TCPRcv - Receive a TCP segment. +// +// This is the routine called by IP when we need to receive a TCP segment. +// In general, we follow the RFC 793 event processing section pretty closely, +// but there is a 'fast path' where we make some quick checks on the incoming +// segment, and if it matches we deliver it immediately. +// +// Entry: IPContext - IPContext identifying physical i/f that +// received the data. +// Dest - IPAddr of destionation. +// Src - IPAddr of source. +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the packet +// IPH - IP Header. +// IPHLength - Bytes in IPH. +// RcvBuf - Pointer to receive buffer chain containing data. +// Size - Size in bytes of data received. +// IsBCast - Boolean indicator of whether or not this came in as +// a bcast. +// Protocol - Protocol this came in on - should be TCP. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception. Anything other than IP_SUCCESS will cause +// IP to send a 'port unreachable' message. +// +IP_STATUS +TCPRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf, + uint Size, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo) +{ + TCPHeader UNALIGNED *TCPH; // The TCP header. + TCB *RcvTCB; // TCB on which to receive the packet. + CTELockHandle TableHandle, TCBHandle; + TCPRcvInfo RcvInfo; // Local swapped copy of rcv info. + uint DataOffset; // Offset from start of header to data. + uint Actions; + uint BytesTaken; + uint NewSize; + + CheckRBList(RcvBuf, Size); + + TStats.ts_insegs++; + + // Checksum it, to make sure it's valid. + TCPH = (TCPHeader *)RcvBuf->ipr_buffer; + + if (!IsBCast) { + + if (Size >= sizeof(TCPHeader) && XsumRcvBuf(PHXSUM(Src, Dest, PROTOCOL_TCP, + Size), RcvBuf) == 0xffff) { + + // The packet is valid. Get the info we need and byte swap it, + // and then try to find a matching TCB. + + RcvInfo.tri_seq = net_long(TCPH->tcp_seq); + RcvInfo.tri_ack = net_long(TCPH->tcp_ack); + RcvInfo.tri_window = (uint)net_short(TCPH->tcp_window); + RcvInfo.tri_urgent = (uint)net_short(TCPH->tcp_urgent); + RcvInfo.tri_flags = (uint)TCPH->tcp_flags; + DataOffset = TCP_HDR_SIZE(TCPH); + + if (DataOffset <= Size) { + + Size -= DataOffset; + CTEAssert(DataOffset <= RcvBuf->ipr_size); + RcvBuf->ipr_size -= DataOffset; + RcvBuf->ipr_buffer += DataOffset; + + CTEGetLockAtDPC(&TCBTableLock, &TableHandle); + RcvTCB = FindTCB(Dest, Src, TCPH->tcp_src, TCPH->tcp_dest); + if (RcvTCB != NULL) { + // Found one. Get the lock on it, and continue. + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TCBHandle); + CTEFreeLockFromDPC(&TCBTableLock, TCBHandle); + } else { + uchar DType; + + // Didn't find a matching TCB. If this segment carries a SYN, + // find a matching address object and see it it has a listen + // indication. If it does, call it. Otherwise send a RST + // back to the sender. + CTEFreeLockFromDPC(&TCBTableLock, TableHandle); + + + // Make sure that the source address isn't a broadcast + // before proceeding. + if ((*LocalNetInfo.ipi_invalidsrc)(Src)) + return IP_SUCCESS; + + // If it doesn't have a SYN (and only a SYN), we'll send a + // reset. + if ((RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_ACK | TCP_FLAG_RST)) == + TCP_FLAG_SYN) { + AddrObj *AO; + + // + // This segment had a SYN. + // + // +#ifdef NT + CTEGetLockAtDPC(&AddrObjTableLock, &TableHandle); +#endif + +#ifdef SECFLTR + // See if we are filtering the + // destination interface/port. + // + if ( (!SecurityFilteringEnabled || + IsPermittedSecurityFilter( + LocalAddr, + IPContext, + PROTOCOL_TCP, + (ulong) net_short(TCPH->tcp_dest) + )) + ) + { +#else // SECFLTR + if ( 1 ) { +#endif // SECFLTR + + // + // Find a matching address object, and then try and find a + // listening connection on that AO. + // + AO = GetBestAddrObj(Dest, TCPH->tcp_dest, PROTOCOL_TCP); + if (AO != NULL) { + + // Found an AO. Try and find a listening connection. + // FindListenConn will free the lock on the AddrObjTable. + RcvTCB = FindListenConn(AO, Src, TCPH->tcp_src, OptInfo); + + if (RcvTCB != NULL) { + uint Inserted; + + CTEStructAssert(RcvTCB, tcb); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + + // We found a listening connection. Initialize it + // now, and if it is actually to be accepted we'll + // send a SYN-ACK also. + + CTEAssert(RcvTCB->tcb_state == TCB_SYN_RCVD); + + RcvTCB->tcb_daddr = Src; + RcvTCB->tcb_saddr = Dest; + RcvTCB->tcb_dport = TCPH->tcp_src; + RcvTCB->tcb_sport = TCPH->tcp_dest; + RcvTCB->tcb_rcvnext = ++RcvInfo.tri_seq; + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_remmss = FindMSS(TCPH); + TStats.ts_passiveopens++; + RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV; + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + + Inserted = InsertTCB(RcvTCB); + + // Get the lock on it, and see if it's been + // accepted. + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + if (!Inserted) { + // Couldn't insert it!. + CompleteConnReq(RcvTCB, OptInfo, + TDI_CONNECTION_ABORTED); + RcvTCB->tcb_refcnt--; +#ifdef NT + TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, DISPATCH_LEVEL); +#else + TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, TableHandle); +#endif + return IP_SUCCESS; + } + + + RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV; + if (RcvTCB->tcb_flags & SEND_AFTER_RCV) { + RcvTCB->tcb_flags &= ~SEND_AFTER_RCV; + DelayAction(RcvTCB, NEED_OUTPUT); + } + + // We'll need to update the options, in any case. + if (OptInfo->ioi_options != NULL) { + if (!(RcvTCB->tcb_flags & CLIENT_OPTIONS)) { + (*LocalNetInfo.ipi_updateopts)(OptInfo, + &RcvTCB->tcb_opt, Src, NULL_IP_ADDR); + } + } + + if (RcvTCB->tcb_flags & CONN_ACCEPTED) { + + // The connection was accepted. Finish the + // initialization, and send the SYN ack. + +#ifdef NT + AcceptConn(RcvTCB, DISPATCH_LEVEL); +#else + AcceptConn(RcvTCB, TableHandle); +#endif + + return IP_SUCCESS; + } else { + + // We don't know what to do about the + // connection yet. Return the pending listen, + // dereference the connection, and return. + + CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS); + +#ifdef NT + DerefTCB(RcvTCB, DISPATCH_LEVEL); +#else + DerefTCB(RcvTCB, TableHandle); +#endif + + return IP_SUCCESS; + } + + } + // No listening connection. AddrObjTableLock was + // released by FindListenConn. Fall through to send + // RST code. + + } else { + // No address object. Free the lock, and fall through + // to the send RST code. + CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle); + } + } + else { + // Operation not permitted. Free the lock, and fall through + // to the send RST code. + CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle); + } + + } + + // Toss out any segments containing RST. + if (RcvInfo.tri_flags & TCP_FLAG_RST) + return IP_SUCCESS; + + // Not a SYN, no AddrObj available, or port filtered. + // Send a RST back. + SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); + + return IP_SUCCESS; + } + + // Do the fast path check. We can hit the fast path if the incoming + // sequence number matches our receive next and the masked flags + // match our 'predicted' flags. + CheckTCBRcv(RcvTCB); + RcvTCB->tcb_alive = TCPTime; + + if (RcvTCB->tcb_rcvnext == RcvInfo.tri_seq && + (RcvInfo.tri_flags & TCP_FLAGS_ALL) == RcvTCB->tcb_fastchk){ + + Actions = 0; + RcvTCB->tcb_refcnt++; + + // The fast path. We know all we have to do here is ack sends and + // deliver data. First try and ack data. + + + if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + + uint CWin; + uint MSS; + + // The ack acknowledes something. Pull the + // appropriate amount off the send q. + ACKData(RcvTCB, RcvInfo.tri_ack); + + // If this acknowledges something we were running a RTT on, + // update that stuff now. + if (RcvTCB->tcb_rtt != 0 && SEQ_GT(RcvInfo.tri_ack, + RcvTCB->tcb_rttseq)) { + short RTT; + + RTT = (short)(TCPTime - RcvTCB->tcb_rtt); + RcvTCB->tcb_rtt = 0; + RTT -= (RcvTCB->tcb_smrtt >> 3); + RcvTCB->tcb_smrtt += RTT; + RTT = (RTT >= 0 ? RTT : -RTT); + RTT -= (RcvTCB->tcb_delta >> 3); + RcvTCB->tcb_delta += RTT; + RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB), + MIN_RETRAN_TICKS), MAX_REXMIT_TO); + } + + // Update the congestion window now. + CWin = RcvTCB->tcb_cwin; + MSS = RcvTCB->tcb_mss; + if (CWin < RcvTCB->tcb_maxwin) { + if (CWin < RcvTCB->tcb_ssthresh) + CWin += MSS; + else + CWin += (MSS * MSS)/CWin; + + RcvTCB->tcb_cwin = CWin; + } + + CTEAssert(*(int *)&RcvTCB->tcb_cwin > 0); + + // We've acknowledged something, so reset the rexmit count. + // If there's still stuff outstanding, restart the rexmit + // timer. + RcvTCB->tcb_rexmitcnt = 0; + if (SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + else + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, RcvTCB->tcb_rexmit); + + // Since we've acknowledged data, we need to update the window. + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + // We've updated the window, remember to send some more. + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + +#if FAST_RETRANSMIT + { + // If the receiver has already sent dup acks, but we are not + // sending because the SendWin is less than a segment, then + // to avoid time outs on the previous send (receiver is waiting for + // retransmitted data but we are not sending the segment..) prematurely + // timeout (set rexmittimer to 1 tick) + // + + int SendWin; + uint AmtOutstanding,AmtUnsent; + + AmtOutstanding = (uint)(RcvTCB->tcb_sendnext - + RcvTCB->tcb_senduna); + AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding; + + SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) - + AmtOutstanding); + + + if ((Size == 0) && + (SendWin < RcvTCB->tcb_mss) && (RcvTCB->tcb_dup > 0)) { + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, 1); + } + } + + RcvTCB->tcb_dup = 0; +#endif + + } else { + // It doesn't ack anything. If it's an ack for something + // larger than we've sent then ACKAndDrop it, otherwise + // ignore it. + if (SEQ_GT(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } else + + //SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax + // If the ack matches our existing UNA, we need to see if + // we can update the window. + // Or check if fast retransmit is needed + +#if FAST_RETRANSMIT + // If it is a pure duplicate ack, check if it is + // time to retransmit immediately + + if ( (Size == 0) && SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (RcvTCB->tcb_sendwin == RcvInfo.tri_window) ) { + + RcvTCB->tcb_dup++; + + if ((RcvTCB->tcb_dup == MaxDupAcks) ) { + + //Okay. Time to retransmit the segment the receiver is asking for + + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + + RcvTCB->tcb_rtt = 0; + + if (!(RcvTCB->tcb_flags & FLOW_CNTLD)) { + + // Don't let the slow start threshold go below 2 + // segments + + RcvTCB->tcb_ssthresh = + MAX( + MIN(RcvTCB->tcb_cwin,RcvTCB->tcb_sendwin) / 2, + (uint) RcvTCB->tcb_mss * 2 ); + RcvTCB->tcb_cwin = RcvTCB->tcb_mss; + } + + // Recall the segment in question and send it out + // Note that tcb_lock will be dereferenced by the caller + + ResetAndFastSend (RcvTCB, RcvTCB->tcb_senduna); + + return IP_SUCCESS; + + + } else if ((RcvTCB->tcb_dup > MaxDupAcks) ) { + + int SendWin; + uint AmtOutstanding,AmtUnsent; + + if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) || + (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) && + SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) { + + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, + RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + + // Since we've updated the window, remember to send + // some more. + + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + } + + // Update the cwin to reflect the fact that the dup ack + // indicates the previous frame was received by the + // receiver + + + RcvTCB->tcb_cwin += RcvTCB->tcb_mss; + + if ((RcvTCB->tcb_cwin+RcvTCB->tcb_mss) < RcvTCB->tcb_sendwin ) { + AmtOutstanding = (uint)(RcvTCB->tcb_sendnext - + RcvTCB->tcb_senduna); + AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding; + + SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) - + AmtOutstanding); + + if (SendWin < RcvTCB->tcb_mss) { + RcvTCB->tcb_force=1; + } + } + + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + + } else if ((RcvTCB->tcb_dup < MaxDupAcks)) { + + int SendWin; + uint AmtOutstanding,AmtUnsent; + + if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) || + (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) && + SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) { + + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, + RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + + // Since we've updated the window, remember to send + // some more. + } + + // Check if we need to set tcb_force. + + if ((RcvTCB->tcb_cwin+RcvTCB->tcb_mss) < RcvTCB->tcb_sendwin ) { + + AmtOutstanding = (uint)(RcvTCB->tcb_sendnext - + RcvTCB->tcb_senduna); + AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding; + + SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) - + AmtOutstanding); + if (SendWin < RcvTCB->tcb_mss){ + RcvTCB->tcb_force=1; + } + } + + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + + + } // End of all MaxDupAck cases + + } else { // not a pure duplicate ack (size == 0 ) + + // Size !=0 or recvr is advertizing new window. + // update the window and check if + // anything needs to be sent + + RcvTCB->tcb_dup = 0; + + if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) || + (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) && + SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) { + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, + RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + // Since we've updated the window, remember to send + // some more. + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + } + + } // for SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax) case + +#else //FAST_RETRANSMIT + + if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) || + (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) && + SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) { + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, + RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + // Since we've updated the window, remember to send + // some more. + Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0); + } +#endif //FAST_RETRANSMIT + + } + + + NewSize = MIN((int) Size, RcvTCB->tcb_rcvwin); + if (NewSize != 0) { + RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV; +#ifdef VXD + CTEFreeLock(&RcvTCB->tcb_lock, TableHandle); + BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, RcvInfo.tri_flags, + RcvBuf, NewSize); + CTEGetLock(&RcvTCB->tcb_lock, &TableHandle); +#else + BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, RcvInfo.tri_flags, + RcvBuf, NewSize); +#endif + RcvTCB->tcb_rcvnext += BytesTaken; + RcvTCB->tcb_rcvwin -= BytesTaken; + CheckTCBRcv(RcvTCB); + + RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV; + + Actions |= (RcvTCB->tcb_flags & SEND_AFTER_RCV ? + NEED_OUTPUT : 0); + + RcvTCB->tcb_flags &= ~SEND_AFTER_RCV; + if ((RcvTCB->tcb_flags & ACK_DELAYED) || (BytesTaken != NewSize)) + Actions |= NEED_ACK; + else { + RcvTCB->tcb_flags |= ACK_DELAYED; + START_TCB_TIMER(RcvTCB->tcb_delacktimer, DEL_ACK_TICKS); + } + } else { + // The new size is 0. If the original size was not 0, we must + // have a 0 rcv. win and hence need to send an ACK to this + // probe. + Actions |= (Size ? NEED_ACK : 0); + } + + if (Actions) + DelayAction(RcvTCB, Actions); + +#ifndef VXD + TableHandle = DISPATCH_LEVEL; +#endif + DerefTCB(RcvTCB, TableHandle); + + return IP_SUCCESS; + } + +#ifndef VXD + TableHandle = DISPATCH_LEVEL; +#endif + // Make sure we can handle this frame. We can't handle it if we're + // in SYN_RCVD and the accept is still pending, or we're in a + // non-established state and already in the receive handler. + if ((RcvTCB->tcb_state == TCB_SYN_RCVD && + !(RcvTCB->tcb_flags & CONN_ACCEPTED)) || + (RcvTCB->tcb_state != TCB_ESTAB && (RcvTCB->tcb_fastchk & + TCP_FLAG_IN_RCV))) { + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + return IP_SUCCESS; + } + + // If it's closed, it's a temporary zombie TCB. Reset the sender. + if (RcvTCB->tcb_state == TCB_CLOSED || CLOSING(RcvTCB) || + ((RcvTCB->tcb_flags & (GC_PENDING | TW_PENDING)) == GC_PENDING)) { + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); + return IP_SUCCESS; + } + + // At this point, we have a connection, and it's locked. Following + // the 'Segment Arrives' section of 793, the next thing to check is + // if this connection is in SynSent state. + + if (RcvTCB->tcb_state == TCB_SYN_SENT) { + + CTEAssert(RcvTCB->tcb_flags & ACTIVE_OPEN); + + // Check the ACK bit. Since we don't send data with our SYNs, the + // check we make is for the ack to exactly match our SND.NXT. + if (RcvInfo.tri_flags & TCP_FLAG_ACK) { + // ACK is set. + if (!SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendnext)) { + // Bad ACK value. + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + // Send a RST back at him. + SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); + return IP_SUCCESS; + } + } + + if (RcvInfo.tri_flags & TCP_FLAG_RST) { + // There's an acceptable RST. We'll persist here, sending + // another SYN in PERSIST_TIMEOUT ms, until we fail from too + // many retrys. + if (RcvTCB->tcb_rexmitcnt == MaxConnectRexmitCount) { + // We've had a positive refusal, and one more rexmit + // would time us out, so close the connection now. + CompleteConnReq(RcvTCB, OptInfo, TDI_CONN_REFUSED); + + TryToCloseTCB(RcvTCB, TCB_CLOSE_REFUSED, TableHandle); + } else { + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, PERSIST_TIMEOUT); + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + } + return IP_SUCCESS; + } + + // See if we have a SYN. If we do, we're going to change state + // somehow (either to ESTABLISHED or SYN_RCVD). + if (RcvInfo.tri_flags & TCP_FLAG_SYN) { + RcvTCB->tcb_refcnt++; + + // We have a SYN. Go ahead and record the sequence number and + // window info. + RcvTCB->tcb_rcvnext = ++RcvInfo.tri_seq; + + if (RcvInfo.tri_flags & TCP_FLAG_URG) { + // Urgent data. Update the pointer. + if (RcvInfo.tri_urgent != 0) + RcvInfo.tri_urgent--; + else + RcvInfo.tri_flags &= ~TCP_FLAG_URG; + } + + RcvTCB->tcb_remmss = FindMSS(TCPH); + + // If there are options, update them now. We already have an + // RCE open, so if we have new options we'll have to close + // it and open a new one. + if (OptInfo->ioi_options != NULL) { + if (!(RcvTCB->tcb_flags & CLIENT_OPTIONS)) { + (*LocalNetInfo.ipi_updateopts)(OptInfo, + &RcvTCB->tcb_opt, Src, NULL_IP_ADDR); + (*LocalNetInfo.ipi_closerce)(RcvTCB->tcb_rce); + InitRCE(RcvTCB); + } + } else{ + RcvTCB->tcb_mss = MIN(RcvTCB->tcb_mss, RcvTCB->tcb_remmss); + + CTEAssert(RcvTCB->tcb_mss > 0); + + } + + RcvTCB->tcb_rexmitcnt = 0; + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + + AdjustRcvWin(RcvTCB); + + if (RcvInfo.tri_flags & TCP_FLAG_ACK) { + // Our SYN has been acked. Update SND.UNA and stop the + // retrans timer. + RcvTCB->tcb_senduna = RcvInfo.tri_ack; + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = RcvInfo.tri_window; + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + GoToEstab(RcvTCB); + +#ifdef RASAUTODIAL + // + // Set a bit that informs TCBTimeout to notify + // the automatic connection driver of this new + // connection. Only set this flag if we + // have binded succesfully with the automatic + // connection driver. + // + if (fAcdLoadedG) + RcvTCB->tcb_flags |= ACD_CONN_NOTIF; +#endif // RASAUTODIAL + + // Remove whatever command exists on this connection. + CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS); + + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + SendACK(RcvTCB); + + // Now handle other data and controls. To do this we need + // to reaquire the lock, and make sure we haven't started + // closing it. + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + if (!CLOSING(RcvTCB)) { + // We haven't started closing it. Turn off the + // SYN flag and continue processing. + RcvInfo.tri_flags &= ~TCP_FLAG_SYN; + if ((RcvInfo.tri_flags & TCP_FLAGS_ALL) != TCP_FLAG_ACK || + Size != 0) + goto NotSYNSent; + } + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } else { + // A SYN, but not an ACK. Go to SYN_RCVD. + RcvTCB->tcb_state = TCB_SYN_RCVD; + RcvTCB->tcb_sendnext = RcvTCB->tcb_senduna; + SendSYN(RcvTCB, TableHandle); + + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } + + } else { + // No SYN, just toss the frame. + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + return IP_SUCCESS; + } + + } + + RcvTCB->tcb_refcnt++; + +NotSYNSent: + // Not in the SYN-SENT state. Check the sequence number. If my window + // is 0, I'll truncate all incoming frames but look at some of the + // control fields. Otherwise I'll try and make this segment fit into + // the window. + if (RcvTCB->tcb_rcvwin != 0) { + int StateSize; // Size, including state info. + SeqNum LastValidSeq; // Sequence number of last valid + // byte at RWE. + + // We are offering a window. If this segment starts in front of my + // receive window, clip off the front part. +#if 1 // Bug #63900 + //Check for the sanity of received sequence. + //This is to fix the 1 bit error(MSB) case in the rcv seq. + // Also, check the incoming size. + + + if ((SEQ_LT(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) && + ((int)Size >= 0) && + (RcvTCB->tcb_rcvnext - RcvInfo.tri_seq ) > 0) { + +#else + + if (SEQ_LT(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) { +#endif + + int AmountToClip, FinByte; + + if (RcvInfo.tri_flags & TCP_FLAG_SYN) { + // Had a SYN. Clip it off and update the sequence number. + RcvInfo.tri_flags &= ~TCP_FLAG_SYN; + RcvInfo.tri_seq++; + RcvInfo.tri_urgent--; + } + + // Advance the receive buffer to point at the new data. + AmountToClip = RcvTCB->tcb_rcvnext - RcvInfo.tri_seq; + CTEAssert(AmountToClip >= 0); + + // If there's a FIN on this segment, we'll need to account for + // it. + FinByte = ((RcvInfo.tri_flags & TCP_FLAG_FIN) ? 1: 0); + + if (AmountToClip >= (((int) Size) + FinByte)) { + // Falls entirely before the window. We have more special + // case code here - if the ack. number acks something, + // we'll go ahead and take it, faking the sequence number + // to be rcvnext. This prevents problems on full duplex + // connections, where data has been received but not acked, + // and retransmission timers reset the seq. number to + // below our rcvnext. + if ((RcvInfo.tri_flags & TCP_FLAG_ACK) && + SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + // This contains valid ACK info. Fudge the information + // to get through the rest of this. + Size = 0; + AmountToClip = 0; + RcvInfo.tri_seq = RcvTCB->tcb_rcvnext; + RcvInfo.tri_flags &= ~(TCP_FLAG_SYN | TCP_FLAG_FIN | + TCP_FLAG_RST | TCP_FLAG_URG); +#ifdef DEBUG + FinByte = 1; // Fake out assert below. +#endif + } else { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } + } + + // Trim what we have to. If we can't trim enough, the frame + // is too short. This shouldn't happen, but it it does we'll + // drop the frame. + Size -= AmountToClip; + RcvInfo.tri_seq += AmountToClip; + RcvInfo.tri_urgent -= AmountToClip; + RcvBuf = TrimRcvBuf(RcvBuf, AmountToClip); + CTEAssert(RcvBuf != NULL); + CTEAssert(RcvBuf->ipr_size != 0 || + (Size == 0 && FinByte)); + + if (*(int *)&RcvInfo.tri_urgent < 0) { + RcvInfo.tri_urgent = 0; + RcvInfo.tri_flags &= ~TCP_FLAG_URG; + } + + } + + // We've made sure the front is OK. Now make sure part of it doesn't + // fall outside of the right edge of the window. If it does, + // we'll truncate the frame (removing the FIN, if any). If we + // truncate the whole frame we'll ACKAndDrop it. + StateSize = Size + ((RcvInfo.tri_flags & TCP_FLAG_SYN) ? 1: 0) + + ((RcvInfo.tri_flags & TCP_FLAG_FIN) ? 1: 0); + + if (StateSize) + StateSize--; + + // Now the incoming sequence number (RcvInfo.tri_seq) + StateSize + // it the last sequence number in the segment. If this is greater + // than the last valid byte in the window, we have some overlap + // to chop off. + + CTEAssert(StateSize >= 0); + LastValidSeq = RcvTCB->tcb_rcvnext + RcvTCB->tcb_rcvwin - 1; + if (SEQ_GT(RcvInfo.tri_seq + StateSize, LastValidSeq)) { + int AmountToChop; + + // At least some part of the frame is outside of our window. + // See if it starts outside our window. + + if (SEQ_GT(RcvInfo.tri_seq, LastValidSeq)) { + // Falls entirely outside the window. We have special + // case code to deal with a pure ack that falls exactly at + // our right window edge. Otherwise we ack and drop it. + if (!SEQ_EQ(RcvInfo.tri_seq, LastValidSeq+1) || Size != 0 + || (RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_FIN))) { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } + } else { + + // At least some part of it is in the window. If there's a + // FIN, chop that off and see if that moves us inside. + if (RcvInfo.tri_flags & TCP_FLAG_FIN) { + RcvInfo.tri_flags &= ~TCP_FLAG_FIN; + StateSize--; + } + + // Now figure out how much to chop off. + AmountToChop = (RcvInfo.tri_seq + StateSize) - LastValidSeq; + CTEAssert(AmountToChop >= 0); + Size -= AmountToChop; + + } + } + } else { + if (!SEQ_EQ(RcvTCB->tcb_rcvnext, RcvInfo.tri_seq)) { + + // If there's a RST on this segment, and he's only off by 1, + // take it anyway. This can happen if the remote peer is + // probing and sends with the seq. # after the probe. + if (!(RcvInfo.tri_flags & TCP_FLAG_RST) || + !(SEQ_EQ(RcvTCB->tcb_rcvnext, (RcvInfo.tri_seq - 1)))) { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } else + RcvInfo.tri_seq = RcvTCB->tcb_rcvnext; + } + + // He's in sequence, but we have a window of 0. Truncate the + // size, and clear any sequence consuming bits. + if (Size != 0 || + (RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_FIN))) { + RcvInfo.tri_flags &= ~(TCP_FLAG_SYN | TCP_FLAG_FIN); + Size = 0; + if (!(RcvInfo.tri_flags & TCP_FLAG_RST)) + DelayAction(RcvTCB, NEED_ACK); + } + } + + // At this point, the segment is in our window and does not overlap + // on either end. If it's the next sequence number we expect, we can + // handle the data now. Otherwise we'll queue it for later. In either + // case we'll handle RST and ACK information right now. + CTEAssert((*(int *)&Size) >= 0); + + // Now, following 793, we check the RST bit. + if (RcvInfo.tri_flags & TCP_FLAG_RST) { + uchar Reason; + // We can't go back into the LISTEN state from SYN-RCVD here, + // because we may have notified the client via a listen completing + // or a connect indication. So, if came from an active open we'll + // give back a 'connection refused' notice. For all other cases + // we'll just destroy the connection. + + if (RcvTCB->tcb_state == TCB_SYN_RCVD) { + if (RcvTCB->tcb_flags & ACTIVE_OPEN) + Reason = TCB_CLOSE_REFUSED; + else + Reason = TCB_CLOSE_RST; + } else + Reason = TCB_CLOSE_RST; + + TryToCloseTCB(RcvTCB, Reason, TableHandle); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + + if (RcvTCB->tcb_state != TCB_TIME_WAIT) { + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + RemoveTCBFromConn(RcvTCB); + NotifyOfDisc(RcvTCB, OptInfo, TDI_CONNECTION_RESET); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + } + + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } + + // Next check the SYN bit. + if (RcvInfo.tri_flags & TCP_FLAG_SYN) { + // Again, we can't quietly go back into the LISTEN state here, even + // if we came from a passive open. + TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, TableHandle); + SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); + + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + + if (RcvTCB->tcb_state != TCB_TIME_WAIT) { + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + RemoveTCBFromConn(RcvTCB); + NotifyOfDisc(RcvTCB, OptInfo, TDI_CONNECTION_RESET); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + } + + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } + + // Check the ACK field. If it's not on drop the segment. + if (RcvInfo.tri_flags & TCP_FLAG_ACK) { + uint UpdateWindow; + + // If we're in SYN-RCVD, go to ESTABLISHED. + if (RcvTCB->tcb_state == TCB_SYN_RCVD) { + if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + // The ack is valid. + +#ifdef SYN_ATTACK + if (SynAttackProtect) { + CTELockHandle Handle; + + // + // We will be reiniting the tcprexmitcnt to 0. If we are + // configured for syn-attack protection and the rexmit cnt + // is >1, decrement the count of connections that are + // in the half-open-retried state. Check whether we are + // below a low-watermark. If we are, increase the rexmit + // count back to configured values + // + CTEGetLockAtDPC(&SynAttLock, &Handle); + if (RcvTCB->tcb_rexmitcnt >= ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT) { + BOOLEAN Trigger; + Trigger = (TCPHalfOpen < TCPMaxHalfOpen) || + (--TCPHalfOpenRetried <= TCPMaxHalfOpenRetriedLW); + if (Trigger && (MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) + { + MaxConnectResponseRexmitCountTmp = MAX_CONNECT_RESPONSE_REXMIT_CNT; + } + + } + // + // Decrement the # of conn. in half open state + // + TCPHalfOpen--; + CTEFreeLockFromDPC(&SynAttLock, Handle); + } +#endif + RcvTCB->tcb_rexmitcnt = 0; + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + RcvTCB->tcb_senduna++; + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = RcvInfo.tri_window; + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + GoToEstab(RcvTCB); + + // Now complete whatever we can here. + CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS); + } else { + DerefTCB(RcvTCB, TableHandle); + SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); + return IP_SUCCESS; + } + } else { + + // We're not in SYN-RCVD. See if this acknowledges anything. + if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + uint CWin; + + // The ack acknowledes something. Pull the + // appropriate amount off the send q. + ACKData(RcvTCB, RcvInfo.tri_ack); + + // If this acknowledges something we were running a RTT on, + // update that stuff now. + if (RcvTCB->tcb_rtt != 0 && SEQ_GT(RcvInfo.tri_ack, + RcvTCB->tcb_rttseq)) { + short RTT; + + RTT = (short)(TCPTime - RcvTCB->tcb_rtt); + RcvTCB->tcb_rtt = 0; + RTT -= (RcvTCB->tcb_smrtt >> 3); + RcvTCB->tcb_smrtt += RTT; + RTT = (RTT >= 0 ? RTT : -RTT); + RTT -= (RcvTCB->tcb_delta >> 3); + RcvTCB->tcb_delta += RTT; + RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB), + MIN_RETRAN_TICKS), MAX_REXMIT_TO); + } + + // If we're probing for a PMTU black hole we've found one, so turn off + // the detection. The size is already down, so leave it there. + if (RcvTCB->tcb_flags & PMTU_BH_PROBE) { + RcvTCB->tcb_flags &= ~PMTU_BH_PROBE; + RcvTCB->tcb_bhprobecnt = 0; + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + } + + // Update the congestion window now. + CWin = RcvTCB->tcb_cwin; + if (CWin < RcvTCB->tcb_maxwin) { + if (CWin < RcvTCB->tcb_ssthresh) + CWin += RcvTCB->tcb_mss; + else + CWin += (RcvTCB->tcb_mss * RcvTCB->tcb_mss)/CWin; + + RcvTCB->tcb_cwin = MIN(CWin, RcvTCB->tcb_maxwin); + } + + CTEAssert(*(int *)&RcvTCB->tcb_cwin > 0); + + // We've acknowledged something, so reset the rexmit count. + // If there's still stuff outstanding, restart the rexmit + // timer. + RcvTCB->tcb_rexmitcnt = 0; + if (!SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, + RcvTCB->tcb_rexmit); + else + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + + // If we've sent a FIN, and this acknowledges it, we + // need to complete the client's close request and + // possibly transition our state. + + if (RcvTCB->tcb_flags & FIN_SENT) { + // We have sent a FIN. See if it's been acknowledged. + // Once we've sent a FIN, tcb_sendmax + // can't advance, so our FIN must have seq. number + // tcb_sendmax - 1. Thus our FIN is acknowledged + // if the incoming ack is equal to tcb_sendmax. + if (SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + // He's acked our FIN. Turn off the flags, + // and complete the request. We'll leave the + // FIN_OUTSTANDING flag alone, to force early + // outs in the send code. + RcvTCB->tcb_flags &= ~(FIN_NEEDED | FIN_SENT); + + CTEAssert(RcvTCB->tcb_unacked == 0); + CTEAssert(RcvTCB->tcb_sendnext == + RcvTCB->tcb_sendmax); + + // Now figure out what we need to do. In FIN_WAIT1 + // or FIN_WAIT, just complete the disconnect req. + // and continue. Otherwise, it's a bit trickier, + // since we can't complete the connreq until we + // remove the TCB from it's connection. + switch (RcvTCB->tcb_state) { + + case TCB_FIN_WAIT1: + RcvTCB->tcb_state = TCB_FIN_WAIT2; + CompleteConnReq(RcvTCB, OptInfo, + TDI_SUCCESS); + + // Start a timer in case we never get + // out of FIN_WAIT2. Set the retransmit + // count high to force a timeout the + // first time the timer fires. + RcvTCB->tcb_rexmitcnt = MaxDataRexmitCount; + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, + FinWait2TO); + + // Fall through to FIN-WAIT-2 processing. + case TCB_FIN_WAIT2: + break; + case TCB_CLOSING: + GracefulClose(RcvTCB, TRUE, FALSE, + TableHandle); + return IP_SUCCESS; + break; + case TCB_LAST_ACK: + GracefulClose(RcvTCB, FALSE, FALSE, + TableHandle); + return IP_SUCCESS; + break; + default: + DEBUGCHK; + break; + } + } + + } + UpdateWindow = TRUE; + } else { + // It doesn't ack anything. If it's an ack for something + // larger than we've sent then ACKAndDrop it, otherwise + // ignore it. If we're in FIN_WAIT2, we'll restart the timer. + // We don't make this check above because we know no + // data can be acked when we're in FIN_WAIT2. + + if (RcvTCB->tcb_state == TCB_FIN_WAIT2) + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, FinWait2TO); + + if (SEQ_GT(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } else { + // Now update the window if we can. + if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) && + (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) || + (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) && + SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) { + UpdateWindow = TRUE; + } else + UpdateWindow = FALSE; + } + } + + if (UpdateWindow) { + RcvTCB->tcb_sendwin = RcvInfo.tri_window; + RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, + RcvInfo.tri_window); + RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq; + RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack; + if (RcvInfo.tri_window == 0) { + // We've got a zero window. + if (!EMPTYQ(&RcvTCB->tcb_sendq)) { + RcvTCB->tcb_flags &= ~NEED_OUTPUT; + RcvTCB->tcb_rexmitcnt = 0; + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, + RcvTCB->tcb_rexmit); + if (!(RcvTCB->tcb_flags & FLOW_CNTLD)) { + RcvTCB->tcb_flags |= FLOW_CNTLD; + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + } + } else { + if (RcvTCB->tcb_flags & FLOW_CNTLD) { + RcvTCB->tcb_rexmitcnt = 0; + RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB), + MIN_RETRAN_TICKS), MAX_REXMIT_TO); + if (TCB_TIMER_RUNNING(RcvTCB->tcb_rexmittimer)) { + START_TCB_TIMER(RcvTCB->tcb_rexmittimer, + RcvTCB->tcb_rexmit); + } + RcvTCB->tcb_flags &= ~(FLOW_CNTLD | FORCE_OUTPUT); + // Reset send next to the left edge of the window, + // because it might be at senduna+1 if we've been + // probing. + ResetSendNext(RcvTCB, RcvTCB->tcb_senduna); + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + } + + // Since we've updated the window, see if we can send + // some more. + if (RcvTCB->tcb_unacked != 0 || + (RcvTCB->tcb_flags & FIN_NEEDED)) + DelayAction(RcvTCB, NEED_OUTPUT); + + } + } + + } + + // We've handled all the acknowledgment stuff. If the size + // is greater than 0 or important bits are set process it further, + // otherwise it's a pure ack and we're done with it. + if (Size > 0 || (RcvInfo.tri_flags & TCP_FLAG_FIN)) { + + // If we're not in a state where we can process incoming data + // or FINs, there's no point in going further. Just send an + // ack and drop this segment. + if (!DATA_RCV_STATE(RcvTCB->tcb_state) || + (RcvTCB->tcb_flags & GC_PENDING)) { + ACKAndDrop(&RcvInfo, RcvTCB); + return IP_SUCCESS; + } + + // If it's in sequence process it now, otherwise reassemble it. + if (SEQ_EQ(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) { + + // If we're already in the recv. handler, this is a + // duplicate. We'll just toss it. + if (RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV) { + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } + + RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV; + + // Now loop, pulling things from the reassembly queue, until + // the queue is empty, or we can't take all of the data, + // or we hit a FIN. + + do { + + // Handle urgent data, if any. + if (RcvInfo.tri_flags & TCP_FLAG_URG) { + HandleUrgent(RcvTCB, &RcvInfo, RcvBuf, &Size); + + // Since we may have freed the lock, we need to recheck + // and see if we're closing here. + if (CLOSING(RcvTCB)) + break; + + } + + + // OK, the data is in sequence, we've updated the + // reassembly queue and handled any urgent data. If we + // have any data go ahead and process it now. + if (Size > 0) { + +#ifdef VXD + CTEFreeLock(&RcvTCB->tcb_lock, TableHandle); + BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, + RcvInfo.tri_flags, RcvBuf, Size); + CTEGetLock(&RcvTCB->tcb_lock, &TableHandle); +#else + BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, + RcvInfo.tri_flags, RcvBuf, Size); +#endif + RcvTCB->tcb_rcvnext += BytesTaken; + RcvTCB->tcb_rcvwin -= BytesTaken; + + CheckTCBRcv(RcvTCB); + if (RcvTCB->tcb_flags & ACK_DELAYED) + DelayAction(RcvTCB, NEED_ACK); + else { + RcvTCB->tcb_flags |= ACK_DELAYED; + START_TCB_TIMER(RcvTCB->tcb_delacktimer, + DEL_ACK_TICKS); + } + + if (BytesTaken != Size) { + // We didn't take everything we could. No + // use in further processing, just bail + // out. + DelayAction(RcvTCB, NEED_ACK); + break; + } + + // If we're closing now, we're done, so get out. + if (CLOSING(RcvTCB)) + break; + } + + // See if we need to advance over some urgent data. + if (RcvTCB->tcb_flags & URG_VALID) { + uint AdvanceNeeded; + + // We only need to advance if we're not doing + // urgent inline. Urgent inline also has some + // implications for when we can clear the URG_VALID + // flag. If we're not doing urgent inline, we can + // clear it when rcvnext advances beyond urgent end. + // If we are doing inline, we clear it when rcvnext + // advances one receive window beyond urgend. + if (!(RcvTCB->tcb_flags & URG_INLINE)) { + if (RcvTCB->tcb_rcvnext == RcvTCB->tcb_urgstart) + RcvTCB->tcb_rcvnext = RcvTCB->tcb_urgend + + 1; + else + CTEAssert(SEQ_LT(RcvTCB->tcb_rcvnext, + RcvTCB->tcb_urgstart) || + SEQ_GT(RcvTCB->tcb_rcvnext, + RcvTCB->tcb_urgend)); + AdvanceNeeded = 0; + } else + AdvanceNeeded = RcvTCB->tcb_defaultwin; + + // See if we can clear the URG_VALID flag. + if (SEQ_GT(RcvTCB->tcb_rcvnext - AdvanceNeeded, + RcvTCB->tcb_urgend)) { + RcvTCB->tcb_flags &= ~URG_VALID; + if (--(RcvTCB->tcb_slowcount) == 0) { + RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + } + } + + } + + // We've handled the data. If the FIN bit is set, we + // have more processing. + if (RcvInfo.tri_flags & TCP_FLAG_FIN) { + uint Notify = FALSE; + + RcvTCB->tcb_rcvnext++; + DelayAction(RcvTCB, NEED_ACK); + + PushData(RcvTCB); + + switch (RcvTCB->tcb_state) { + + case TCB_SYN_RCVD: + // I don't think we can get here - we + // should have discarded the frame if it + // had no ACK, or gone to established if + // it did. + DEBUGCHK; + case TCB_ESTAB: + RcvTCB->tcb_state = TCB_CLOSE_WAIT; + // We left established, we're off the + // fast path. + RcvTCB->tcb_slowcount++; + RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW; + CheckTCBRcv(RcvTCB); + Notify = TRUE; + break; + case TCB_FIN_WAIT1: + RcvTCB->tcb_state = TCB_CLOSING; + Notify = TRUE; + break; + case TCB_FIN_WAIT2: + // Stop the FIN_WAIT2 timer. + STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer); + RcvTCB->tcb_refcnt++; + GracefulClose(RcvTCB, TRUE, TRUE, + TableHandle); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, + &TableHandle); + break; + default: + DEBUGCHK; + break; + } + + if (Notify) { + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, + TableHandle); + NotifyOfDisc(RcvTCB, OptInfo, TDI_GRACEFUL_DISC); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, + &TableHandle); + } + + break; // Exit out of WHILE loop. + } + + // If the reassembly queue isn't empty, get what we + // can now. + RcvBuf = PullFromRAQ(RcvTCB, &RcvInfo, &Size); + + CheckRBList(RcvBuf, Size); + + } while (RcvBuf != NULL); + + RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV; + if (RcvTCB->tcb_flags & SEND_AFTER_RCV) { + RcvTCB->tcb_flags &= ~SEND_AFTER_RCV; + DelayAction(RcvTCB, NEED_OUTPUT); + } + + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + + } else { + + // It's not in sequence. Since it needs further processing, + // put in on the reassembly queue. + if (DATA_RCV_STATE(RcvTCB->tcb_state) && + !(RcvTCB->tcb_flags & GC_PENDING)) { + PutOnRAQ(RcvTCB, &RcvInfo, RcvBuf, Size); + CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle); + SendACK(RcvTCB); + CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle); + DerefTCB(RcvTCB, TableHandle); + } else + ACKAndDrop(&RcvInfo, RcvTCB); + + return IP_SUCCESS; + } + } + + } else { + // No ACK. Just drop the segment and return. + DerefTCB(RcvTCB, TableHandle); + return IP_SUCCESS; + } + + DerefTCB(RcvTCB, TableHandle); + } else // DataOffset <= Size + TStats.ts_inerrs++; + } else { + // Bump bad xsum counter. + TStats.ts_inerrs++; + + } + + } else // IsBCast + TStats.ts_inerrs++; + + + return IP_SUCCESS; + +} + +#pragma BEGIN_INIT + +//* InitTCPRcv - Initialize TCP receive side. +// +// Called during init time to initialize our TCP receive side. +// +// Input: Nothing. +// +// Returns: TRUE. +// +int +InitTCPRcv(void) +{ +#ifdef NT + ExInitializeSListHead(&TCPRcvReqFree); +#endif + + CTEInitLock(&RequestCompleteLock); + CTEInitLock(&TCBDelayLock); + CTEInitLock(&TCPRcvReqFreeLock); + INITQ(&ConnRequestCompleteQ); + INITQ(&SendCompleteQ); + INITQ(&TCBDelayQ); + RequestCompleteFlags = 0; + TCBDelayRtnCount = 0; + +#ifdef VXD + TCBDelayRtnLimit = 1; +#endif +#ifdef NT + TCBDelayRtnLimit = (uint) (** (PCHAR *) &KeNumberProcessors); + if (TCBDelayRtnLimit > TCB_DELAY_RTN_LIMIT) + TCBDelayRtnLimit = TCB_DELAY_RTN_LIMIT; +#endif + + DummyBuf.ipr_owner = IPR_OWNER_IP; + DummyBuf.ipr_size = 0; + DummyBuf.ipr_next = 0; + DummyBuf.ipr_buffer = NULL; + return TRUE; +} + +//* UnInitTCPRcv - Uninitialize our receive side. +// +// Called if initialization fails to uninitialize our receive side. +// +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +UnInitTCPRcv(void) +{ + +} + + +#pragma END_INIT + + diff --git a/private/ntos/tdi/tcpip/tcp/tcprcv.h b/private/ntos/tdi/tcpip/tcp/tcprcv.h new file mode 100644 index 000000000..629eb5cf8 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcprcv.h @@ -0,0 +1,74 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPRCV.H - TCP receive protocol definitions. +// +// This file contains the definitions for structures used by the receive code. +// + +#define CONN_REQUEST_COMPLETE 0x01 +#define SEND_REQUEST_COMPLETE 0x02 + +#define IN_RCV_COMPLETE 0x10 +#define ANY_REQUEST_COMPLETE (CONN_REQUEST_COMPLETE | SEND_REQUEST_COMPLETE) + +#define trh_signature 0x20485254 // 'TRH ' + +typedef struct TCPRAHdr { +#ifdef DEBUG + ulong trh_sig; // Signature. +#endif + struct TCPRAHdr *trh_next; // Next pointer. + SeqNum trh_start; // First sequence number. + uint trh_size; // Size in bytes of data in this TRH. + uint trh_flags; // Flags for this segment. + uint trh_urg; // Urgent pointer from this seg. + IPRcvBuf *trh_buffer; // Head of buffer list for this TRH. + IPRcvBuf *trh_end; // End of buffer list for this TRH. + +} TCPRAHdr; + +//* Structure of a TCP receive request. + +#define trr_signature 0x20525254 // 'TRR ' + +typedef struct TCPRcvReq { +#ifdef DEBUG + ulong trr_sig; // Signature. +#endif + struct TCPRcvReq *trr_next; // Next in chain. + CTEReqCmpltRtn trr_rtn; // Completion routine. + PVOID trr_context; // User context. + uint trr_amt; // Number of bytes currently in buffer. + uint trr_offset; // Offset into first buffer on chain + // at which to start copying. + uint trr_flags; // Flags for this recv. + ushort *trr_uflags; // Pointer to user specifed flags. + uint trr_size; // Total size of buffer chain. + PNDIS_BUFFER trr_buffer; // Pointer to useable NDIS buffer chain. +} TCPRcvReq; + +#define TRR_PUSHED 0x80000000 // This buffer has been pushed. + + +extern uint RequestCompleteFlags; + +extern Queue SendCompleteQ; +extern Queue TCBDelayQ; + +EXTERNAL_LOCK(RequestCompleteLock) +EXTERNAL_LOCK(TCBDelayLock) + +extern void TCPRcvComplete(void); +extern void FreeRBChain(IPRcvBuf *RBChain); + +extern void DelayAction(struct TCB *DelayTCB, uint Action); +extern void ProcessTCBDelayQ(void); +extern void AdjustRcvWin(struct TCB *WinTCB); + + + + diff --git a/private/ntos/tdi/tcpip/tcp/tcpsend.c b/private/ntos/tdi/tcpip/tcp/tcpsend.c new file mode 100644 index 000000000..6c97e9f8a --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpsend.c @@ -0,0 +1,2666 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPSEND.C - TCP send protocol code. +// +// This file contains the code for sending Data and Control segments. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + +#if FAST_RETRANSMIT +void +TCPFastSend(TCB *SendTCB, + PNDIS_BUFFER in_SendBuf, + uint in_SendOfs, + TCPSendReq *in_SendReq, + uint in_SendSize); +#endif + + + +#ifdef NT +SLIST_HEADER TCPSendFree; +#else +PNDIS_BUFFER TCPSendFree; +#endif + +DEFINE_LOCK_STRUCTURE(TCPSendFreeLock); + +ulong TCPCurrentSendFree; +ulong TCPMaxSendFree = TCP_MAX_HDRS; + +void *TCPProtInfo; // TCP protocol info for IP. + +#ifdef NT +SLIST_HEADER TCPSendReqFree; // Send req. free list. +#else +TCPSendReq *TCPSendReqFree; // Send req. free list. +#endif + +DEFINE_LOCK_STRUCTURE(TCPSendReqFreeLock); +DEFINE_LOCK_STRUCTURE(TCPSendReqCompleteLock); +uint NumTCPSendReq; // Current number of SendReqs in system. +uint MaxSendReq = 0xffffffff; // Maximum allowed number of SendReqs. + + +NDIS_HANDLE TCPSendBufferPool; + +typedef struct TCPHdrBPoolEntry { + struct TCPHdrBPoolEntry *the_next; + NDIS_HANDLE the_handle; + uchar *the_buffer; +} TCPHdrBPoolEntry; + +TCPHdrBPoolEntry *TCPHdrBPoolList; + +extern IPInfo LocalNetInfo; + +#ifdef CHICAGO +extern uchar TransportName[]; +#endif // CHICAGO + +EXTERNAL_LOCK(TCBTableLock) + +// +// All of the init code can be discarded. +// +#ifdef NT + +int InitTCPSend(void); +void UnInitTCPSend(void); + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, InitTCPSend) +#pragma alloc_text(INIT, UnInitTCPSend) + +#endif // ALLOC_PRAGMA +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + +extern void ResetSendNext(TCB *SeqTCB, SeqNum NewSeq); + + +//* GrowTCPHeaderList - Try to grow the tcp header list. +// +// Called when we run out of buffers on the TCP header list, and need +// to grow it. We look to see if we're already at the maximum size, and +// if not we'll allocate the need structures and free them to the list. +// +// Input: Nothing. +// +// Returns: A pointer to a new TCP header buffer if we have one, or NULL. +// +PNDIS_BUFFER +GrowTCPHeaderList(void) +{ + TCPHdrBPoolEntry *NewEntry; + CTELockHandle Handle; + NDIS_STATUS Status; + uint HeaderSize; + uchar *TCPSendHP; + uint i; + PNDIS_BUFFER Buffer; + PNDIS_BUFFER ReturnBuffer; + + CTEGetLock(&TCPSendFreeLock, &Handle); + + if (TCPCurrentSendFree < TCPMaxSendFree) { + + // Still room to grow the list. + NewEntry = CTEAllocMem(sizeof(TCPHdrBPoolEntry)); + + if (NewEntry == NULL) { + // Couldn't get the memory. + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + + NdisAllocateBufferPool(&Status, &NewEntry->the_handle, + NUM_TCP_HEADERS); + + if (Status != NDIS_STATUS_SUCCESS) { + // Couldn't get a new set of buffers. Fail. + CTEFreeMem(NewEntry); + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + HeaderSize = sizeof(TCPHeader) + + MAX(MSS_OPT_SIZE, sizeof(SendCmpltContext)) + + LocalNetInfo.ipi_hsize; + + TCPSendHP = CTEAllocMem(HeaderSize * NUM_TCP_HEADERS); + + if (TCPSendHP == NULL) { + NdisFreeBufferPool(NewEntry->the_handle); + CTEFreeMem(NewEntry); + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + NewEntry->the_buffer = TCPSendHP; + TCPCurrentSendFree += NUM_TCP_HEADERS; + NewEntry->the_next = TCPHdrBPoolList; + TCPHdrBPoolList = NewEntry; + ReturnBuffer = NULL; + CTEFreeLock(&TCPSendFreeLock, Handle); + + for (i = 0; i < NUM_TCP_HEADERS; i++) { + NdisAllocateBuffer(&Status, &Buffer, NewEntry->the_handle, + TCPSendHP + (i * HeaderSize), HeaderSize); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); // This is probably harmless, but check. + break; + } + + NdisBufferLength(Buffer) = sizeof(TCPHeader); + + if (i != 0) + FreeTCPHeader(Buffer); + else + ReturnBuffer = Buffer; + } + + // Update the count with what we didn't allocate, if any. + CTEInterlockedAddUlong(&TCPCurrentSendFree, i - NUM_TCP_HEADERS, + &TCPSendFreeLock); + + return ReturnBuffer; + + } else { + // At the limit already. It's possible someone snuck in and grew + // the list before we got to, so check and see if it's still empty. +#ifdef VXD + if (TCPSendFree != NULL) { + ReturnBuffer = TCPSendFree; + TCPSendFree = NDIS_BUFFER_LINKAGE(ReturnBuffer); + +#else + PSINGLE_LIST_ENTRY BufferLink; + + CTEFreeLock(&TCPSendFreeLock, Handle); + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendFree, + &TCPSendFreeLock + ); + + if (BufferLink != NULL) { + ReturnBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + } else + ReturnBuffer = NULL; + + return ReturnBuffer; +#endif +#ifdef VXD + } else + ReturnBuffer = NULL; +#endif + + } + + CTEFreeLock(&TCPSendFreeLock, Handle); + return ReturnBuffer; + + +} + +//* GetTCPHeader - Get a TCP header buffer. +// +// Called when we need to get a TCP header buffer. This routine is +// specific to the particular environment (VxD or NT). All we +// need to do is pop the buffer from the free list. +// +// Input: Nothing. +// +// Returns: Pointer to an NDIS buffer, or NULL is none. +// +PNDIS_BUFFER +GetTCPHeader(void) +{ + PNDIS_BUFFER NewBuffer; + +#ifdef VXD + NewBuffer = TCPSendFree; + if (NewBuffer != NULL) { + TCPSendFree = NDIS_BUFFER_LINKAGE(NewBuffer); + return NewBuffer; + } + +#else + + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendFree, + &TCPSendFreeLock + ); + if (BufferLink != NULL) { + NewBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + return NewBuffer; + } +#endif + else + return GrowTCPHeaderList(); +} + +//* FreeTCPHeader - Free a TCP header buffer. +// +// Called to free a TCP header buffer. +// +// Input: Buffer to be freed. +// +// Returns: Nothing. +// +void +FreeTCPHeader(PNDIS_BUFFER FreedBuffer) +{ + + CTEAssert(FreedBuffer != NULL); + + NdisBufferLength(FreedBuffer) = sizeof(TCPHeader); + +#ifdef VXD + + NDIS_BUFFER_LINKAGE(FreedBuffer) = TCPSendFree; + TCPSendFree = FreedBuffer; + +#else + + ExInterlockedPushEntrySList( + &TCPSendFree, + STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedBuffer->Next), Next), + &TCPSendFreeLock + ); + +#endif +} + +//* FreeSendReq - Free a send request structure. +// +// Called to free a send request structure. +// +// Input: FreedReq - Connection request structure to be freed. +// +// Returns: Nothing. +// +void +FreeSendReq(TCPSendReq *FreedReq) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(FreedReq, tsr); + + BufferLink = STRUCT_OF( + SINGLE_LIST_ENTRY, + &(FreedReq->tsr_req.tr_q.q_next), + Next + ); + + ExInterlockedPushEntrySList( + &TCPSendReqFree, + BufferLink, + &TCPSendReqFreeLock + ); + +#else // NT + + TCPSendReq **Temp; + + CTEStructAssert(FreedReq, tsr); + + Temp = (TCPSendReq **)&FreedReq->tsr_req.tr_q.q_next; + *Temp = TCPSendReqFree; + TCPSendReqFree = FreedReq; + +#endif // NT +} + +//* GetSendReq - Get a send request structure. +// +// Called to get a send request structure. +// +// Input: Nothing. +// +// Returns: Pointer to SendReq structure, or NULL if none. +// +TCPSendReq * +GetSendReq(void) +{ + TCPSendReq *Temp; + +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + Queue *QueuePtr; + TCPReq *ReqPtr; + + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendReqFree, + &TCPSendReqFreeLock + ); + + if (BufferLink != NULL) { + QueuePtr = STRUCT_OF(Queue, BufferLink, q_next); + ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q); + Temp = STRUCT_OF(TCPSendReq, ReqPtr, tsr_req); + CTEStructAssert(Temp, tsr); + } + else { + if (NumTCPSendReq < MaxSendReq) + Temp = CTEAllocMem(sizeof(TCPSendReq)); + else + Temp = NULL; + + if (Temp != NULL) { + ExInterlockedAddUlong(&NumTCPSendReq, 1, &TCPSendReqFreeLock); +#ifdef DEBUG + Temp->tsr_req.tr_sig = tr_signature; + Temp->tsr_sig = tsr_signature; +#endif + } + } + +#else // NT + + Temp = TCPSendReqFree; + if (Temp != NULL) + TCPSendReqFree = (TCPSendReq *)Temp->tsr_req.tr_q.q_next; + else { + if (NumTCPSendReq < MaxSendReq) + Temp = CTEAllocMem(sizeof(TCPSendReq)); + else + Temp = NULL; + + if (Temp != NULL) { + NumTCPSendReq++; +#ifdef DEBUG + Temp->tsr_req.tr_sig = tr_signature; + Temp->tsr_sig = tsr_signature; +#endif + } + } + +#endif // NT + + return Temp; +} + + + +//* TCPSendComplete - Complete a TCP send. +// +// Called by IP when a send we've made is complete. We free the buffer, +// and possibly complete some sends. Each send queued on a TCB has a ref. +// count with it, which is the number of times a pointer to a buffer +// associated with the send has been passed to the underlying IP layer. We +// can't complete a send until that count it 0. If this send was actually +// from a send of data, we'll go down the chain of send and decrement the +// refcount on each one. If we have one going to 0 and the send has already +// been acked we'll complete the send. If it hasn't been acked we'll leave +// it until the ack comes in. +// +// NOTE: We aren't protecting any of this with locks. When we port this to +// NT we'll need to fix this, probably with a global lock. See the comments +// in ACKSend() in TCPRCV.C for more details. +// +// Input: Context - Context we gave to IP. +// BufferChain - BufferChain for send. +// +// Returns: Nothing. +// +void +TCPSendComplete(void *Context, PNDIS_BUFFER BufferChain) +{ + CTELockHandle SendHandle; + PNDIS_BUFFER CurrentBuffer; + + + if (Context != NULL) { + SendCmpltContext *SCContext = (SendCmpltContext *)Context; + TCPSendReq *CurrentSend; + uint i; + + CTEStructAssert(SCContext, scc); + + // First, loop through and free any NDIS buffers here that need to be. + // freed. We'll skip any 'user' buffers, and then free our buffers. We + // need to do this before decrementing the reference count to avoid + // destroying the buffer chain if we have to zap tsr_lastbuf->Next to + // NULL. + + CurrentBuffer = NDIS_BUFFER_LINKAGE(BufferChain); + for (i = 0; i < (uint)SCContext->scc_ubufcount; i++) { + CTEAssert(CurrentBuffer != NULL); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } + + for (i = 0; i < (uint)SCContext->scc_tbufcount; i++) { + PNDIS_BUFFER TempBuffer; + + CTEAssert(CurrentBuffer != NULL); + + TempBuffer = CurrentBuffer; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + NdisFreeBuffer(TempBuffer); + } + + CurrentSend = SCContext->scc_firstsend; + + i = 0; + while (i < SCContext->scc_count) { + Queue *TempQ; + long Result; + + + TempQ = QNEXT(&CurrentSend->tsr_req.tr_q); + + CTEStructAssert(CurrentSend, tsr); + + Result = CTEInterlockedDecrementLong( + &(CurrentSend->tsr_refcnt) + ); + + CTEAssert(Result >= 0); + + if (Result <= 0) { + + // Reference count has gone to 0 which means the send has + // been ACK'd or cancelled. Complete it now. + + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (CurrentSend->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(CurrentSend->tsr_lastbuf) = NULL; + CurrentSend->tsr_lastbuf = NULL; + } + + CTEGetLock(&RequestCompleteLock, &SendHandle); + ENQUEUE(&SendCompleteQ, &CurrentSend->tsr_req.tr_q); + RequestCompleteFlags |= SEND_REQUEST_COMPLETE; + CTEFreeLock(&RequestCompleteLock, SendHandle); + } + + CurrentSend = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q), + tsr_req); + + i++; + } + + } + + FreeTCPHeader(BufferChain); + + if (RequestCompleteFlags & SEND_REQUEST_COMPLETE) + TCPRcvComplete(); + +} + +//* RcvWin - Figure out the receive window to offer in an ack. +// +// A routine to figure out what window to offer on a connection. We +// take into account SWS avoidance, what the default connection window is, +// and what the last window we offered is. +// +// Input: WinTCB - TCB on which to perform calculations. +// +// Returns: Window to be offered. +// +uint +RcvWin(TCB *WinTCB) +{ + int CouldOffer; // The window size we could offer. + + CTEStructAssert(WinTCB, tcb); + + CheckRBList(WinTCB->tcb_pendhead, WinTCB->tcb_pendingcnt); + + CTEAssert(WinTCB->tcb_rcvwin >= 0); + + CouldOffer = WinTCB->tcb_defaultwin - WinTCB->tcb_pendingcnt; + + CTEAssert(CouldOffer >= 0); + CTEAssert(CouldOffer >= WinTCB->tcb_rcvwin); + + if ((CouldOffer - WinTCB->tcb_rcvwin) >= (int) MIN(WinTCB->tcb_defaultwin/2, + WinTCB->tcb_mss)) + WinTCB->tcb_rcvwin = CouldOffer; + + return WinTCB->tcb_rcvwin; +} + +//* SendSYN - Send a SYN segment. +// +// This is called during connection establishment time to send a SYN +// segment to the peer. We get a buffer if we can, and then fill +// it in. There's a tricky part here where we have to build the MSS +// option in the header - we find the MSS by finding the MSS offered +// by the net for the local address. After that, we send it. +// +// Input: SYNTcb - TCB from which SYN is to be sent. +// TCBHandle - Handle for lock on TCB. +// +// Returns: Nothing. +// +void +SendSYN(TCB *SYNTcb, CTELockHandle TCBHandle) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *SYNHeader; + uchar *OptPtr; + IP_STATUS SendStatus; + + CTEStructAssert(SYNTcb, tcb); + + HeaderBuffer = GetTCPHeader(); + + // Go ahead and set the retransmission timer now, in case we didn't get a + // buffer. In the future we might want to queue the connection for + // when we free a buffer. + START_TCB_TIMER(SYNTcb->tcb_rexmittimer, SYNTcb->tcb_rexmit); + if (HeaderBuffer != NULL) { + ushort TempWin; + ushort MSS; + uchar FoundMSS; + + SYNHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + MSS_OPT_SIZE; + SYNHeader->tcp_src = SYNTcb->tcb_sport; + SYNHeader->tcp_dest = SYNTcb->tcb_dport; + SYNHeader->tcp_seq = net_long(SYNTcb->tcb_sendnext); + SYNTcb->tcb_sendnext++; + + if (SEQ_GT(SYNTcb->tcb_sendnext, SYNTcb->tcb_sendmax)) { + TStats.ts_outsegs++; + SYNTcb->tcb_sendmax = SYNTcb->tcb_sendnext; + } else + TStats.ts_retranssegs++; + + SYNHeader->tcp_ack = net_long(SYNTcb->tcb_rcvnext); + if (SYNTcb->tcb_state == TCB_SYN_RCVD) { + SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN | TCP_FLAG_ACK); +#ifdef SYN_ATTACK + // + // if this is the second time we are trying to send the SYN-ACK, + // increment the count of retried half-connections + // + if (SynAttackProtect && (SYNTcb->tcb_rexmitcnt == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + CTEInterlockedAddUlong(&TCPHalfOpenRetried, 1, &SynAttLock); + } +#endif + } else { + SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN); + } + + TempWin = (ushort)SYNTcb->tcb_rcvwin; + SYNHeader->tcp_window = net_short(TempWin); + SYNHeader->tcp_xsum = 0; + OptPtr = (uchar *)(SYNHeader + 1); + FoundMSS = (*LocalNetInfo.ipi_getlocalmtu)(SYNTcb->tcb_saddr, &MSS); + + if (!FoundMSS) { + DEBUGCHK; + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + FreeTCPHeader(HeaderBuffer); + return; + } + + MSS -= sizeof(TCPHeader); + + *OptPtr++ = TCP_OPT_MSS; + *OptPtr++ = MSS_OPT_SIZE; + **(ushort **)&OptPtr = net_short(MSS); + + SYNTcb->tcb_refcnt++; + SYNHeader->tcp_xsum = ~XsumSendChain(SYNTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader) + MSS_OPT_SIZE), HeaderBuffer); + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader) + MSS_OPT_SIZE, SYNTcb->tcb_daddr, + SYNTcb->tcb_saddr, &SYNTcb->tcb_opt, SYNTcb->tcb_rce, + PROTOCOL_TCP); + + SYNTcb->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) { + FreeTCPHeader(HeaderBuffer); + } + + CTEGetLock(&SYNTcb->tcb_lock, &TCBHandle); + DerefTCB(SYNTcb, TCBHandle); + + } else { + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + return; + } + +} + +//* SendKA - Send a keep alive segment. +// +// This is called when we want to send a keep alive. +// +// Input: KATcb - TCB from which keep alive is to be sent. +// Handle - Handle for lock on TCB. +// +// Returns: Nothing. +// +void +SendKA(TCB *KATcb, CTELockHandle Handle) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *Header; + IP_STATUS SendStatus; + + CTEStructAssert(KATcb, tcb); + + HeaderBuffer = GetTCPHeader(); + + if (HeaderBuffer != NULL) { + ushort TempWin; + SeqNum TempSeq; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + 1; + Header->tcp_src = KATcb->tcb_sport; + Header->tcp_dest = KATcb->tcb_dport; + TempSeq = KATcb->tcb_senduna - 1; + Header->tcp_seq = net_long(TempSeq); + + TStats.ts_retranssegs++; + + Header->tcp_ack = net_long(KATcb->tcb_rcvnext); + Header->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK); + + TempWin = (ushort)RcvWin(KATcb); + Header->tcp_window = net_short(TempWin); + Header->tcp_xsum = 0; + + Header->tcp_xsum = ~XsumSendChain(KATcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader) + 1), HeaderBuffer); + + KATcb->tcb_kacount++; + CTEFreeLock(&KATcb->tcb_lock, Handle); + + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader) + 1, KATcb->tcb_daddr, + KATcb->tcb_saddr, &KATcb->tcb_opt, KATcb->tcb_rce, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) { + FreeTCPHeader(HeaderBuffer); + } + + + } else { + CTEFreeLock(&KATcb->tcb_lock, Handle); + } + +} + +//* SendACK - Send an ACK segment. +// +// This is called whenever we need to send an ACK for some reason. Nothing +// fancy, we just do it. +// +// Input: ACKTcb - TCB from which ACK is to be sent. +// +// Returns: Nothing. +// +void +SendACK(TCB *ACKTcb) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *ACKHeader; + IP_STATUS SendStatus; + CTELockHandle TCBHandle; + SeqNum SendNext; + + CTEStructAssert(ACKTcb, tcb); + + if ((ACKTcb->tcb_state == TCB_TIME_WAIT) && + (ACKTcb->tcb_flags & TW_PENDING)) { + CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle); + STOP_TCB_TIMER(ACKTcb->tcb_delacktimer); + ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED); + ACKTcb->tcb_error = IP_SUCCESS; + CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle); + return; + } + + HeaderBuffer = GetTCPHeader(); + + + if (HeaderBuffer != NULL) { + ushort TempWin; + + CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle); + + ACKHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + + ACKHeader->tcp_src = ACKTcb->tcb_sport; + ACKHeader->tcp_dest = ACKTcb->tcb_dport; + ACKHeader->tcp_ack = net_long(ACKTcb->tcb_rcvnext); + + // If the remote peer is advertising a window of zero, we need to + // send this ack with a seq. number of his rcv_next (which in that case + // should be our senduna). We have code here ifdef'd out that makes + // sure that we don't send outside the RWE, but this doesn't work. We + // need to be able to send a pure ACK exactly at the RWE. + + if (ACKTcb->tcb_sendwin != 0) { + SeqNum MaxValidSeq; + + SendNext = ACKTcb->tcb_sendnext; +#if 0 + MaxValidSeq = ACKTcb->tcb_senduna + ACKTcb->tcb_sendwin - 1; + + SendNext = (SEQ_LT(SendNext, MaxValidSeq) ? SendNext : MaxValidSeq); +#endif + + } else + SendNext = ACKTcb->tcb_senduna; + + if ((ACKTcb->tcb_flags & FIN_SENT) && + SEQ_EQ(SendNext, ACKTcb->tcb_sendmax - 1)) { + ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5, + TCP_FLAG_FIN | TCP_FLAG_ACK); + } else + ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK); + + ACKHeader->tcp_seq = net_long(SendNext); + + TempWin = (ushort)RcvWin(ACKTcb); + ACKHeader->tcp_window = net_short(TempWin); + ACKHeader->tcp_xsum = 0; + + ACKHeader->tcp_xsum = ~XsumSendChain(ACKTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader)), HeaderBuffer); + + STOP_TCB_TIMER(ACKTcb->tcb_delacktimer); + ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED); + + if (ACKTcb->tcb_flags & TW_PENDING) { + ACKTcb->tcb_state = TCB_TIME_WAIT; + } + + CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle); + + TStats.ts_outsegs++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader), ACKTcb->tcb_daddr, ACKTcb->tcb_saddr, + &ACKTcb->tcb_opt, ACKTcb->tcb_rce, PROTOCOL_TCP); + + ACKTcb->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) + FreeTCPHeader(HeaderBuffer); + } + + return; + + +} + +//* SendRSTFromTCB - Send a RST from a TCB. +// +// This is called during close when we need to send a RST. +// +// Input: RSTTcb - TCB from which RST is to be sent. +// +// Returns: Nothing. +// +void +SendRSTFromTCB(TCB *RSTTcb) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *RSTHeader; + IP_STATUS SendStatus; + + CTEStructAssert(RSTTcb, tcb); + + CTEAssert(RSTTcb->tcb_state == TCB_CLOSED); + + HeaderBuffer = GetTCPHeader(); + + + if (HeaderBuffer != NULL) { + SeqNum RSTSeq; + + RSTHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + + RSTHeader->tcp_src = RSTTcb->tcb_sport; + RSTHeader->tcp_dest = RSTTcb->tcb_dport; + + // If the remote peer has a window of 0, send with a seq. # equal + // to senduna so he'll accept it. Otherwise send with send max. + if (RSTTcb->tcb_sendwin != 0) + RSTSeq = RSTTcb->tcb_sendmax; + else + RSTSeq = RSTTcb->tcb_senduna; + + RSTHeader->tcp_seq = net_long(RSTSeq); + RSTHeader->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST); + + RSTHeader->tcp_window = 0; + RSTHeader->tcp_xsum = 0; + + RSTHeader->tcp_xsum = ~XsumSendChain(RSTTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader)), HeaderBuffer); + + TStats.ts_outsegs++; + TStats.ts_outrsts++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader), RSTTcb->tcb_daddr, RSTTcb->tcb_saddr, + &RSTTcb->tcb_opt, RSTTcb->tcb_rce, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) + FreeTCPHeader(HeaderBuffer); + } + + return; + + +} +//* SendRSTFromHeader - Send a RST back, based on a header. +// +// Called when we need to send a RST, but don't necessarily have a TCB. +// +// Input: TCPH - TCP header to be RST. +// Length - Length of the incoming segment. +// Dest - Destination IP address for RST. +// Src - Source IP address for RST. +// OptInfo - IP Options to use on RST. +// +// Returns: Nothing. +// +void +SendRSTFromHeader(TCPHeader UNALIGNED *TCPH, uint Length, IPAddr Dest, + IPAddr Src, IPOptInfo *OptInfo) +{ + PNDIS_BUFFER Buffer; + TCPHeader *RSTHdr; + IPOptInfo NewInfo; + IP_STATUS SendStatus; + + if (TCPH->tcp_flags & TCP_FLAG_RST) + return; + + Buffer = GetTCPHeader(); + + if (Buffer != NULL) { + // Got a buffer. Fill in the header so as to make it believable to + // the remote guy, and send it. + + RSTHdr = (TCPHeader *)((uchar *)NdisBufferVirtualAddress(Buffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + + if (TCPH->tcp_flags & TCP_FLAG_SYN) + Length++; + + if (TCPH->tcp_flags & TCP_FLAG_FIN) + Length++; + + if (TCPH->tcp_flags & TCP_FLAG_ACK) { + RSTHdr->tcp_seq = TCPH->tcp_ack; + RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST); + } else { + SeqNum TempSeq; + + RSTHdr->tcp_seq = 0; + TempSeq = net_long(TCPH->tcp_seq); + TempSeq += Length; + RSTHdr->tcp_ack = net_long(TempSeq); + RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST | TCP_FLAG_ACK); + } + + RSTHdr->tcp_window = 0; + RSTHdr->tcp_dest = TCPH->tcp_src; + RSTHdr->tcp_src = TCPH->tcp_dest; + RSTHdr->tcp_xsum = 0; + + RSTHdr->tcp_xsum = ~XsumSendChain(PHXSUM(Src, Dest, PROTOCOL_TCP, + sizeof(TCPHeader)), Buffer); + + (*LocalNetInfo.ipi_initopts)(&NewInfo); + + if (OptInfo->ioi_options != NULL) + (*LocalNetInfo.ipi_updateopts)(OptInfo, &NewInfo, Dest, NULL_IP_ADDR); + + TStats.ts_outsegs++; + TStats.ts_outrsts++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, Buffer, + sizeof(TCPHeader), Dest, Src, &NewInfo, NULL, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) + FreeTCPHeader(Buffer); + + (*LocalNetInfo.ipi_freeopts)(&NewInfo); + } + +} +//* GoToEstab - Transition to the established state. +// +// Called when we are going to the established state and need to finish up +// initializing things that couldn't be done until now. We assume the TCB +// lock is held by the caller on the TCB we're called with. +// +// Input: EstabTCB - TCB to transition. +// +// Returns: Nothing. +// +void +GoToEstab(TCB *EstabTCB) +{ + + // Initialize our slow start and congestion control variables. + EstabTCB->tcb_cwin = 2 * EstabTCB->tcb_mss; + EstabTCB->tcb_ssthresh = 0xffffffff; + + EstabTCB->tcb_state = TCB_ESTAB; + + // We're in established. We'll subtract one from slow count for this fact, + // and if the slowcount goes to 0 we'll move onto the fast path. + + if (--(EstabTCB->tcb_slowcount) == 0) + EstabTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + + TStats.ts_currestab++; + + EstabTCB->tcb_flags &= ~ACTIVE_OPEN; // Turn off the active opening flag. + +} + +//* InitSendState - Initialize the send state of a connection. +// +// Called during connection establishment to initialize our send state. +// (In this case, this refers to all information we'll put on the wire as +// well as pure send state). We pick an ISS, set up a rexmit timer value, +// etc. We assume the tcb_lock is held on the TCB when we are called. +// +// Input: NewTCB - TCB to be set up. +// +// Returns: Nothing. +void +InitSendState(TCB *NewTCB) +{ + CTEStructAssert(NewTCB, tcb); + + NewTCB->tcb_sendnext = CTESystemUpTime(); + NewTCB->tcb_senduna = NewTCB->tcb_sendnext; + NewTCB->tcb_sendmax = NewTCB->tcb_sendnext; + NewTCB->tcb_error = IP_SUCCESS; + + // Initialize pseudo-header xsum. + NewTCB->tcb_phxsum = PHXSUM(NewTCB->tcb_saddr, NewTCB->tcb_daddr, + PROTOCOL_TCP, 0); + + // Initialize retransmit and delayed ack stuff. + NewTCB->tcb_rexmitcnt = 0; + NewTCB->tcb_rtt = 0; + NewTCB->tcb_smrtt = 0; + NewTCB->tcb_delta = MS_TO_TICKS(6000); + NewTCB->tcb_rexmit = MS_TO_TICKS(3000); + STOP_TCB_TIMER(NewTCB->tcb_rexmittimer); + STOP_TCB_TIMER(NewTCB->tcb_delacktimer); + +} + +//* TCPStatus - Handle a status indication. +// +// This is the TCP status handler, called by IP when a status event +// occurs. For most of these we do nothing. For certain severe status +// events we will mark the local address as invalid. +// +// Entry: StatusType - Type of status (NET or HW). NET status +// is usually caused by a received ICMP +// message. HW status indicate a HW +// problem. +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is NET status, the original dest. of +// DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local +// or remote). +// Param - Additional information for status - i.e. the +// param field of an ICMP message. +// Data - Data pertaining to status - for NET status, this +// is the first 8 bytes of the original DG. +// +// Returns: Nothing +// +void +TCPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) +{ + CTELockHandle TableHandle, TCBHandle; + TCB *StatusTCB; + TCPHeader UNALIGNED *Header = (TCPHeader UNALIGNED *)Data; + SeqNum DropSeq; + + // Handle NET status codes differently from HW status codes. + if (StatusType == IP_NET_STATUS) { + // It's a NET code. Find a matching TCB. + CTEGetLock(&TCBTableLock, &TableHandle); + StatusTCB = FindTCB(OrigSrc, OrigDest, Header->tcp_dest, + Header->tcp_src); + if (StatusTCB != NULL) { + // Found one. Get the lock on it, and continue. + CTEStructAssert(StatusTCB, tcb); + + CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&TCBTableLock, TCBHandle); + + + // Make sure the TCB is in a state that is interesting. + if (StatusTCB->tcb_state == TCB_CLOSED || + StatusTCB->tcb_state == TCB_TIME_WAIT || + CLOSING(StatusTCB)) { + CTEFreeLock(&StatusTCB->tcb_lock, TableHandle); + return; + } + + switch (StatusCode) { + // Hard errors - Destination protocol unreachable. We treat + // these as fatal errors. Close the connection now. + case IP_DEST_PROT_UNREACHABLE: + StatusTCB->tcb_error = StatusCode; + StatusTCB->tcb_refcnt++; + TryToCloseTCB(StatusTCB, TCB_CLOSE_UNREACH, TableHandle); + + RemoveTCBFromConn(StatusTCB); + NotifyOfDisc(StatusTCB, NULL, + MapIPError(StatusCode, TDI_DEST_UNREACHABLE)); + CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle); + DerefTCB(StatusTCB, TCBHandle); + return; + break; + + // Soft errors. Save the error in case it time out. + case IP_DEST_NET_UNREACHABLE: + case IP_DEST_HOST_UNREACHABLE: + case IP_DEST_PORT_UNREACHABLE: + case IP_PACKET_TOO_BIG: + case IP_BAD_ROUTE: + case IP_TTL_EXPIRED_TRANSIT: + case IP_TTL_EXPIRED_REASSEM: + case IP_PARAM_PROBLEM: + StatusTCB->tcb_error = StatusCode; + break; + + case IP_SPEC_MTU_CHANGE: + // A TCP datagram has triggered an MTU change. Figure out + // which connection it is, and update him to retransmit the + // segment. The Param value is the new MTU. We'll need to + // retransmit if the new MTU is less than our existing MTU + // and the sequence of the dropped packet is less than our + // current send next. + + Param -= sizeof(TCPHeader) - + StatusTCB->tcb_opt.ioi_optlength; + DropSeq = net_long(Header->tcp_seq); + + if (*(ushort *)&Param <= StatusTCB->tcb_mss && + (SEQ_GTE(DropSeq, StatusTCB->tcb_senduna) && + SEQ_LT(DropSeq, StatusTCB->tcb_sendnext))) { + + // Need to initiate a retranmsit. + ResetSendNext(StatusTCB, DropSeq); + // Set the congestion window to allow only one packet. + // This may prevent us from sending anything if we + // didn't just set sendnext to senduna. This is OK, + // we'll retransmit later, or send when we get an ack. + StatusTCB->tcb_cwin = Param; + DelayAction(StatusTCB, NEED_OUTPUT); + } + + StatusTCB->tcb_mss = (ushort)MIN(Param, + (ulong)StatusTCB->tcb_remmss); + + CTEAssert(StatusTCB->tcb_mss > 0); + + + // + // Reset the Congestion Window if necessary + // + if (StatusTCB->tcb_cwin < StatusTCB->tcb_mss) { + StatusTCB->tcb_cwin = StatusTCB->tcb_mss; + + // + // Make sure the slow start threshold is at least + // 2 segments + // + if ( StatusTCB->tcb_ssthresh < + ((uint) StatusTCB->tcb_mss*2) + ) { + StatusTCB->tcb_ssthresh = StatusTCB->tcb_mss * 2; + } + } + + break; + + // Source quench. This will cause us to reinitiate our + // slow start by resetting our congestion window and + // adjusting our slow start threshold. + case IP_SOURCE_QUENCH: + StatusTCB->tcb_ssthresh = + MAX( + MIN( + StatusTCB->tcb_cwin, + StatusTCB->tcb_sendwin + ) / 2, + (uint) StatusTCB->tcb_mss * 2 + ); + StatusTCB->tcb_cwin = StatusTCB->tcb_mss; + break; + + default: + DEBUGCHK; + break; + } + + CTEFreeLock(&StatusTCB->tcb_lock, TableHandle); + } else { + // Couldn't find a matching TCB. Just free the lock and return. + CTEFreeLock(&TCBTableLock, TableHandle); + } + } else { + uint NewMTU; + + // 'Hardware' or 'global' status. Figure out what to do. + switch (StatusCode) { + case IP_ADDR_DELETED: + // Local address has gone away. OrigDest is the IPAddr which is + // gone. + +#ifndef _PNP_POWER + // + // Delete all TCBs with that as a source address. + // This is done via TDI notifications in the PNP world. + // + TCBWalk(DeleteTCBWithSrc, &OrigDest, NULL, NULL); + +#endif // _PNP_POWER + +#ifdef SECFLTR + // + // Delete any security filters associated with this address + // + DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_TCP); + +#endif // SECFLTR + + break; + + case IP_ADDR_ADDED: + +#ifdef SECFLTR + // + // An address has materialized. OrigDest identifies the address. + // Data is a handle to the IP configuration information for the + // interface on which the address is instantiated. + // + AddProtocolSecurityFilter(OrigDest, PROTOCOL_TCP, + (NDIS_HANDLE) Data); +#endif // SECFLTR + + break; + + case IP_MTU_CHANGE: + NewMTU = Param - sizeof(TCPHeader); + TCBWalk(SetTCBMTU, &OrigDest, &OrigSrc, &NewMTU); + break; +#ifdef CHICAGO + case IP_UNLOAD: + // IP is telling us we're being unloaded. First, deregister + // with VTDI, and then call CTEUnload(). + (void)TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL); + TLRegisterDispatch(TransportName, NULL); + (void)RegisterAddrChangeHndlr(AddrChange, FALSE); + CTEUnload(TransportName); + break; +#endif // CHICAGO + default: + DEBUGCHK; + break; + } + } +} + +//* FillTCPHeader - Fill the TCP header in. +// +// A utility routine to fill in the TCP header. +// +// Input: SendTCB - TCB to fill from. +// Header - Header to fill into. +// +// Returns: Nothing. +// +void +FillTCPHeader(TCB *SendTCB, TCPHeader *Header) +{ +#ifdef VXD + +// STUPID FUCKING COMPILER generates incorrect code for this. Put it back on +// the blessed day we get a real compiler. + +#if 0 + _asm { + mov edx, dword ptr SendTCB + mov ecx, dword ptr Header + mov ax, word ptr [edx].tcb_sport + xchg al, ah + mov word ptr [ecx].tcp_src, ax + mov ax, [edx].tcb_dport + xchg ah, al + mov [ecx].tcp_dest, ax + + mov eax, [edx].tcb_sendnext + xchg ah, al + ror eax, 16 + xchg ah, al + mov [ecx].tcp_seq, eax + + mov eax, [edx].tcb_rcvnext + xchg ah, al + ror eax, 16 + xchg ah, al + mov [ecx].tcp_ack, eax + + mov [ecx].tcp_flags, 1050H + mov dword ptr [ecx].tcp_xsum, 0 + + push edx + call near ptr RcvWin + add esp, 4 + + mov ecx, Header + xchg ah, al + mov [ecx].tcp_window, ax + + } +#else + ushort S; + ulong L; + + + Header->tcp_src = SendTCB->tcb_sport; + Header->tcp_dest = SendTCB->tcb_dport; + L = SendTCB->tcb_sendnext; + Header->tcp_seq = net_long(L); + L = SendTCB->tcb_rcvnext; + Header->tcp_ack = net_long(L); + Header->tcp_flags = 0x1050; + *(ulong *)&Header->tcp_xsum = 0; + S = RcvWin(SendTCB); + Header->tcp_window = net_short(S); +#endif +#else + + // + // BUGBUG: Is this worth coding in assembly? + // + ushort S; + ulong L; + + + Header->tcp_src = SendTCB->tcb_sport; + Header->tcp_dest = SendTCB->tcb_dport; + L = SendTCB->tcb_sendnext; + Header->tcp_seq = net_long(L); + L = SendTCB->tcb_rcvnext; + Header->tcp_ack = net_long(L); + Header->tcp_flags = 0x1050; + *(ulong *)&Header->tcp_xsum = 0; + S = RcvWin(SendTCB); + Header->tcp_window = net_short(S); +#endif + + +} + +//* TCPSend - Send data from a TCP connection. +// +// This is the main 'send data' routine. We go into a loop, trying +// to send data until we can't for some reason. First we compute +// the useable window, use it to figure the amount we could send. If +// the amount we could send meets certain criteria we'll build a frame +// and send it, after setting any appropriate control bits. We assume +// the caller has put a reference on the TCB. +// +// Input: SendTCB - TCB to be sent from. +// TCBHandle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +#ifdef VXD +TCPSend(TCB *SendTCB) +#else +TCPSend(TCB *SendTCB, CTELockHandle TCBHandle) +#endif +{ + int SendWin; // Useable send window. + uint AmountToSend; // Amount to send this time. + uint AmountLeft; + TCPHeader *Header; // TCP header for a send. + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + TCPSendReq *CurSend; + SendCmpltContext *SCC; + SeqNum OldSeq; + IP_STATUS SendStatus; + uint AmtOutstanding, AmtUnsent; + int ForceWin; // Window we're force to use. +#ifdef VXD + CTELockHandle TCBHandle; + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); +#endif + + + CTEStructAssert(SendTCB, tcb); + CTEAssert(SendTCB->tcb_refcnt != 0); + + CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0); + CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss); + + CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) || + (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax)); + + if (!(SendTCB->tcb_flags & IN_TCP_SEND) && + !(SendTCB->tcb_fastchk & TCP_FLAG_IN_RCV)) { + SendTCB->tcb_flags |= IN_TCP_SEND; + + // We'll continue this loop until we send a FIN, or we break out + // internally for some other reason. + while (!(SendTCB->tcb_flags & FIN_OUTSTANDING)) { + + CheckTCBSends(SendTCB); + + AmtOutstanding = (uint)(SendTCB->tcb_sendnext - + SendTCB->tcb_senduna); + AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding; + + CTEAssert(*(int *)&AmtUnsent >= 0); + + SendWin = (int)(MIN(SendTCB->tcb_sendwin, SendTCB->tcb_cwin) - + AmtOutstanding); + +#if FAST_RETRANSMIT + // if this send is after the fast recovery + // and sendwin is zero because of amt outstanding + // then, at least force 1 segment to prevent delayed + // ack timeouts from the remote + + if (SendTCB->tcb_force) { + SendTCB->tcb_force=0; + if (SendWin < SendTCB->tcb_mss ){ + + SendWin = SendTCB->tcb_mss; + } + } + +#endif + + + + // Since the window could have shrank, need to get it to zero at + // least. + ForceWin = (int)((SendTCB->tcb_flags & FORCE_OUTPUT) >> + FORCE_OUT_SHIFT); + SendWin = MAX(SendWin, ForceWin); + + AmountToSend = MIN(MIN((uint)SendWin, AmtUnsent), SendTCB->tcb_mss); + + CTEAssert(SendTCB->tcb_mss > 0); + + // See if we have enough to send. We'll send if we have at least a + // segment, or if we really have some data to send and we can send + // all that we have, or the send window is > 0 and we need to force + // output or send a FIN (note that if we need to force output + // SendWin will be at least 1 from the check above), or if we can + // send an amount == to at least half the maximum send window + // we've seen. + if (AmountToSend == SendTCB->tcb_mss || + (AmountToSend != 0 && AmountToSend == AmtUnsent) || + (SendWin != 0 && + ((SendTCB->tcb_flags & (FORCE_OUTPUT | FIN_NEEDED)) || + AmountToSend >= (SendTCB->tcb_maxwin / 2)))) { + + // It's OK to send something. Try to get a header buffer now. + FirstBuffer = GetTCPHeader(); + if (FirstBuffer != NULL) { + + // Got a header buffer. Loop through the sends on the TCB, + // building a frame. + CurrentBuffer = FirstBuffer; + CurSend = SendTCB->tcb_cursend; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(FirstBuffer) + + LocalNetInfo.ipi_hsize); + + SCC = (SendCmpltContext *)(Header + 1); +#ifdef DEBUG + SCC->scc_sig = scc_signature; +#endif + FillTCPHeader(SendTCB, Header); + + SCC->scc_ubufcount = 0; + SCC->scc_tbufcount = 0; + SCC->scc_count = 0; + + AmountLeft = AmountToSend; + + if (AmountToSend != 0) { + long Result; + + + CTEStructAssert(CurSend, tsr); + SCC->scc_firstsend = CurSend; + + do { + CTEAssert(CurSend->tsr_refcnt > 0); + Result = CTEInterlockedIncrementLong( + &(CurSend->tsr_refcnt) + ); + + CTEAssert(Result > 0); + + SCC->scc_count++; + // If the current send offset is 0 and the current + // send is less than or equal to what we have left + // to send, we haven't already put a transport + // buffer on this send, and nobody else is using + // the buffer chain directly, just use the input + // buffers. We check for other people using them + // by looking at tsr_lastbuf. If it's NULL, + // nobody else is using the buffers. If it's not + // NULL, somebody is. + + if (SendTCB->tcb_sendofs == 0 && + (SendTCB->tcb_sendsize <= AmountLeft) && + (SCC->scc_tbufcount == 0) && + CurSend->tsr_lastbuf == NULL) { + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = + SendTCB->tcb_sendbuf; + do { + SCC->scc_ubufcount++; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL); + + CurSend->tsr_lastbuf = CurrentBuffer; + AmountLeft -= SendTCB->tcb_sendsize; + SendTCB->tcb_sendsize = 0; + } else { + uint AmountToDup; + PNDIS_BUFFER NewBuf, Buf; + uint Offset; + NDIS_STATUS NStatus; + uchar *VirtualAddress; + uint Length; + + // Either the current send has more data than + // we want to send, or the starting offset is + // not 0. In either case we'll need to loop + // through the current send, allocating buffers. + Buf = SendTCB->tcb_sendbuf; + Offset = SendTCB->tcb_sendofs; + + do { + CTEAssert(Buf != NULL); + + NdisQueryBuffer(Buf, &VirtualAddress, + &Length); + + CTEAssert((Offset < Length) || + (Offset == 0 && Length == 0)); + + // Adjust the length for the offset into + // this buffer. + + Length -= Offset; + + AmountToDup = MIN(AmountLeft, Length); + + NdisAllocateBuffer(&NStatus, &NewBuf, + TCPSendBufferPool, + VirtualAddress + Offset, + AmountToDup); + if (NStatus == NDIS_STATUS_SUCCESS) { + SCC->scc_tbufcount++; + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = + NewBuf; + + CurrentBuffer = NewBuf; + if (AmountToDup >= Length) { + // Exhausted this buffer. + Buf = NDIS_BUFFER_LINKAGE(Buf); + Offset = 0; + } else { + Offset += AmountToDup; + CTEAssert(Offset < NdisBufferLength(Buf)); + } + + SendTCB->tcb_sendsize -= AmountToDup; + AmountLeft -= AmountToDup; + } else { + // Couldn't allocate a buffer. If + // the packet is already partly built, + // send what we've got, otherwise + // bail out. + if (SCC->scc_tbufcount == 0 && + SCC->scc_ubufcount == 0) { + TCPSendComplete(SCC, FirstBuffer); + goto error_oor; + } + AmountToSend -= AmountLeft; + AmountLeft = 0; + } + } while (AmountLeft && SendTCB->tcb_sendsize); + + SendTCB->tcb_sendbuf = Buf; + SendTCB->tcb_sendofs = Offset; + } + + if (CurSend->tsr_flags & TSR_FLAG_URG) { + ushort UP; + // This send is urgent data. We need to figure + // out what the urgent data pointer should be. + // We know sendnext is the starting sequence + // number of the frame, and that at the top of + // this do loop sendnext identified a byte in + // the CurSend at that time. We advanced CurSend + // at the same rate we've decremented + // AmountLeft (AmountToSend - AmountLeft == + // AmountBuilt), so sendnext + + // (AmountToSend - AmountLeft) identifies a byte + // in the current value of CurSend, and that + // quantity plus tcb_sendsize is the sequence + // number one beyond the current send. + UP = + (ushort)(AmountToSend - AmountLeft) + + (ushort)SendTCB->tcb_sendsize - + ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1); + + Header->tcp_urgent = net_short(UP); + + Header->tcp_flags |= TCP_FLAG_URG; + } + + // See if we've exhausted this send. If we have, + // set the PUSH bit in this frame and move on to + // the next send. We also need to check the + // urgent data bit. + if (SendTCB->tcb_sendsize == 0) { + Queue *Next; + uchar PrevFlags; + + // We've exhausted this send. Set the PUSH bit. + Header->tcp_flags |= TCP_FLAG_PUSH; + PrevFlags = CurSend->tsr_flags; + Next = QNEXT(&CurSend->tsr_req.tr_q); + if (Next != QEND(&SendTCB->tcb_sendq)) { + CurSend = STRUCT_OF(TCPSendReq, + QSTRUCT(TCPReq, Next, tr_q), tsr_req); + CTEStructAssert(CurSend, tsr); + SendTCB->tcb_sendsize = CurSend->tsr_unasize; + SendTCB->tcb_sendofs = CurSend->tsr_offset; + SendTCB->tcb_sendbuf = CurSend->tsr_buffer; + SendTCB->tcb_cursend = CurSend; + + // Check the urgent flags. We can't combine + // new urgent data on to the end of old + // non-urgent data. + if ((PrevFlags & TSR_FLAG_URG) && ! + (CurSend->tsr_flags & TSR_FLAG_URG)) + break; + } else { + CTEAssert(AmountLeft == 0); + SendTCB->tcb_cursend = NULL; + SendTCB->tcb_sendbuf = NULL; + } + } + } while (AmountLeft != 0); + + } else { + + // We're in the loop, but AmountToSend is 0. This + // should happen only when we're sending a FIN. Check + // this, and return if it's not true. + CTEAssert(AmtUnsent == 0); + if (!(SendTCB->tcb_flags & FIN_NEEDED)) { + // DEBUGCHK; + FreeTCPHeader(FirstBuffer); + break; + } + + SCC->scc_firstsend = NULL; + NDIS_BUFFER_LINKAGE(FirstBuffer) = NULL; + } + + // Adjust for what we're really going to send. + AmountToSend -= AmountLeft; + + // Update the sequence numbers, and start a RTT measurement + // if needed. + + OldSeq = SendTCB->tcb_sendnext; + SendTCB->tcb_sendnext += AmountToSend; + + if (SEQ_EQ(OldSeq, SendTCB->tcb_sendmax)) { + // We're sending entirely new data. + + // We can't advance sendmax once FIN_SENT is set. + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT)); + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + // We've advanced sendmax, so we must be sending some + // new data, so bump the outsegs counter. + TStats.ts_outsegs++; + + if (SendTCB->tcb_rtt == 0) { + // No RTT running, so start one. + SendTCB->tcb_rtt = TCPTime; + SendTCB->tcb_rttseq = OldSeq; + } + } else { + // We have at least some retransmission. + + TStats.ts_retranssegs++; + if (SEQ_GT(SendTCB->tcb_sendnext, SendTCB->tcb_sendmax)) { + // But we also have some new data, so check the + // rtt stuff. + TStats.ts_outsegs++; + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT)); + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + + if (SendTCB->tcb_rtt == 0) { + // No RTT running, so start one. + SendTCB->tcb_rtt = TCPTime; + SendTCB->tcb_rttseq = OldSeq; + } + } + } + + // We've built the frame entirely. If we've send everything + // we have and their's a FIN pending, OR it in. + if (AmtUnsent == AmountToSend) { + if (SendTCB->tcb_flags & FIN_NEEDED) { + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT) || + (SendTCB->tcb_sendnext == (SendTCB->tcb_sendmax - 1))); + // See if we still have room in the window for a FIN. + if (SendWin > (int) AmountToSend) { + Header->tcp_flags |= TCP_FLAG_FIN; + SendTCB->tcb_sendnext++; + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + SendTCB->tcb_flags |= (FIN_SENT | FIN_OUTSTANDING); + SendTCB->tcb_flags &= ~FIN_NEEDED; + } + } + } + + AmountToSend += sizeof(TCPHeader); + + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + + SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED | + FORCE_OUTPUT); + STOP_TCB_TIMER(SendTCB->tcb_delacktimer); + STOP_TCB_TIMER(SendTCB->tcb_swstimer); + SendTCB->tcb_alive = TCPTime; + + CTEFreeLock(&SendTCB->tcb_lock, TCBHandle); + + // We're all set. Xsum it and send it. + Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum + + (uint)net_short(AmountToSend), FirstBuffer); + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC, + FirstBuffer, AmountToSend, SendTCB->tcb_daddr, + SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce, + PROTOCOL_TCP); + + SendTCB->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) { + TCPSendComplete(SCC, FirstBuffer); + if (SendStatus != IP_SUCCESS) { + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + // This packet didn't get sent. If nothing's + // changed in the TCB, put sendnext back to + // what we just tried to send. Depending on + // the error, we may try again. + if (SEQ_GTE(OldSeq, SendTCB->tcb_senduna) && + SEQ_LT(OldSeq, SendTCB->tcb_sendnext)) + ResetSendNext(SendTCB, OldSeq); + + // We know this packet didn't get sent. Start + // the retransmit timer now, if it's not already + // runnimg, in case someone came in while we + // were in IP and stopped it. + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + + // If it failed because of an MTU problem, get + // the new MTU and try again. + if (SendStatus == IP_PACKET_TOO_BIG) { + uint NewMTU; + + // The MTU has changed. Update it, and try + // again. + SendStatus = (*LocalNetInfo.ipi_getpinfo)( + SendTCB->tcb_daddr, SendTCB->tcb_saddr, + &NewMTU, NULL); + + if (SendStatus != IP_SUCCESS) + break; + + // We have a new MTU. Make sure it's big enough + // to use. If not, correct this and turn off + // MTU discovery on this TCB. Otherwise use the + // new MTU. + if (NewMTU <= (sizeof(TCPHeader) + + SendTCB->tcb_opt.ioi_optlength)) { + + // The new MTU is too small to use. Turn off + // PMTU discovery on this TCB, and drop to + // our off net MTU size. + SendTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF; + SendTCB->tcb_mss = MIN((ushort)MAX_REMOTE_MSS, + SendTCB->tcb_remmss); + } else { + + // The new MTU is adequate. Adjust it for + // the header size and options length, and use + // it. + NewMTU -= sizeof(TCPHeader) - + SendTCB->tcb_opt.ioi_optlength; + SendTCB->tcb_mss = MIN((ushort)NewMTU, + SendTCB->tcb_remmss); + } + + CTEAssert(SendTCB->tcb_mss > 0); + + continue; + } + break; + } + } + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + continue; + } else // FirstBuffer != NULL. + goto error_oor; + } else { + // We've decided we can't send anything now. Figure out why, and + // see if we need to set a timer. + if (SendTCB->tcb_sendwin == 0) { + if (!(SendTCB->tcb_flags & FLOW_CNTLD)) { + SendTCB->tcb_flags |= FLOW_CNTLD; + SendTCB->tcb_rexmitcnt = 0; + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + SendTCB->tcb_slowcount++; + SendTCB->tcb_fastchk |= TCP_FLAG_SLOW; + } else + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + } else + if (AmountToSend != 0) + // We have something to send, but we're not sending + // it, presumably due to SWS avoidance. + if (!TCB_TIMER_RUNNING(SendTCB->tcb_swstimer)) + START_TCB_TIMER(SendTCB->tcb_swstimer, SWS_TO); + + break; + } + } // while (!FIN_OUTSTANDING) + + // We're done sending, so we don't need the output flags set. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT | + SEND_AFTER_RCV); + } else + SendTCB->tcb_flags |= SEND_AFTER_RCV; + + DerefTCB(SendTCB, TCBHandle); + return; + +// Common case error handling code for out of resource conditions. Start the +// retransmit timer if it's not already running (so that we try this again +// later), clean up and return. +error_oor: + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + // We had an out of resource problem, so clear the OUTPUT flags. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT); + DerefTCB(SendTCB, TCBHandle); + return; + +} + + +#if FAST_RETRANSMIT +//* ResetSendNextAndFastSend - Set the sendnext value of a TCB. +// +// Called to handle fast retransmit of the segment which the reveiver +// is asking for. +// tcb_lock will be held while entering (called by TCPRcv) +// and will be released in this routine after doing IP xmit. +// +// Input: SeqTCB - Pointer to TCB to be updated. +// NewSeq - Sequence number to set. +// +// Returns: Nothing. +// +void +ResetAndFastSend(TCB *SeqTCB, SeqNum NewSeq) +{ + + TCPSendReq *SendReq; + uint AmtForward; + Queue *CurQ; + PNDIS_BUFFER Buffer; + uint Offset; + uint SendSize; + CTELockHandle TCBHandle; + + CTEStructAssert(SeqTCB, tcb); + CTEAssert(SEQ_GTE(NewSeq, SeqTCB->tcb_senduna)); + + // The new seq must be less than send max, or NewSeq, senduna, sendnext, + // and sendmax must all be equal. (The latter case happens when we're + // called exiting TIME_WAIT, or possibly when we're retransmitting + // during a flow controlled situation). + + CTEAssert(SEQ_LT(NewSeq, SeqTCB->tcb_sendmax) || + (SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendnext) && + SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendmax) && + SEQ_EQ(SeqTCB->tcb_senduna, NewSeq))); + + + + //KdPrint(("Resetandfastsend TCB %x, seq %x\n", SeqTCB, NewSeq)); + + if (SYNC_STATE(SeqTCB->tcb_state) && SeqTCB->tcb_state != TCB_TIME_WAIT) { + // In these states we need to update the send queue. + + if (!EMPTYQ(&SeqTCB->tcb_sendq)) { + + CurQ = QHEAD(&SeqTCB->tcb_sendq); + + SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q); + + // SendReq points to the first send request on the send queue. + // We're pointing at the proper send req now. We need to go down + + // SendReq points to the cursend + // SendSize point to sendsize in the cursend + + SendSize = SendReq->tsr_unasize; + + Buffer = SendReq->tsr_buffer; + Offset = SendReq->tsr_offset; + + // Call the fast retransmit send now + + //KdPrint(("Calling fastsend buf %x, Offset %x\n", Buffer,Offset)); + + TCPFastSend(SeqTCB, Buffer, Offset, SendReq, SendSize); + + + } else { + + CTEAssert(SeqTCB->tcb_cursend == NULL); + } + + } + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + DerefTCB(SeqTCB, TCBHandle); + return; + +} + +//* TCPFastSend - To send a segment without changing TCB state +// +// Called to handle fast retransmit of the segment +// tcb_lock will be held while entering (called by TCPRcv) +// +// Input: SendTCB - Pointer to TCB +// in_sendBuf - Pointer to ndis_buffer +// in_sendofs - Send Offset +// in_sendreq - current send request +// in_sendsize - size of this send +// +// Returns: Nothing. +// + +void +TCPFastSend(TCB *SendTCB, + PNDIS_BUFFER in_SendBuf, + uint in_SendOfs, + TCPSendReq *in_SendReq, + uint in_SendSize) +{ + + int SendWin; // Useable send window. + uint AmountToSend; // Amount to send this time. + uint AmountLeft; + TCPHeader *Header; // TCP header for a send. + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + TCPSendReq *CurSend; + SendCmpltContext *SCC; + SeqNum OldSeq; + IP_STATUS SendStatus; + uint AmtOutstanding, AmtUnsent; + int ForceWin; // Window we're force to use. + CTELockHandle TCBHandle; + + uint SendOfs = in_SendOfs; + uint SendSize = in_SendSize; + PNDIS_BUFFER SendBuf; + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + + CTEStructAssert(SendTCB, tcb); + CTEAssert(SendTCB->tcb_refcnt != 0); + + CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0); + CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss); + + CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) || + (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax)); + + + + AmtOutstanding = (uint)(SendTCB->tcb_sendnext - + SendTCB->tcb_senduna); + + AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding; + + CTEAssert(*(int *)&AmtUnsent >= 0); + + SendWin = SendTCB->tcb_mss; + + + AmountToSend = MIN(in_SendSize, SendTCB->tcb_mss); + + CTEAssert (AmountToSend >= 0); + + + CTEAssert(SendTCB->tcb_mss > 0); + + // See if we have enough to send. We'll send if we have at least a + // segment, or if we really have some data to send and we can send + // all that we have, or the send window is > 0 and we need to force + // output or send a FIN (note that if we need to force output + // SendWin will be at least 1 from the check above), or if we can + // send an amount == to at least half the maximum send window + // we've seen. + + //KdPrint(("In fastsend Sendwin %x, Amttosend %x\n", SendWin,AmountToSend)); + + if (AmountToSend >= 0) { + + + // It's OK to send something. Try to get a header buffer now. + // Mark the TCB for debugging. + // This should be removed for shipping version. + + SendTCB->tcb_fastchk |= TCP_FLAG_FASTREC; + + FirstBuffer = GetTCPHeader(); + + if (FirstBuffer != NULL) { + + // Got a header buffer. Loop through the sends on the TCB, + // building a frame. + + CurrentBuffer = FirstBuffer; + CurSend = in_SendReq; + SendOfs = in_SendOfs; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(FirstBuffer) + + LocalNetInfo.ipi_hsize); + + SCC = (SendCmpltContext *)(Header + 1); + +#ifdef DEBUG + SCC->scc_sig = scc_signature; +#endif + FillTCPHeader(SendTCB, Header); + { + ulong L = SendTCB->tcb_senduna; + Header->tcp_seq = net_long(L); + + } + + SCC->scc_ubufcount = 0; + SCC->scc_tbufcount = 0; + SCC->scc_count = 0; + + AmountLeft = AmountToSend; + + if (AmountToSend != 0) { + long Result; + + CTEStructAssert(CurSend, tsr); + SCC->scc_firstsend = CurSend; + + do { + + CTEAssert(CurSend->tsr_refcnt > 0); + + Result = CTEInterlockedIncrementLong( + &(CurSend->tsr_refcnt) + ); + + CTEAssert(Result > 0); + + SCC->scc_count++; + + // If the current send offset is 0 and the current + // send is less than or equal to what we have left + // to send, we haven't already put a transport + // buffer on this send, and nobody else is using + // the buffer chain directly, just use the input + // buffers. We check for other people using them + // by looking at tsr_lastbuf. If it's NULL, + // nobody else is using the buffers. If it's not + // NULL, somebody is. + + if (SendOfs == 0 && + (SendSize <= AmountLeft) && + (SCC->scc_tbufcount == 0) && + CurSend->tsr_lastbuf == NULL) { + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = in_SendBuf; + + do { + SCC->scc_ubufcount++; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL); + + CurSend->tsr_lastbuf = CurrentBuffer; + AmountLeft -= SendSize; + //KdPrint(("nobody using this CurSend %x\n",CurSend )); + // SendSize = 0; + } else { + uint AmountToDup; + PNDIS_BUFFER NewBuf, Buf; + uint Offset; + NDIS_STATUS NStatus; + uchar *VirtualAddress; + uint Length; + + // Either the current send has more data than + // we want to send, or the starting offset is + // not 0. In either case we'll need to loop + // through the current send, allocating buffers. + + Buf = in_SendBuf; + + Offset = SendOfs; + + do { + CTEAssert(Buf != NULL); + + NdisQueryBuffer(Buf, &VirtualAddress, + &Length); + + CTEAssert((Offset < Length) || + (Offset == 0 && Length == 0)); + + // Adjust the length for the offset into + // this buffer. + + Length -= Offset; + + AmountToDup = MIN(AmountLeft, Length); + + NdisAllocateBuffer(&NStatus, &NewBuf, + TCPSendBufferPool, + VirtualAddress + Offset, + AmountToDup); + + if (NStatus == NDIS_STATUS_SUCCESS) { + + SCC->scc_tbufcount++; + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = NewBuf; + + CurrentBuffer = NewBuf; + + if (AmountToDup >= Length) { + + // Exhausted this buffer. + + Buf = NDIS_BUFFER_LINKAGE(Buf); + Offset = 0; + + } else { + + Offset += AmountToDup; + CTEAssert(Offset < NdisBufferLength(Buf)); + } + + SendSize -= AmountToDup; + AmountLeft -= AmountToDup; + + } else { + + // Couldn't allocate a buffer. If + // the packet is already partly built, + // send what we've got, otherwise + // bail out. + + if (SCC->scc_tbufcount == 0 && + SCC->scc_ubufcount == 0) { + TCPSendComplete(SCC, FirstBuffer); + goto error_oor; + } + + AmountToSend -= AmountLeft; + AmountLeft = 0; + + } + } while (AmountLeft && SendSize); + + SendBuf = Buf; + SendOfs = Offset; + //KdPrint(("Ready to send. SendBuf %x SendOfs %x\n",SendBuf, SendOfs )); + + } + + if (CurSend->tsr_flags & TSR_FLAG_URG) { + ushort UP; + + KdPrint(("Fast send in URG %x\n", CurSend)); + + // This send is urgent data. We need to figure + // out what the urgent data pointer should be. + // We know sendnext is the starting sequence + // number of the frame, and that at the top of + // this do loop sendnext identified a byte in + // the CurSend at that time. We advanced CurSend + // at the same rate we've decremented + // AmountLeft (AmountToSend - AmountLeft == + // AmountBuilt), so sendnext + + // (AmountToSend - AmountLeft) identifies a byte + // in the current value of CurSend, and that + // quantity plus tcb_sendsize is the sequence + // number one beyond the current send. + + UP = + (ushort)(AmountToSend - AmountLeft) + + (ushort)SendTCB->tcb_sendsize - + ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1); + + Header->tcp_urgent = net_short(UP); + + Header->tcp_flags |= TCP_FLAG_URG; + } + + // See if we've exhausted this send. If we have, + // set the PUSH bit in this frame and move on to + // the next send. We also need to check the + // urgent data bit. + + if (SendSize == 0) { + Queue *Next; + uchar PrevFlags; + + // We've exhausted this send. Set the PUSH bit. + Header->tcp_flags |= TCP_FLAG_PUSH; + PrevFlags = CurSend->tsr_flags; + Next = QNEXT(&CurSend->tsr_req.tr_q); + if (Next != QEND(&SendTCB->tcb_sendq)) { + CurSend = STRUCT_OF(TCPSendReq, + QSTRUCT(TCPReq, Next, tr_q), tsr_req); + CTEStructAssert(CurSend, tsr); + SendSize = CurSend->tsr_unasize; + SendOfs = CurSend->tsr_offset; + SendBuf = CurSend->tsr_buffer; + CurSend = CurSend; + + // Check the urgent flags. We can't combine + // new urgent data on to the end of old + // non-urgent data. + if ((PrevFlags & TSR_FLAG_URG) && ! + (CurSend->tsr_flags & TSR_FLAG_URG)) + break; + } else { + CTEAssert(AmountLeft == 0); + CurSend = NULL; + SendBuf = NULL; + } + } + + } while (AmountLeft != 0); + + } else { + + // Amt to send is 0. + // Just bail out and strat timer. + + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + FreeTCPHeader(FirstBuffer); + return; + + } + + // Adjust for what we're really going to send. + + AmountToSend -= AmountLeft; + + + TStats.ts_retranssegs++; + + // We've built the frame entirely. If we've send everything + // we have and their's a FIN pending, OR it in. + + AmountToSend += sizeof(TCPHeader); + + + SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED | + FORCE_OUTPUT); + + + + STOP_TCB_TIMER(SendTCB->tcb_delacktimer); + STOP_TCB_TIMER(SendTCB->tcb_swstimer); + + SendTCB->tcb_alive = TCPTime; + + SendTCB->tcb_fastchk &= ~TCP_FLAG_FASTREC; + + CTEFreeLock(&SendTCB->tcb_lock, TCBHandle); + + //KdPrint (("Going out to IP SendTCB %x, Firstbuf %x\n", SendTCB, FirstBuffer)); + // We're all set. Xsum it and send it. + + Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum + + (uint)net_short(AmountToSend), FirstBuffer); + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC, + FirstBuffer, AmountToSend, SendTCB->tcb_daddr, + SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce, + PROTOCOL_TCP); + + SendTCB->tcb_error = SendStatus; + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + + if (SendStatus != IP_PENDING) { + TCPSendComplete(SCC, FirstBuffer); + } + + //Reacquire Lock to keep DerefTCB happy + //Bug #63904 + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + + + } else { // FirstBuffer != NULL. + goto error_oor; + } + } else{ + + SendTCB->tcb_flags |= SEND_AFTER_RCV; + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + } + + + SendTCB->tcb_flags |= NEED_OUTPUT; + + return; + +// Common case error handling code for out of resource conditions. Start the +// retransmit timer if it's not already running (so that we try this again +// later), clean up and return. + +error_oor: + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + // We had an out of resource problem, so clear the OUTPUT flags. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT); + + return; + + +} + +#endif //FAST_RETRANSMIT + + + + +//* TDISend - Send data on a connection. +// +// The main TDI send entry point. We take the input parameters, validate them, +// allocate a send request, etc. We then put the send request on the queue. +// If we have no other sends on the queue or Nagling is disabled we'll +// call TCPSend to send the data. +// +// Input: Request - The TDI request for the call. +// Flags - Flags for this send. +// SendLength - Length in bytes of send. +// SendBuffer - Pointer to buffer chain to be sent. +// +// Returns: Status of attempt to send. +// +TDI_STATUS +TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength, + PNDIS_BUFFER SendBuffer) +{ + TCPConn *Conn; + TCB *SendTCB; + TCPSendReq *SendReq; + CTELockHandle ConnTableHandle, TCBHandle; + TDI_STATUS Error; + uint EmptyQ; + +#ifdef DEBUG + uint RealSendSize; + PNDIS_BUFFER Temp; + + // Loop through the buffer chain, and make sure that the length matches + // up with SendLength. + + Temp = SendBuffer; + RealSendSize = 0; + do { + CTEAssert(Temp != NULL); + + RealSendSize += NdisBufferLength(Temp); + Temp = NDIS_BUFFER_LINKAGE(Temp); + } while (Temp != NULL); + + CTEAssert(RealSendSize == SendLength); + +#endif + + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + SendTCB = Conn->tc_tcb; + if (SendTCB != NULL) { + CTEStructAssert(SendTCB, tcb); + CTEGetLockAtDPC(&SendTCB->tcb_lock, &TCBHandle); + CTEFreeLockFromDPC(&ConnTableLock, TCBHandle); + if (DATA_SEND_STATE(SendTCB->tcb_state) && !CLOSING(SendTCB)) { + // We have a TCB, and it's valid. Get a send request now. + + CheckTCBSends(SendTCB); + + if (SendLength != 0) { + + SendReq = GetSendReq(); + if (SendReq != NULL) { + SendReq->tsr_req.tr_rtn = Request->RequestNotifyObject; + SendReq->tsr_req.tr_context = Request->RequestContext; + SendReq->tsr_buffer = SendBuffer; + SendReq->tsr_size = SendLength; + SendReq->tsr_unasize = SendLength; + SendReq->tsr_refcnt = 1; // ACK will decrement this ref + SendReq->tsr_offset = 0; + SendReq->tsr_lastbuf = NULL; + SendReq->tsr_time = TCPTime; + SendReq->tsr_flags = (Flags & TDI_SEND_EXPEDITED) ? + TSR_FLAG_URG : 0; + SendTCB->tcb_unacked += SendLength; + + EmptyQ = EMPTYQ(&SendTCB->tcb_sendq); + ENQUEUE(&SendTCB->tcb_sendq, &SendReq->tsr_req.tr_q); + if (SendTCB->tcb_cursend == NULL) { + SendTCB->tcb_cursend = SendReq; + SendTCB->tcb_sendbuf = SendBuffer; + SendTCB->tcb_sendofs = 0; + SendTCB->tcb_sendsize = SendLength; + } + if (EmptyQ) { + SendTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle); + TCPSend(SendTCB); +#else + TCPSend(SendTCB, ConnTableHandle); +#endif + } else + if (!(SendTCB->tcb_flags & NAGLING) || + (SendTCB->tcb_unacked - (SendTCB->tcb_sendmax - + SendTCB->tcb_senduna)) >= SendTCB->tcb_mss) { + SendTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&SendTCB->tcb_lock, + ConnTableHandle); + TCPSend(SendTCB); +#else + TCPSend(SendTCB, ConnTableHandle); +#endif + } else + CTEFreeLock(&SendTCB->tcb_lock, + ConnTableHandle); + + return TDI_PENDING; + } else + Error = TDI_NO_RESOURCES; + } else + Error = TDI_SUCCESS; + } else + Error = TDI_INVALID_STATE; + + CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle); + return Error; + } else + Error = TDI_INVALID_STATE; + } else + Error = TDI_INVALID_CONNECTION; + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return Error; + +} + +#pragma BEGIN_INIT +extern void *TLRegisterProtocol(uchar Protocol, void *RcvHandler, + void *XmitHandler, void *StatusHandler, + void *RcvCmpltHandler); + +extern IP_STATUS TCPRcv(void *IPContext, IPAddr Dest, IPAddr Src, + IPAddr LocalAddr, IPAddr SrcAddr, + IPHeader UNALIGNED *IPH, uint IPHLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, + uchar Protocol, IPOptInfo *OptInfo); +extern void TCPRcvComplete(void); + +uchar SendInited = FALSE; + +//* FreeTCPHeaderList - Free the list of TCP header buffers. +// +// Called when we want to free the list of TCP header buffers. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +FreeTCPHeaderList(void) +{ + CTELockHandle Handle; + TCPHdrBPoolEntry *Entry; + + CTEGetLock(&TCPSendFreeLock, &Handle); + + Entry = TCPHdrBPoolList; + TCPHdrBPoolList = NULL; + + TCPCurrentSendFree = 0; + + while (Entry != NULL) { + TCPHdrBPoolEntry *OldEntry; + + NdisFreeBufferPool(Entry->the_handle); + CTEFreeMem(Entry->the_buffer); + OldEntry = Entry; + Entry = Entry->the_next; + CTEFreeMem(OldEntry); + } + + CTEFreeLock(&TCPSendFreeLock, Handle); + + +} + +//* InitTCPSend - Initialize our send side. +// +// Called during init time to initialize our TCP send state. +// +// Input: Nothing. +// +// Returns: TRUE if we inited, false if we didn't. +// +int +InitTCPSend(void) +{ + PNDIS_BUFFER Buffer; + NDIS_STATUS Status; + + +#ifdef NT + ExInitializeSListHead(&TCPSendFree); + ExInitializeSListHead(&TCPSendReqFree); +#endif + + CTEInitLock(&TCPSendReqFreeLock); + CTEInitLock(&TCPSendFreeLock); + CTEInitLock(&TCPSendReqCompleteLock); + + TCPHdrBPoolList = NULL; + TCPCurrentSendFree = 0; + + Buffer = GrowTCPHeaderList(); + + if (Buffer != NULL) + FreeTCPHeader(Buffer); + else + return FALSE; + + NdisAllocateBufferPool(&Status, &TCPSendBufferPool, NUM_TCP_BUFFERS); + if (Status != NDIS_STATUS_SUCCESS) { + FreeTCPHeaderList(); + return FALSE; + } + + TCPProtInfo = TLRegisterProtocol(PROTOCOL_TCP, TCPRcv, TCPSendComplete, + TCPStatus, TCPRcvComplete); + + if (TCPProtInfo == NULL) { + FreeTCPHeaderList(); + NdisFreeBufferPool(TCPSendBufferPool); + return FALSE; + } + + SendInited = TRUE; + return TRUE; +} + +//* UnInitTCPSend - UnInitialize our send side. +// +// Called during init time if we're going to fail to initialize. +// +// Input: Nothing. +// +// Returns: TRUE if we inited, false if we didn't. +// +void +UnInitTCPSend(void) +{ + if (!SendInited) + return; + + TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL); + FreeTCPHeaderList(); + NdisFreeBufferPool(TCPSendBufferPool); + +} +#pragma END_INIT + diff --git a/private/ntos/tdi/tcpip/tcp/tcpsend.h b/private/ntos/tdi/tcpip/tcp/tcpsend.h new file mode 100644 index 000000000..b74c69861 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpsend.h @@ -0,0 +1,105 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPSEND.H - TCP send protocol definitions. +// +// This file contains the definitions of TCP send protocol things. +// + +#ifdef NT + +#define NUM_TCP_HEADERS 32 +#define NUM_TCP_BUFFERS 150 +#define TCP_MAX_HDRS 0xffffffff + +#else // NT + +#define NUM_TCP_HEADERS 16 +#define NUM_TCP_BUFFERS 100 +#define TCP_MAX_HDRS 320 + +#endif // NT + +//#define SEND_DEBUG 1 + +#ifdef SEND_DEBUG +#define SEND_TICKS 10 +EXTERNAL_LOCK(SendUseLock) +extern struct TCPSendReq *SendUseList; +#endif + +//* Structure of a TCP send request. + +#define tsr_signature 0x20525354 // 'TSR ' + +typedef struct TCPSendReq { + struct TCPReq tsr_req; // General request structure. +#ifdef DEBUG + ulong tsr_sig; +#endif + uint tsr_size; // Size in bytes of data in send. + long tsr_refcnt; // Reference count for this send. + uchar tsr_flags; // Flags for this send. + uchar tsr_pad[3]; // Pad to dword boundary. + uint tsr_unasize; // Number of bytes unacked. + uint tsr_offset; // Offset into first buffer in chain + // of start of unacked data.. + PNDIS_BUFFER tsr_buffer; // Pointer to start of unacked buffer + // chain. + PNDIS_BUFFER tsr_lastbuf; // Pointer to last buffer in chain. + // Valid iff we've sent directly from + // the buffer chain w/o doing an + // NdisCopyBuffer. + uint tsr_time; // TCP time this was received. +#ifdef SEND_DEBUG + struct TCPSendReq *tsr_next; // Debug next field. + uint tsr_timer; // Timer field. + uint tsr_cmplt; // Who completed it. +#endif +} TCPSendReq; + +#define TSR_FLAG_URG 0x01 // Urgent data. + +//* Structure defining the context received during a send completes. + +#define scc_signature 0x20434353 // 'SCC ' + +typedef struct SendCmpltContext { +#ifdef DEBUG + ulong scc_sig; +#endif + + TCPSendReq *scc_firstsend; // First send in this context. + uint scc_count; // Number of sends in count. + ushort scc_ubufcount; // Number of 'user' buffers in send. + ushort scc_tbufcount; // Number of transport buffers in send. +} SendCmpltContext; + +EXTERNAL_LOCK(TCPSendReqCompleteLock) + +extern void InitSendState(struct TCB *NewTCB); +extern void SendSYN(struct TCB *SYNTcb, CTELockHandle); +extern void SendKA(struct TCB *KATCB, CTELockHandle Handle); +extern void SendRSTFromHeader(struct TCPHeader UNALIGNED *TCPH, uint Length, + IPAddr Dest, IPAddr Src, IPOptInfo *OptInfo); +extern void SendACK(struct TCB *ACKTcb); +extern void SendRSTFromTCB(struct TCB *RSTTcb); +extern void GoToEstab(struct TCB *EstabTCB); +extern void FreeSendReq(TCPSendReq *FreedReq); +extern void FreeTCPHeader(PNDIS_BUFFER FreedBuffer); + +extern int InitTCPSend(void); +extern void UnInitTCPSend(void); + +#ifdef VXD +extern void TCPSend(struct TCB *SendTCB); +#else +extern void TCPSend(struct TCB *SendTCB, CTELockHandle Handle); +#endif + +extern TDI_STATUS TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength, + PNDIS_BUFFER SendBuffer); +extern uint RcvWin(struct TCB *WinTCB); diff --git a/private/ntos/tdi/tcpip/tcp/tlcommon.c b/private/ntos/tdi/tcpip/tcp/tlcommon.c new file mode 100644 index 000000000..902378004 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tlcommon.c @@ -0,0 +1,553 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TLCOMMON.C - Common transport layer code. +// +// This file contains the code for routines that are common to +// both TCP and UDP. +// +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" +#ifdef NT +#include "tdikrnl.h" +#endif +#include "tlcommon.h" + +extern uint tcpxsum(uint Seed, void *Ptr, uint Length); +extern IPInfo LocalNetInfo; + +//* XsumSendChain - Checksum a chain of NDIS send buffers. +// +// Called to xsum a chain of NDIS send buffers. We're given the +// pseudo-header xsum to start with, and we call xsum on each +// buffer. We assume that this is a send chain, and that the +// first buffer of the chain has room for an IP header that we +// need to skip. +// +// Input: PHXsum - Pseudo-header xsum. +// BufChain - Pointer to NDIS_BUFFER chain. +// +// Returns: The computed xsum. +// + +ushort +XsumSendChain(uint PHXsum, PNDIS_BUFFER BufChain) +{ + uint HeaderSize; + uint OldLength; + uint SwapCount; + uchar *Ptr; + + HeaderSize = LocalNetInfo.ipi_hsize; + OldLength = 0; + SwapCount = 0; + + // + // ***** The following line of code can be removed if the pseudo + // checksum never has any bits sets in the upper word. + // + + PHXsum = (((PHXsum << 16) | (PHXsum >> 16)) + PHXsum) >> 16; + do { + + // + // If the length of the last buffer was odd, then swap the checksum. + // + + if ((OldLength & 1) != 0) { + PHXsum = ((PHXsum & 0xff) << 8) | (PHXsum >> 8); + SwapCount ^= 1; + } + + Ptr = (uchar *)NdisBufferVirtualAddress(BufChain) + HeaderSize; + PHXsum = tcpxsum(PHXsum, Ptr, NdisBufferLength(BufChain)); + HeaderSize = 0; + OldLength = NdisBufferLength(BufChain); + BufChain = NDIS_BUFFER_LINKAGE(BufChain); + } while(BufChain != NULL); + + // + // If an odd number of swaps were done, then swap the xsum again. + // + // N.B. At this point the checksum is only a word. + // + + if (SwapCount != 0) { + PHXsum = ((PHXsum & 0xff) << 8) | (PHXsum >> 8); + } + + return (ushort)PHXsum; +} + +//* XsumRcvBuf - Checksum a chain of IP receive buffers. +// +// Called to xsum a chain of IP receive buffers. We're given the +// pseudo-header xsum to start with, and we call xsum on each buffer. +// +// We assume that this rcv buf chain has no odd sized buffers, except +// possibly the last one. +// +// Input: PHXsum - Pseudo-header xsum. +// BufChain - Pointer to IPRcvBuf chain. +// +// Returns: The computed xsum. +// + +ushort +XsumRcvBuf(uint PHXsum, IPRcvBuf *BufChain) +{ + + // + // ***** The following line of code can be removed if the pseudo + // checksum never has any bits sets in the upper word. + // + + PHXsum = (((PHXsum << 16) | (PHXsum >> 16)) + PHXsum) >> 16; + do { + CTEAssert(!(BufChain->ipr_size & 1) || (BufChain->ipr_next == NULL)); + + PHXsum = tcpxsum(PHXsum, BufChain->ipr_buffer, BufChain->ipr_size); + BufChain = BufChain->ipr_next; + } while (BufChain != NULL); + + return (ushort)(PHXsum); +} + + +//* CopyRcvToNdis - Copy from an IPRcvBuf chain to an NDIS buffer chain. +// +// This is the function we use to copy from a chain of IP receive buffers +// to a chain of NDIS buffers. The caller specifies the source and destination, +// a maximum size to copy, and an offset into the first buffer to start +// copying from. We copy as much as possible up to the size, and return +// the size copied. +// +// Input: RcvBuf - Pointer to receive buffer chain. +// DestBuf - Pointer to NDIS buffer chain. +// Size - Size in bytes to copy. +// RcvOffset - Offset into first buffer to copy from. +// DestOffset - Offset into dest buffer to start copying at. +// +// Returns: Bytes copied. +// + +#ifdef NT + +uint +CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf, uint Size, + uint RcvOffset, uint DestOffset) +{ + uint TotalBytesCopied = 0; // Bytes we've copied so far. + uint BytesCopied = 0; // Bytes copied out of each buffer. + uint DestSize, RcvSize; // Size left in current destination and + // recv. buffers, respectively. + uint BytesToCopy; // How many bytes to copy this time. + NTSTATUS Status; + + + CTEAssert(RcvBuf != NULL); + + CTEAssert(RcvOffset <= RcvBuf->ipr_size); + + // The destination buffer can be NULL - this is valid, if odd. + if (DestBuf != NULL) { + + RcvSize = RcvBuf->ipr_size - RcvOffset; + DestSize = NdisBufferLength(DestBuf); + + if (Size < DestSize) { + DestSize = Size; + } + + do { + // Compute the amount to copy, and then copy from the + // appropriate offsets. + BytesToCopy = MIN(DestSize, RcvSize); + + Status = TdiCopyBufferToMdl(RcvBuf->ipr_buffer, RcvOffset, + BytesToCopy, DestBuf, DestOffset, &BytesCopied); + + if (!NT_SUCCESS(Status)) { + break; + } + + CTEAssert(BytesCopied == BytesToCopy); + + TotalBytesCopied += BytesCopied; + DestSize -= BytesCopied; + DestOffset += BytesCopied; + RcvSize -= BytesToCopy; + + if (!RcvSize) { + // Exhausted this buffer. + + RcvBuf = RcvBuf->ipr_next; + + // If we have another one, use it. + if (RcvBuf != NULL) { + RcvOffset = 0; + RcvSize = RcvBuf->ipr_size; + } + else { + break; + } + } + else { // Buffer not exhausted, update offset. + RcvOffset += BytesToCopy; + } + + } while (DestSize); + + } + + return TotalBytesCopied; + + +} + +#else // NT + +uint +CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf, uint Size, + uint RcvOffset, uint DestOffset) +{ + uint BytesCopied = 0; // Bytes we've copied so far. + uint DestSize, RcvSize; // Size left in current destination and + // recv. buffers, respectively. + uint BytesToCopy; // How many bytes to copy this time. + + CTEAssert(RcvBuf != NULL); + + CTEAssert(RcvOffset <= RcvBuf->ipr_size); + + // The destination buffer can be NULL - this is valid, if odd. + if (DestBuf != NULL) { + + DestSize = NdisBufferLength(DestBuf); + RcvSize = RcvBuf->ipr_size - RcvOffset; + + // + // Skip over DestOffset bytes + // + while (DestOffset >= DestSize) { + DestOffset -= DestSize; + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + + if (DestBuf == NULL) { + return(0); + } + + DestSize = NdisBufferLength(DestBuf); + } + + DestSize -= DestOffset; + + do { + + // Compute the amount to copy, and then copy from the + // appropriate offsets. + BytesToCopy = MIN(MIN(DestSize, RcvSize), Size); + + // Do the copy using the intrinsic - we might want to + // do this with a function that does a smarter job of + // copying. + + CTEMemCopy((uchar *)NdisBufferVirtualAddress(DestBuf) + DestOffset, + RcvBuf->ipr_buffer + RcvOffset, BytesToCopy); + + BytesCopied += BytesToCopy; + + if (!(RcvSize -= BytesToCopy)) { + // Exhausted this buffer. + + RcvBuf = RcvBuf->ipr_next; + + // If we have another one, use it. + if (RcvBuf != NULL) { + RcvOffset = 0; + RcvSize = RcvBuf->ipr_size; + } else + break; + } else // Buffer not exhausted, update offset. + RcvOffset += BytesToCopy; + + // Now do the same thing for the destination buffer. + if (!(DestSize -= BytesToCopy)) { + // Exhausted this buffer. + + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + + // If we have another one, use it. + if (DestBuf != NULL) { + DestOffset = 0; + DestSize = NdisBufferLength(DestBuf); + } else + break; + } else // Buffer not exhausted, update offset. + DestOffset += BytesToCopy; + + Size -= BytesToCopy; // Decrement amount left to copy. + } while (Size); + + } + + return BytesCopied; + + +} +#endif // NT + + +//* CopyRcvToBuffer - Copy from an IPRcvBuf chain to a flat buffer. +// +// Called during receive processing to copy from an IPRcvBuffer chain to a +// flag buffer. We skip Offset bytes in the src chain, and then +// copy Size bytes. +// +// Input: DestBuf - Pointer to destination buffer. +// SrcRB - Pointer to SrcRB chain. +// Size - Size in bytes to copy. +// SrcOffset - Offset in SrcRB to start copying from. +// +// Returns: Nothing. +// +void +CopyRcvToBuffer(uchar *DestBuf, IPRcvBuf *SrcRB, uint Size, uint SrcOffset) +{ +#ifdef DEBUG + IPRcvBuf *TempRB; + uint TempSize; +#endif + + CTEAssert(DestBuf != NULL); + CTEAssert(SrcRB != NULL); + + // In debug versions check to make sure we're copying a reasonable size + // and from a reasonable offset. + +#ifdef DEBUG + TempRB = SrcRB; + TempSize = 0; + while (TempRB != NULL) { + TempSize += TempRB->ipr_size; + TempRB = TempRB->ipr_next; + } + + CTEAssert(SrcOffset < TempSize); + CTEAssert((SrcOffset + Size) <= TempSize); +#endif + + // First, skip Offset bytes. + while (SrcOffset >= SrcRB->ipr_size) { + SrcOffset -= SrcRB->ipr_size; + SrcRB = SrcRB->ipr_next; + } + + while (Size != 0) { + uint BytesToCopy, SrcSize; + + CTEAssert(SrcRB != NULL); + + SrcSize = SrcRB->ipr_size - SrcOffset; + BytesToCopy = MIN(Size, SrcSize); + CTEMemCopy(DestBuf, SrcRB->ipr_buffer + SrcOffset, BytesToCopy); + + if (BytesToCopy == SrcSize) { + // Copied everything from this buffer. + SrcRB = SrcRB->ipr_next; + SrcOffset = 0; + } + + DestBuf += BytesToCopy; + Size -= BytesToCopy; + } + +} + +//* CopyFlatToNdis - Copy a flat buffer to an NDIS_BUFFER chain. +// +// A utility function to copy a flat buffer to an NDIS buffer chain. We +// assume that the NDIS_BUFFER chain is big enough to hold the copy amount; +// in a debug build we'll debugcheck if this isn't true. We return a pointer +// to the buffer where we stopped copying, and an offset into that buffer. +// This is useful for copying in pieces into the chain. +// +// Input: DestBuf - Destination NDIS_BUFFER chain. +// SrcBuf - Src flat buffer. +// Size - Size in bytes to copy. +// StartOffset - Pointer to start of offset into first buffer in +// chain. Filled in on return with the offset to +// copy into next. +// BytesCopied - Pointer to a variable into which to store the +// number of bytes copied by this operation +// +// Returns: Pointer to next buffer in chain to copy into. +// + +#ifdef NT + +PNDIS_BUFFER +CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset, uint *BytesCopied) +{ + NTSTATUS Status = 0; + + *BytesCopied = 0; + + Status = TdiCopyBufferToMdl(SrcBuf, 0, Size, DestBuf, *StartOffset, + BytesCopied); + + *StartOffset += *BytesCopied; + + // + // Always return the first buffer, since the TdiCopy function handles + // finding the appropriate buffer based on offset. + // + return(DestBuf); + +} + +#else // NT + +PNDIS_BUFFER +CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset, uint *BytesCopied) +{ + uint CopySize; + uchar *DestPtr; + uint DestSize; + uint Offset = *StartOffset; + uint bytesCopied = 0; + + CTEAssert(DestBuf != NULL); + CTEAssert(SrcBuf != NULL); + + CTEAssert(NdisBufferLength(DestBuf) >= Offset); + DestPtr = ((uchar *) NdisBufferVirtualAddress(DestBuf)) + Offset; + DestSize = NdisBufferLength(DestBuf) - Offset; + + for (;;) { + CopySize = MIN(Size, DestSize); + CTEMemCopy(DestPtr, SrcBuf, CopySize); + + DestPtr += CopySize; + SrcBuf += CopySize; + bytesCopied += CopySize; + + if ((Size -= CopySize) == 0) + break; + + if ((DestSize -= CopySize) == 0) { + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + CTEAssert(DestBuf != NULL); + DestPtr = NdisBufferVirtualAddress(DestBuf); + DestSize = NdisBufferLength(DestBuf); + } + } + + *StartOffset = DestPtr - NdisBufferVirtualAddress(DestBuf); + *BytesCopied = bytesCopied; + + return DestBuf; + +} + +#endif // NT + + +//* BuildTDIAddress - Build a TDI address structure. +// +// Called when we need to build a TDI address structure. We fill in +// the specifed buffer with the correct information in the correct +// format. +// +// Input: Buffer - Buffer to be filled in as TDI address structure. +// Addr - IP Address to fill in. +// Port - Port to be filled in. +// +// Returns: Nothing. +// +void +BuildTDIAddress(uchar *Buffer, IPAddr Addr, ushort Port) +{ + PTRANSPORT_ADDRESS XportAddr; + PTA_ADDRESS TAAddr; + + XportAddr = (PTRANSPORT_ADDRESS)Buffer; + XportAddr->TAAddressCount = 1; + TAAddr = XportAddr->Address; + TAAddr->AddressType = TDI_ADDRESS_TYPE_IP; + TAAddr->AddressLength = sizeof(TDI_ADDRESS_IP); + ((PTDI_ADDRESS_IP)TAAddr->Address)->sin_port = Port; + ((PTDI_ADDRESS_IP)TAAddr->Address)->in_addr = Addr; +} + +//* UpdateConnInfo - Update a connection information structure. +// +// Called when we need to update a connection information structure. We +// copy any options, and create a transport address. If any buffer is +// too small we return an error. +// +// Input: ConnInfo - Pointer to TDI_CONNECTION_INFORMATION struc +// to be filled in. +// OptInfo - Pointer to IP options information. +// SrcAddress - Source IP address. +// SrcPort - Source port. +// +// Returns: TDI_SUCCESS if it worked, TDI_BUFFER_OVERFLOW for an error. +// +TDI_STATUS +UpdateConnInfo(PTDI_CONNECTION_INFORMATION ConnInfo, IPOptInfo *OptInfo, + IPAddr SrcAddress, ushort SrcPort) +{ + TDI_STATUS Status = TDI_SUCCESS; // Default status to return. + uint AddrLength, OptLength; + + + if (ConnInfo != NULL) { + ConnInfo->UserDataLength = 0; // No user data. + + // Fill in the options. If the provided buffer is too small, + // we'll truncate the options and return an error. Otherwise + // we'll copy the whole IP option buffer. + if (ConnInfo->OptionsLength) { + if (ConnInfo->OptionsLength < OptInfo->ioi_optlength) { + Status = TDI_BUFFER_OVERFLOW; + OptLength = ConnInfo->OptionsLength; + } else + OptLength = OptInfo->ioi_optlength; + + CTEMemCopy(ConnInfo->Options, OptInfo->ioi_options, OptLength); + + ConnInfo->OptionsLength = OptLength; + } + + // Options are copied. Build a TRANSPORT_ADDRESS structure in + // the buffer. + if (AddrLength = ConnInfo->RemoteAddressLength) { + + // Make sure we have at least enough to fill in the count and type. + if (AddrLength >= TCP_TA_SIZE) { + + // The address fits. Fill it in. + ConnInfo->RemoteAddressLength = TCP_TA_SIZE; + BuildTDIAddress(ConnInfo->RemoteAddress, SrcAddress, SrcPort); + + } else { + ConnInfo->RemoteAddressLength = 0; + Status = TDI_INVALID_PARAMETER; + } + } + + } + + return Status; + +} diff --git a/private/ntos/tdi/tcpip/tcp/tlcommon.h b/private/ntos/tdi/tcpip/tcp/tlcommon.h new file mode 100644 index 000000000..29790156c --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tlcommon.h @@ -0,0 +1,46 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TLCOMMON.H - Common transport layer definitions. +// +// This file contains definitions for common transport layer items. +// + +#define PHXSUM(s,d,p,l) (uint)( (uint)*(ushort *)&(s) + \ + (uint)*(ushort *)((char *)&(s) + sizeof(ushort)) + \ + (uint)*(ushort *)&(d) + \ + (uint)*(ushort *)((char *)&(d) + sizeof(ushort)) + \ + (uint)((ushort)net_short((p))) + \ + (uint)((ushort)net_short((ushort)(l))) ) + + +#define TCP_TA_SIZE (offsetof(TRANSPORT_ADDRESS, Address->Address)+ \ + sizeof(TDI_ADDRESS_IP)) + +extern ushort XsumSendChain(uint PHXsum, PNDIS_BUFFER BufChain); +extern ushort XsumRcvBuf(uint PHXsum, IPRcvBuf *BufChain); +extern uint CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf, + uint Size, uint RcvOffset, uint DestOffset); +extern TDI_STATUS UpdateConnInfo(PTDI_CONNECTION_INFORMATION ConnInfo, + IPOptInfo *OptInfo, IPAddr SrcAddress, ushort SrcPort); + +extern void BuildTDIAddress(uchar *Buffer, IPAddr Addr, ushort Port); + +extern void CopyRcvToBuffer(uchar *DestBuf, IPRcvBuf *SrcRB, uint Size, + uint Offset); + +extern PNDIS_BUFFER CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, + uint Size, uint *Offset, uint *BytesCopied); + +extern void *TLRegisterProtocol(uchar Protocol, void *RcvHandler, + void *XmitHandler, void *StatusHandler, + void *RcvCmpltHandler); + +#ifdef VXD +extern int TLRegisterDispatch(char *, struct TDIDispatchTable *); +#endif + + diff --git a/private/ntos/tdi/tcpip/tcp/udp.c b/private/ntos/tdi/tcpip/tcp/udp.c new file mode 100644 index 000000000..c9c284403 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/udp.c @@ -0,0 +1,673 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** UDP.C - UDP protocol code. +// +// This file contains the code for the UDP protocol functions, +// principally send and receive datagram. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" +#ifdef VXD +#include "tdivxd.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "udp.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'uPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'uPCT') + +#endif // POOL_TAGGING + +#endif // NT + +EXTERNAL_LOCK(AddrObjTableLock) + +void *UDPProtInfo = NULL; + +extern IPInfo LocalNetInfo; + +#ifdef CHICAGO +extern uchar TransportName[]; +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + + + +//** UDPSend - Send a datagram. +// +// The real send datagram routine. We assume that the busy bit is +// set on the input AddrObj, and that the address of the SendReq +// has been verified. +// +// We start by sending the input datagram, and we loop until there's +// nothing left on the send q. +// +// Input: SrcAO - Pointer to AddrObj doing the send. +// SendReq - Pointer to sendreq describing send. +// +// Returns: Nothing +// +void +UDPSend(AddrObj *SrcAO, DGSendReq *SendReq) +{ + UDPHeader *UH; + PNDIS_BUFFER UDPBuffer; + CTELockHandle HeaderHandle, AOHandle; + RouteCacheEntry *RCE; // RCE used for each send. + IPAddr SrcAddr; // Source address IP thinks we should + // use. + uchar DestType; // Type of destination address. + ushort UDPXsum; // Checksum of packet. + ushort SendSize; // Size we're sending. + IP_STATUS SendStatus; // Status of send attempt. + ushort MSS; + uint AddrValid; + IPOptInfo *OptInfo; + IPAddr OrigSrc; + + CTEStructAssert(SrcAO, ao); + CTEAssert(SrcAO->ao_usecnt != 0); + + //* Loop while we have something to send, and can get + // resources to send. + for (;;) { + + CTEStructAssert(SendReq, dsr); + + // Make sure we have a UDP header buffer for this send. If we + // don't, try to get one. + if ((UDPBuffer = SendReq->dsr_header) == NULL) { + // Don't have one, so try to get one. + CTEGetLock(&DGSendReqLock, &HeaderHandle); + UDPBuffer = GetDGHeader(); + if (UDPBuffer != NULL) + SendReq->dsr_header = UDPBuffer; + else { + // Couldn't get a header buffer. Push the send request + // back on the queue, and queue the addr object for when + // we get resources. + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q); + PutPendingQ(SrcAO); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + return; + } + CTEFreeLock(&DGSendReqLock, HeaderHandle); + } + + // At this point, we have the buffer we need. Call IP to get an + // RCE (along with the source address if we need it), then compute + // the checksum and send the data. + CTEAssert(UDPBuffer != NULL); + + if (!CLASSD_ADDR(SendReq->dsr_addr)) { + // This isn't a multicast send, so we'll use the ordinary + // information. + OrigSrc = SrcAO->ao_addr; + OptInfo = &SrcAO->ao_opt; + } else { + OrigSrc = SrcAO->ao_mcastaddr; + OptInfo = &SrcAO->ao_mcastopt; + } + + if (!(SrcAO->ao_flags & AO_DHCP_FLAG)) { + SrcAddr = (*LocalNetInfo.ipi_openrce)(SendReq->dsr_addr, + OrigSrc, &RCE, &DestType, &MSS, OptInfo); + + AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR); + } else { + // This is a DHCP send. He really wants to send from the + // NULL IP address. + SrcAddr = NULL_IP_ADDR; + RCE = NULL; + AddrValid = TRUE; + } + + if (AddrValid) { + // The OpenRCE worked. Compute the checksum, and send it. + + if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR)) + SrcAddr = OrigSrc; + + UH = (UDPHeader *)((uchar *)NdisBufferVirtualAddress(UDPBuffer) + + LocalNetInfo.ipi_hsize); + NdisBufferLength(UDPBuffer) = sizeof(UDPHeader); + NDIS_BUFFER_LINKAGE(UDPBuffer) = SendReq->dsr_buffer; + UH->uh_src = SrcAO->ao_port; + UH->uh_dest = SendReq->dsr_port; + SendSize = SendReq->dsr_size + sizeof(UDPHeader); + UH->uh_length = net_short(SendSize); + UH->uh_xsum = 0; + + if (AO_XSUM(SrcAO)) { + // Compute the header xsum, and then call XsumNdisChain + UDPXsum = XsumSendChain(PHXSUM(SrcAddr, SendReq->dsr_addr, + PROTOCOL_UDP, SendSize), UDPBuffer); + + // We need to negate the checksum, unless it's already all + // ones. In that case negating it would take it to 0, and + // then we'd have to set it back to all ones. + if (UDPXsum != 0xffff) + UDPXsum =~UDPXsum; + + UH->uh_xsum = UDPXsum; + + } + + // We've computed the xsum. Now send the packet. + UStats.us_outdatagrams++; + SendStatus = (*LocalNetInfo.ipi_xmit)(UDPProtInfo, SendReq, + UDPBuffer, (uint)SendSize, SendReq->dsr_addr, SrcAddr, + OptInfo, RCE, PROTOCOL_UDP); + + (*LocalNetInfo.ipi_closerce)(RCE); + + // If it completed immediately, give it back to the user. + // Otherwise we'll complete it when the SendComplete happens. + // Currently, we don't map the error code from this call - we + // might need to in the future. + if (SendStatus != IP_PENDING) + DGSendComplete(SendReq, UDPBuffer); + + } else { + TDI_STATUS Status; + + if (DestType == DEST_INVALID) + Status = TDI_BAD_ADDR; + else + Status = TDI_DEST_UNREACHABLE; + + // Complete the request with an error. + (*SendReq->dsr_rtn)(SendReq->dsr_context, Status, 0); + // Now free the request. + SendReq->dsr_rtn = NULL; + DGSendComplete(SendReq, UDPBuffer); + } + + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + + if (!EMPTYQ(&SrcAO->ao_sendq)) { + DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + } else { + CLEAR_AO_REQUEST(SrcAO, AO_SEND); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + return; + } + + } +} + + +//* UDPDeliver - Deliver a datagram to a user. +// +// This routine delivers a datagram to a UDP user. We're called with +// the AddrObj to deliver on, and with the AddrObjTable lock held. +// We try to find a receive on the specified AddrObj, and if we do +// we remove it and copy the data into the buffer. Otherwise we'll +// call the receive datagram event handler, if there is one. If that +// fails we'll discard the datagram. +// +// Input: RcvAO - AO to receive the datagram. +// SrcIP - Source IP address of datagram. +// SrcPort - Source port of datagram. +// RcvBuf - The IPReceive buffer containing the data. +// RcvSize - Size received, including the UDP header. +// TableHandle - Lock handle for AddrObj table. +// +// Returns: Nothing. +// +void +UDPDeliver(AddrObj *RcvAO, IPAddr SrcIP, ushort SrcPort, IPRcvBuf *RcvBuf, + uint RcvSize, IPOptInfo *OptInfo, CTELockHandle TableHandle, uchar IsBCast) +{ + Queue *CurrentQ; + CTELockHandle AOHandle; + DGRcvReq *RcvReq; + uint BytesTaken = 0; + uchar AddressBuffer[TCP_TA_SIZE]; + uint RcvdSize; + EventRcvBuffer *ERB = NULL; + + CTEStructAssert(RcvAO, ao); + + CTEGetLock(&RcvAO->ao_lock, &AOHandle); + CTEFreeLock(&AddrObjTableLock, AOHandle); + + if (AO_VALID(RcvAO)) { + + CurrentQ = QHEAD(&RcvAO->ao_rcvq); + + // Walk the list, looking for a receive buffer that matches. + while (CurrentQ != QEND(&RcvAO->ao_rcvq)) { + RcvReq = QSTRUCT(DGRcvReq, CurrentQ, drr_q); + + CTEStructAssert(RcvReq, drr); + + // If this request is a wildcard request, or matches the source IP + // address, check the port. + + if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) { + + // The local address matches, check the port. We'll match + // either 0 or the actual port. + if (RcvReq->drr_port == 0 || RcvReq->drr_port == SrcPort) { + + TDI_STATUS Status; + + // The ports matched. Remove this from the queue. + REMOVEQ(&RcvReq->drr_q); + + // We're done. We can free the AddrObj lock now. + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + // Call CopyRcvToNdis, and then complete the request. + RcvdSize = CopyRcvToNdis(RcvBuf, RcvReq->drr_buffer, + RcvReq->drr_size, sizeof(UDPHeader), 0); + + CTEAssert(RcvdSize <= RcvReq->drr_size); + + Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo, + SrcIP, SrcPort); + + UStats.us_indatagrams++; + + (*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize); + + FreeDGRcvReq(RcvReq); + + return; + } + } + + // Either the IP address or the port didn't match. Get the next + // one. + CurrentQ = QNEXT(CurrentQ); + } + + // We've walked the list, and not found a buffer. Call the recv. + // handler now. + + if (RcvAO->ao_rcvdg != NULL) { + PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg; + PVOID RcvContext = RcvAO->ao_rcvdgcontext; + TDI_STATUS RcvStatus; + CTELockHandle OldLevel; + ULONG Flags = TDI_RECEIVE_COPY_LOOKAHEAD; + + + REF_AO(RcvAO); + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + BuildTDIAddress(AddressBuffer, SrcIP, SrcPort); + + UStats.us_indatagrams++; + if (IsBCast) { + Flags |= TDI_RECEIVE_BROADCAST; + } + RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE, + (PTRANSPORT_ADDRESS)AddressBuffer, OptInfo->ioi_optlength, + OptInfo->ioi_options, Flags, + RcvBuf->ipr_size - sizeof(UDPHeader), + RcvSize - sizeof(UDPHeader), &BytesTaken, + RcvBuf->ipr_buffer + sizeof(UDPHeader), &ERB); + + if (RcvStatus == TDI_MORE_PROCESSING) { + CTEAssert(ERB != NULL); + + // We were passed back a receive buffer. Copy the data in now. + + // He can't have taken more than was in the indicated + // buffer, but in debug builds we'll check to make sure. + + CTEAssert(BytesTaken <= (RcvBuf->ipr_size - sizeof(UDPHeader))); + +#ifdef VXD + RcvdSize = CopyRcvToNdis(RcvBuf, ERB->erb_buffer, + ERB->erb_size, sizeof(UDPHeader) + BytesTaken, 0); + + // + // Call the completion routine. + // + (*ERB->erb_rtn)(ERB->erb_context, TDI_SUCCESS, RcvdSize); + +#endif // VXD + +#ifdef NT + { + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation; + + IrpSp = IoGetCurrentIrpStackLocation(ERB); + DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) + &(IrpSp->Parameters); + + // + // Copy the remaining data to the IRP. + // + RcvdSize = CopyRcvToNdis(RcvBuf, ERB->MdlAddress, + RcvSize - sizeof(UDPHeader) - BytesTaken, + sizeof(UDPHeader) + BytesTaken, 0); + + // + // Update the return address info + // + RcvStatus = UpdateConnInfo( + DatagramInformation->ReturnDatagramInformation, + OptInfo, SrcIP, SrcPort); + + // + // Complete the IRP. + // + ERB->IoStatus.Information = RcvdSize; + ERB->IoStatus.Status = RcvStatus; + IoCompleteRequest(ERB, 2); + } +#endif // NT + + } + else { + CTEAssert( + (RcvStatus == TDI_SUCCESS) || + (RcvStatus == TDI_NOT_ACCEPTED) + ); + + CTEAssert(ERB == NULL); + } + + DELAY_DEREF_AO(RcvAO); + + return; + + } else + UStats.us_inerrors++; + + // When we get here, we didn't have a buffer to put this data into. + // Fall through to the return case. + } else + UStats.us_inerrors++; + + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + +} + + +//* UDPRcv - Receive a UDP datagram. +// +// The routine called by IP when a UDP datagram arrived. We +// look up the port/local address pair in our address table, +// and deliver the data to a user if we find one. For broadcast +// frames we may deliver it to multiple users. +// +// Entry: IPContext - IPContext identifying physical i/f that +// received the data. +// Dest - IPAddr of destionation. +// Src - IPAddr of source. +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the packet +// IPH - IP Header. +// IPHLength - Bytes in IPH. +// RcvBuf - Pointer to receive buffer chain containing data. +// Size - Size in bytes of data received. +// IsBCast - Boolean indicator of whether or not this came in as +// a bcast. +// Protocol - Protocol this came in on - should be UDP. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception. Anything other than IP_SUCCESS will cause +// IP to send a 'port unreachable' message. +// +IP_STATUS +UDPRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf, + uint IPSize, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo) +{ + UDPHeader UNALIGNED *UH; + CTELockHandle AOTableHandle; + AddrObj *ReceiveingAO; + uint Size; + uchar DType; + + DType = (*LocalNetInfo.ipi_getaddrtype)(Src); + + // The following code relies on DEST_INVALID being a broadcast dest type. + // If this is changed the code here needs to change also. + if (IS_BCAST_DEST(DType)) { + if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) { + UStats.us_inerrors++; + return IP_SUCCESS; // Bad src address. + } + } + + UH = (UDPHeader *)RcvBuf->ipr_buffer; + + Size = (uint)(net_short(UH->uh_length)); + + if (Size < sizeof(UDPHeader)) { + UStats.us_inerrors++; + return IP_SUCCESS; // Size is too small. + } + + if (Size != IPSize) { + // Size doesn't match IP datagram size. If the size is larger + // than the datagram, throw it away. If it's smaller, truncate the + // recv. buffer. + if (Size < IPSize) { + IPRcvBuf *TempBuf = RcvBuf; + uint TempSize = Size; + + while (TempBuf != NULL) { + TempBuf->ipr_size = MIN(TempBuf->ipr_size, TempSize); + TempSize -= TempBuf->ipr_size; + TempBuf = TempBuf->ipr_next; + } + } else { + // Size is too big, toss it. + UStats.us_inerrors++; + return IP_SUCCESS; + } + } + + + if (UH->uh_xsum != 0) { + if (XsumRcvBuf(PHXSUM(Src, Dest, PROTOCOL_UDP, Size), RcvBuf) != 0xffff) { + UStats.us_inerrors++; + return IP_SUCCESS; // Checksum failed. + } + } + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + +#ifdef SECFLTR + // + // See if we are filtering the destination interface/port. + // + if ( !SecurityFilteringEnabled || + IsPermittedSecurityFilter( + SrcAddr, + IPContext, + PROTOCOL_UDP, + (ulong) net_short(UH->uh_dest) + ) + ) + { +#endif // SECFLTR + + // Try to find an AddrObj to give this to. In the broadcast case, we + // may have to do this multiple times. If it isn't a broadcast, just + // get the best match and deliver it to them. + + if (!IsBCast) { + ReceiveingAO = GetBestAddrObj(Dest, UH->uh_dest, PROTOCOL_UDP); + if (ReceiveingAO != NULL) { + UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size, + OptInfo, AOTableHandle, IsBCast); + return IP_SUCCESS; + } else { + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + UStats.us_noports++; + return IP_GENERAL_FAILURE; + } + } else { + // This is a broadcast, we'll need to loop. + + AOSearchContext Search; + + DType = (*LocalNetInfo.ipi_getaddrtype)(Dest); + + ReceiveingAO = GetFirstAddrObj(LocalAddr, UH->uh_dest, PROTOCOL_UDP, + &Search); + if (ReceiveingAO != NULL) { + do { + if ((DType != DEST_MCAST) || + ((DType == DEST_MCAST) && + MCastAddrOnAO(ReceiveingAO, Dest))) { + UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size, + OptInfo, AOTableHandle, IsBCast); + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + } + ReceiveingAO = GetNextAddrObj(&Search); + } while (ReceiveingAO != NULL); + } else + UStats.us_noports++; + } + +#ifdef SECFLTR + } +#endif // SECFLTR + + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + return IP_SUCCESS; +} + +//* UDPStatus - Handle a status indication. +// +// This is the UDP status handler, called by IP when a status event +// occurs. For most of these we do nothing. For certain severe status +// events we will mark the local address as invalid. +// +// Entry: StatusType - Type of status (NET or HW). NET status +// is usually caused by a received ICMP +// message. HW status indicate a HW +// problem. +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is NET status, the original dest. of +// DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local +// or remote). +// Param - Additional information for status - i.e. the +// param field of an ICMP message. +// Data - Data pertaining to status - for NET status, this +// is the first 8 bytes of the original DG. +// +// Returns: Nothing +// +void +UDPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) +{ + // If this is a HW status, it could be because we've had an address go + // away. + if (StatusType == IP_HW_STATUS) { + + if (StatusCode == IP_ADDR_DELETED) { + // + // An address has gone away. OrigDest identifies the address. + // +#ifndef _PNP_POWER + // + // This is done via TDI notifications in the PNP world. + // + InvalidateAddrs(OrigDest); + +#endif // _PNP_POWER + +#ifdef SECFLTR + // + // Delete any security filters associated with this address + // + DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_UDP); + +#endif // SECFLTR + + return; + } + + if (StatusCode == IP_ADDR_ADDED) { + +#ifdef SECFLTR + // + // An address has materialized. OrigDest identifies the address. + // Data is a handle to the IP configuration information for the + // interface on which the address is instantiated. + // + AddProtocolSecurityFilter(OrigDest, PROTOCOL_UDP, + (NDIS_HANDLE) Data); +#endif // SECFLTR + + return; + } + +#ifdef CHICAGO + if (StatusCode == IP_UNLOAD) { + // IP is telling us we're being unloaded. First, deregister + // with VTDI, and then call CTEUnload(). + (void)TLRegisterProtocol(PROTOCOL_UDP, NULL, NULL, NULL, NULL); + +#ifdef UDP_ONLY + // Only do the following in the UDP_ONLY version. TCP does it in + // the generic version. + TLRegisterDispatch(TransportName, NULL); + (void)RegisterAddrChangeHndlr(AddrChange, FALSE); + CTEUnload(TransportName); +#endif // UDP_ONLY + + return; + } +#endif // CHICAGO + } +} + diff --git a/private/ntos/tdi/tcpip/tcp/udp.h b/private/ntos/tdi/tcpip/tcp/udp.h new file mode 100644 index 000000000..4ba5ec5e0 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/udp.h @@ -0,0 +1,39 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** UDP. - UDP protocol definitions. +// +// This file contains definitions for the UDP protocol functions. +// + +#include "dgram.h" + +#define PROTOCOL_UDP 17 // UDP protocol number + +//* Structure of a UDP header. +struct UDPHeader { + ushort uh_src; // Source port. + ushort uh_dest; // Destination port. + ushort uh_length; // Length + ushort uh_xsum; // Checksum. +}; /* UDPHeader */ + +typedef struct UDPHeader UDPHeader; + + +//* External definition of exported functions. +extern IP_STATUS UDPRcv(void *IPContext, IPAddr Dest, IPAddr Src, + IPAddr LocalAddr, IPAddr SrcAddr, + IPHeader UNALIGNED *IPH, uint IPHLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, + uchar Protocol, IPOptInfo *OptInfo); + +extern void UDPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data); + +extern void UDPSend(AddrObj *SrcAO, DGSendReq *SendReq); + + diff --git a/private/ntos/tdi/tcpip/tcp/up/makefile b/private/ntos/tdi/tcpip/tcp/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/tcp/up/sources b/private/ntos/tdi/tcpip/tcp/up/sources new file mode 100644 index 000000000..75e90b2f7 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/up/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +LINKLIBS=..\..\ip\up\obj\*\ip.lib +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/tcpip/tcp/up/tcpip.prf b/private/ntos/tdi/tcpip/tcp/up/tcpip.prf new file mode 100644 index 000000000..4f03cef97 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/up/tcpip.prf @@ -0,0 +1,297 @@ +TdiQueryInformation@20 +TCPDispatchInternalDeviceControl@8 +DerefTCB@8 +FreeARPBuffer@8 +ProcessTCBDelayQ@0 +ARPTransmit@16 +TCPRcv@48 +IPRcv@28 +IPRcvComplete@0 +GetARPBuffer@12 +FreeIPPacket@4 +TCBTimeout@8 +ARPTimeout@8 +ICMPTimer@4 +IPTimeout@8 +IGMPTimer@4 +IndicateData@16 +TCPSendComplete@8 +GetSendReq@0 +FreeSendReq@4 +FillTCPHeader@8 +RcvWin@4 +IPTransmit@36 +GetRcvReq@0 +GetTCPHeader@0 +FindTCB@16 +TCPSendData@8 +TdiSend@16 +TCPSend@8 +ACKData@8 +FreeRBChain@4 +FreeRcvReq@4 +TCPDataRequestComplete@12 +FindUserRcv@4 +TCPRcvComplete@0 +GetLocalNTE@8 +GetConnFromConnID@4 +ARPSendData@16 +XsumRcvBuf@8 +FreeTCPHeader@4 +ARPRcv@28 +XsumSendChain@8 +CTEStartTimer@16 +DeliverToUser@32 +GetIPPacket@0 +ARPRcvComplete@4 +CTESystemUpTime@0 +ARPSendComplete@12 +IPSendComplete@12 +DelayAction@8 +TCPPrepareIrpForCancel@12 +BufferData@16 +SendACK@4 +CompleteRcvs@4 +FreePartialRB@8 +GetAddrObj@16 +AddrOnIF@8 +FindRTE@20 +TdiCopyBufferToMdl@24 +TCPGetMdlChainByteCount@4 +IPHash@4 +LookupRTE@12 +OpenRCE@24 +TCPQueryInformation@8 +CopyFlatToNdis@20 +FindListenConn@16 +DummyDone@8 +GetAddrType@4 +InitTCBFromConn@16 +tcpxsum@12 +FindMSS@4 +DelayDerefAO@4 +FindRCE@12 +TryToCloseTCB@12 +InitRCE@4 +NotifyOfDisc@12 +UpdateConnInfo@16 +GoToEstab@4 +TCPDisconnect@8 +FreeConnReq@4 +TdiDisconnect@20 +IPGetLocalMTU@8 +CloseRCE@4 +IPInitOptions@4 +AdjustRcvWin@4 +BuildTDIAddress@12 +SendSYN@8 +AllocTCB@0 +ProcessUserOptions@8 +FreeTCB@4 +RemoveTCBFromConn@4 +InitSendState@4 +CompleteConnReq@12 +IPFreeOptions@4 +TCPRequestComplete@12 +InvalidSourceAddress@4 +AcceptConn@8 +RemoveTCB@4 +GetConnReq@0 +CloseTCB@8 +RemoveConnFromTCB@4 +FinishRemoveTCBFromConn@4 +IsBCastOnNTE@8 +InsertTCB@4 +ResetSendNext@8 +ARPLookup@12 +BCastRcv@40 +IPGetAddrType@4 +UDPRcv@48 +GetFirstAddrObj@16 +IPForward@32 +GetNextAddrObj@4 +UDPDeliver@32 +LockedDerefIF@4 +BestNTEForIF@8 +ARPInvalidate@8 +IsBCastOnIF@8 +ARPSendBCast@16 +UDPSendDatagram@8 +DGSendComplete@8 +TdiSendDatagram@20 +DerefAO@4 +FreeDGSendReq@4 +GetDGSendReq@0 +FreeDGHeader@4 +UDPSend@8 +GetAddress@12 +ARPRemoveRCE@8 +HandleARPPacket@24 +IsLocalAddr@8 +ARPLocalAddr@8 +IPRouteTimeout@8 +GetConnID@4 +TdiAssociateAddress@8 +FindEA@12 +TCPCreate@12 +TCPAssociateAddress@8 +TdiOpenConnection@8 +TCPDispatch@8 +TdiMapUserRequest@12 +IPGetPInfo@16 +SendARPPacket@40 +RemoveARPTableEntry@8 +CreateARPTableEntry@12 +SendARPRequest@20 +GrowARPHeaders@4 +GrowTCPHeaderList@0 +TdiCloseConnection@4 +RemoveConnFromAO@8 +TCPCloseObjectComplete@12 +TCPClose@8 +CloseDone@8 +TCPCleanup@12 +FreeConnID@4 +DummyCmplt@12 +LoopAddAddr@16 +FindIGMPAddr@12 +CTEInitEvent@8 +TdiSetEvent@16 +ARPSetMCastList@4 +IPGetInfo@8 +IGMPAddrChange@12 +ICMPInit@4 +IGMPInit@0 +ARPRequestComplete@12 +FreeAORequest@4 +TCPQueryInformationEx@8 +ARPFindMCast@12 +InitAdapter@4 +CTESignal@8 +CTEBlock@4 +CTEInitTimer@4 +InsertAddrObj@4 +TdiQueryInformationEx@20 +ARPAddAddr@16 +TdiOpenAddress@16 +InitNTERouting@12 +ARPAddMCast@8 +InitIGMPForNTE@4 +FindAnyAddrObj@8 +GrowDGHeaderList@0 +FindSpecificRTE@20 +InitNTE@4 +CopyToNdis@16 +IPRegisterProtocol@20 +GetAddrOptions@12 +InitInterface@8 +TdiSetInformationEx@16 +TCPConnect@8 +TCPQueryInformationExComplete@12 +TCPSetInformationEx@8 +LockedAddRoute@36 +TCPAcdBind@0 +AddRoute@36 +TdiConnect@16 +LoopXmit@16 +AddValueSecurityFilter@12 +InitGateway@4 +ARPOAComplete@12 +CTEScheduleEvent@8 +GetHashMask@8 +InitLoopback@4 +RawStatus@28 +FindInterfaceEntry@4 +FindProtocolEntry@8 +AddProtocolSecurityFilter@12 +TdiRegisterNetAddress@8 +UDPStatus@28 +ARPDynRegister@36 +FindInsertPoint@4 +IPAddInterface@20 +NotifyAddrChange@28 +TCPStatus@28 +TdiInitialize@0 +TdiRegisterDeviceObject@8 +TdiRegisterAddressChangeHandler@12 +CTEInitialize@0 +ARPBindAdapter@20 +ICMPStatus@28 +IPQueryInfo@16 +DeleteProtocolValueEntries@4 +ModifyProtocolEntry@12 +ModifyInterfaceEntry@16 +ModifySecurityFilter@16 +DoNDISRequest@24 +LoopGetEList@12 +TCPDisassociateAddress@8 +TdiDisAssociateAddress@4 +EnumRegMultiSz@12 +AddressArrival@4 +ARPGetEList@12 +GrowIPPacketList@0 +IPGetEList@8 +RTValidateContext@8 +RTReadNext@8 +GetCurrentRouteTable@4 +AddNTERoutes@4 +GetSecurityFilterList@12 +LoopXmitRtn@8 +InitDG@4 +IPGetConfig@0 +InitTimestamp@0 +IPProcessAdapterSection@8 +TCPInitializeParameter@12 +TLGetIPInfo@8 +InitializeSecurityFilters@0 +IPFreeConfig@4 +InitTCPRcv@0 +InitAddr@0 +IPInit@0 +GetTime@0 +ARPInit@0 +InitTCPConn@0 +IPProcessConfiguration@0 +IPDriverEntry@8 +tlinit@0 +InitTCPSend@0 +InitTCB@0 +GetIFAddrList@8 +ARPOpen@4 +TCPDispatchDeviceControl@8 +TCPSetEventHandler@8 +ARPRegister@12 +GetRegStringValue@16 +IsDHCPZeroAddress@4 +EnumSecurityFilterValue@12 +CloseIFConfig@4 +UseEtherSNAP@4 +OpenIFConfig@8 +IPConvertStringToAddress@8 +SetRegDWORDValue@12 +GetArpCacheLife@0 +InitRegDWORDParameter@16 +GetRegMultiSZValue@12 +OpenRegKey@8 +GetGeneralIFConfig@8 +GetAlwaysSourceRoute@0 +GetRegSZValue@16 +GetRegDWORDValue@12 +IsLLInterfaceValueNull@4 +DerefIF@4 +ARPQueryInfo@20 +DriverEntry@8 +TCPGetConfigInfo@0 +SendIPPacket@28 +GetIPHdrBuffer@0 +GrowHdrBufList@0 +FreeIPHdrBuffer@4 +InitRouting@4 +TLRegisterProtocol@20 +LookupNextHopWithBuffer@28 +SetPersistentRoutesForNTE@12 +SendARPReply@28 +GetGMTDelta@0 +SendRSTFromHeader@20 +InvalidateRCEChain@4 +LoopQInfo@20 diff --git a/private/ntos/tdi/wrapper/cxport.c b/private/ntos/tdi/wrapper/cxport.c new file mode 100644 index 000000000..db444df32 --- /dev/null +++ b/private/ntos/tdi/wrapper/cxport.c @@ -0,0 +1,723 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + cxport.c + +Abstract: + + Common Transport Environment utility functions for the NT environment + +Author: + + Mike Massa (mikemas) Aug 11, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-11-93 created + +Notes: + +--*/ + +#include +#include +#include +#include + + +// +// Mark pageable code +// +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, CTELogEvent) + +#endif // ALLOC_PRAGMA + + +// +// Local variables +// +ULONG CTEpTimeIncrement = 0; // used to convert kernel clock ticks to 100ns. + + // Used in the conversion of 100ns times to milliseconds. +static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; + + +// +// Macros +// +//++ +// +// LARGE_INTEGER +// CTEConvertMillisecondsTo100ns( +// IN LARGE_INTEGER MsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// MsTime - Time in milliseconds. +// +// Return Value: +// +// Time in hundreds of nanoseconds. +// +//-- + +#define CTEConvertMillisecondsTo100ns(MsTime) \ + RtlExtendedIntegerMultiply(MsTime, 10000) + + +//++ +// +// LARGE_INTEGER +// CTEConvert100nsToMilliseconds( +// IN LARGE_INTEGER HnsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// HnsTime - Time in hundreds of nanoseconds. +// +// Return Value: +// +// Time in milliseconds. +// +//-- + +#define SHIFT10000 13 +extern LARGE_INTEGER Magic10000; + +#define CTEConvert100nsToMilliseconds(HnsTime) \ + RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) + + +// +// Local functions +// +VOID +CTEpEventHandler( + IN PVOID Context + ) +/*++ + +Routine Description: + + Internal handler for scheduled CTE Events. Conforms to calling convention + for ExWorkerThread handlers. Calls the CTE handler registered for this + event. + +Arguments: + + Context - Work item to process. + +Return Value: + + None. + +--*/ + +{ + CTEEvent *Event; + CTELockHandle Handle; + +#if DBG + KIRQL StartingIrql; + + StartingIrql = KeGetCurrentIrql(); +#endif + + Event = (CTEEvent *) Context; + + CTEGetLock(&(Event->ce_lock), &Handle); + ASSERT(Event->ce_scheduled); + Event->ce_scheduled = 0; + CTEFreeLock(&(Event->ce_lock), Handle); + + (*Event->ce_handler)(Event, Event->ce_arg); + +#if DBG + if (KeGetCurrentIrql() != StartingIrql) { + DbgPrint( + "CTEpEventHandler: routine %lx , event %lx returned at raised IRQL\n", + Event->ce_handler, Event + ); + DbgBreakPoint(); + } +#endif +} + + +VOID +CTEpTimerHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + Internal handler for scheduled CTE Timers. Conforms to calling convention + for NT DPC handlers. Calls the CTE handler registered for this timer. + +Arguments: + + Dpc - Pointer to the DPC routine being run. + DeferredContext - Private context for this instance of the DPC. + SystemArgument1 - Additional argument. + SystemArgument2 - Additional argument. + +Return Value: + + None. + +--*/ + +{ + CTETimer *Timer; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + Timer = (CTETimer *) DeferredContext; + (*Timer->t_handler)((CTEEvent *)Timer, Timer->t_arg); +} + + +// +// Exported functions. +// +int +CTEInitialize( + VOID + ) + +/*++ + +Routine Description: + + Initializes the Common Tranport Environment (CTE). + +Arguments: + + None. + +Return Value: + + 0 if initialization fails. Nonzero otherwise. + +--*/ + +{ + CTEpTimeIncrement = KeQueryTimeIncrement(); + + return(1); +} + + +void +CTEInitEvent( + CTEEvent *Event, + CTEEventRtn Handler + ) +/*++ + +Routine Description: + + Initializes a CTE Event variable. + +Arguments: + + Event - Event variable to initialize. + Handler - Handler routine for this event variable. + +Return Value: + + None. + +--*/ + +{ + ASSERT(Handler != NULL); + + Event->ce_handler = Handler; + Event->ce_scheduled = 0; + CTEInitLock(&(Event->ce_lock)); + ExInitializeWorkItem(&(Event->ce_workitem), CTEpEventHandler, Event); +} + + +int +CTEScheduleEvent( + IN CTEEvent *Event, + IN void *Argument OPTIONAL + ) + +/*++ + +Routine Description: + + Schedules a routine to be executed later in a different context. In the + NT environment, the event is implemented using Executive worker threads. + +Arguments: + + Event - Pointer to a CTE Event variable + Argument - An argument to pass to the event handler when it is called + +Return Value: + + 0 if the event could not be scheduled. Nonzero otherwise. + +--*/ + +{ + CTELockHandle Handle; + + CTEGetLock(&(Event->ce_lock), &Handle); + + if (!(Event->ce_scheduled)) { + Event->ce_scheduled = 1; + Event->ce_arg = Argument; + + ExQueueWorkItem( + &(Event->ce_workitem), + CriticalWorkQueue + ); + } + + CTEFreeLock(&(Event->ce_lock), Handle); + + return(1); +} + + +void +CTEInitTimer( + CTETimer *Timer + ) +/*++ + +Routine Description: + + Initializes a CTE Timer variable. + +Arguments: + + Timer - Timer variable to initialize. + +Return Value: + + None. + +--*/ + +{ + Timer->t_handler = NULL; + Timer->t_arg = NULL; + KeInitializeDpc(&(Timer->t_dpc), CTEpTimerHandler, Timer); + KeInitializeTimer(&(Timer->t_timer)); +} + + +void * +CTEStartTimer( + CTETimer *Timer, + unsigned long DueTime, + CTEEventRtn Handler, + void *Context + ) + +/*++ + +Routine Description: + + Sets a CTE Timer for expiration. + +Arguments: + + Timer - Pointer to a CTE Timer variable. + DueTime - Time in milliseconds after which the timer should expire. + Handler - Timer expiration handler routine. + Context - Argument to pass to the handler. + +Return Value: + + 0 if the timer could not be set. Nonzero otherwise. + +--*/ + +{ + LARGE_INTEGER LargeDueTime; + + ASSERT(Handler != NULL); + + // + // Convert milliseconds to hundreds of nanoseconds and negate to make + // an NT relative timeout. + // + LargeDueTime.HighPart = 0; + LargeDueTime.LowPart = DueTime; + LargeDueTime = CTEConvertMillisecondsTo100ns(LargeDueTime); + LargeDueTime.QuadPart = -LargeDueTime.QuadPart; + + Timer->t_handler = Handler; + Timer->t_arg = Context; + + KeSetTimer( + &(Timer->t_timer), + LargeDueTime, + &(Timer->t_dpc) + ); + + return((void *) 1); +} + + +unsigned long +CTESystemUpTime( + void + ) + +/*++ + +Routine Description: + + Provides the time since system boot in milliseconds. + +Arguments: + + None. + +Return Value: + + The time since boot in milliseconds. + +--*/ + +{ + LARGE_INTEGER TickCount; + + // + // Get tick count and convert to hundreds of nanoseconds. + // + KeQueryTickCount(&TickCount); + + TickCount = RtlExtendedIntegerMultiply( + TickCount, + (LONG) CTEpTimeIncrement + ); + + TickCount = CTEConvert100nsToMilliseconds(TickCount); + + ASSERT(TickCount.HighPart == 0); + + return(TickCount.LowPart); +} + + +extern uint +CTEBlock( + IN CTEBlockStruc *BlockEvent + ) +{ + NTSTATUS Status; + + Status = KeWaitForSingleObject( + &(BlockEvent->cbs_event), + UserRequest, + KernelMode, + FALSE, + NULL + ); + + if (!NT_SUCCESS(Status)) { + // + // BUGBUG: Do something better here. + // + BlockEvent->cbs_status = Status; + } + + return(BlockEvent->cbs_status); +} + + +extern void +CTESignal( + IN CTEBlockStruc *BlockEvent, + IN uint Status + ) +{ + BlockEvent->cbs_status = Status; + KeSetEvent(&(BlockEvent->cbs_event), 0, FALSE); + return; +} + + +BOOLEAN +CTEInitString( + IN OUT PNDIS_STRING DestinationString, + IN char *SourceString + ) + +/*++ + +Routine Description: + + Converts a C style ASCII string to an NDIS_STRING. Resources needed for + the NDIS_STRING are allocated and must be freed by a call to + CTEFreeString. + +Arguments: + + DestinationString - A pointer to an NDIS_STRING variable with no + associated data buffer. + + SourceString - The C style ASCII string source. + + +Return Value: + + TRUE if the initialization succeeded. FALSE otherwise. + +--*/ + +{ + STRING AnsiString; + ULONG UnicodeLength; + + RtlInitString(&AnsiString, SourceString); + + // calculate size of unicoded ansi string + 2 for NULL terminator + UnicodeLength = RtlAnsiStringToUnicodeSize(&AnsiString) + 2; + DestinationString->MaximumLength = (USHORT) UnicodeLength; + + // allocate storage for the unicode string + DestinationString->Buffer = ExAllocatePool(NonPagedPool, UnicodeLength); + + if (DestinationString->Buffer == NULL) { + return(FALSE); + } + + // Finally, convert the string to unicode + RtlAnsiStringToUnicodeString(DestinationString, &AnsiString, FALSE); + + return(TRUE); +} + + +BOOLEAN +CTEAllocateString( + PNDIS_STRING String, + unsigned short MaximumLength + ) + +/*++ + +Routine Description: + + Allocates a data buffer for Length characters in an uninitialized + NDIS_STRING. The allocated space must be freed by a call to CTEFreeString. + + +Arguments: + + String - A pointer to an NDIS_STRING variable with no + associated data buffer. + + Length - The maximum length of the string. In Unicode, this is a + byte count. + +Return Value: + + TRUE if the initialization succeeded. FALSE otherwise. + +--*/ + +{ + String->Buffer = ExAllocatePool( + NonPagedPool, + MaximumLength + sizeof(UNICODE_NULL) + ); + + if (String->Buffer == NULL) { + return(FALSE); + } + + String->Length = 0; + String->MaximumLength = MaximumLength + sizeof(UNICODE_NULL); + + return(TRUE); +} + + + +LONG +CTELogEvent( + IN PVOID LoggerId, + IN ULONG EventCode, + IN ULONG UniqueEventValue, + IN USHORT NumStrings, + IN PVOID StringsList, OPTIONAL + IN ULONG DataSize, + IN PVOID Data OPTIONAL + ) + +/*++ + +Routine Description: + + This function allocates an I/O error log record, fills it in and writes it + to the I/O error log. + + +Arguments: + + LoggerId - Pointer to the driver object logging this event. + + EventCode - Identifies the error message. + + UniqueEventValue - Identifies this instance of a given error message. + + NumStrings - Number of unicode strings in strings list. + + DataSize - Number of bytes of data. + + Strings - Array of pointers to unicode strings (PWCHAR). + + Data - Binary dump data for this message, each piece being + aligned on word boundaries. + +Return Value: + + TDI_SUCCESS - The error was successfully logged. + TDI_BUFFER_TOO_SMALL - The error data was too large to be logged. + TDI_NO_RESOURCES - Unable to allocate memory. + +Notes: + + This code is paged and may not be called at raised IRQL. + +--*/ +{ + PIO_ERROR_LOG_PACKET ErrorLogEntry; + ULONG PaddedDataSize; + ULONG PacketSize; + ULONG TotalStringsSize = 0; + USHORT i; + PWCHAR *Strings; + PWCHAR Tmp; + + + PAGED_CODE(); + + Strings = (PWCHAR *) StringsList; + + // + // Sum up the length of the strings + // + for (i=0; i CTE_MAX_EVENT_LOG_DATA_SIZE) { + return(TDI_BUFFER_TOO_SMALL); // Too much error data + } + + // + // Now add in the size of the log packet, but subtract 4 from the data + // since the packet struct contains a ULONG for data. + // + if (PacketSize > sizeof(ULONG)) { + PacketSize += sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + } + else { + PacketSize += sizeof(IO_ERROR_LOG_PACKET); + } + + ASSERT(PacketSize <= ERROR_LOG_MAXIMUM_SIZE); + + ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( + (PDRIVER_OBJECT) LoggerId, + (UCHAR) PacketSize + ); + + if (ErrorLogEntry == NULL) { + return(TDI_NO_RESOURCES); + } + + // + // Fill in the necessary log packet fields. + // + ErrorLogEntry->UniqueErrorValue = UniqueEventValue; + ErrorLogEntry->ErrorCode = EventCode; + ErrorLogEntry->NumberOfStrings = NumStrings; + ErrorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + + PaddedDataSize - sizeof(ULONG); + ErrorLogEntry->DumpDataSize = (USHORT) PaddedDataSize; + + // + // Copy the Dump Data to the packet + // + if (DataSize > 0) { + RtlMoveMemory( + (PVOID) ErrorLogEntry->DumpData, + Data, + DataSize + ); + } + + // + // Copy the strings to the packet. + // + Tmp = (PWCHAR) ((char *) ErrorLogEntry + + ErrorLogEntry->StringOffset + + PaddedDataSize); + + for (i=0; i +#include +#include +#include +#include "tdipnp.h" + +#if DBG + +#include "tdidebug.h" + +ULONG TdiDebug; + +#define IF_TDIDBG(sts) \ + if ((TdiDebug & sts) != 0) + +#define TDI_DEBUG_NAMES 0x00000001 +#define TDI_DEBUG_DISPATCH 0x00000002 +#define TDI_DEBUG_MAP 0x00000004 + +#else + +#define IF_TDIDBG(sts) \ + if (0) +#endif + +ULONG TdiInitializationCount; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + Temporary entry point needed to initialize the TDI wrapper driver. + +Arguments: + + DriverObject - Pointer to the driver object created by the system. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + // + // BUGBUG This should not be needed! + // + + UNREFERENCED_PARAMETER(DriverObject); + + return STATUS_SUCCESS; + +} // DriverEntry + + +NTSTATUS +TdiMapUserRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine maps a user request from the NtDeviceIoControlFile format + to the kernel mode request format. It does this by probing and locking all + buffers of interest, copying parameter sets to the stack pointer as + appropriate, and generally preparing for the kernel IO form. + +Arguments: + + Irp - pointer to the irp containing this request. + +Return Value: + + NTSTATUS - status of operation. STATUS_UNSUCCESSFUL if the request could + not be mapped, STATUS_NOT_IMPLEMENTED if the IOCTL is not recognized + (allowing driver writers to extend the supported IOCTLs if needed), and + STATUS_SUCCESS if the request was mapped successfully. + +--*/ + +{ + + NTSTATUS Status; + + DeviceObject; + + Status = STATUS_INVALID_PARAMETER; + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TDI_ACCEPT: + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_ACCEPT; + + Status = STATUS_SUCCESS; + break; + + case IOCTL_TDI_ACTION: + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_ACTION; + + Status = STATUS_SUCCESS; + break; + + case IOCTL_TDI_CONNECT: + { + PTDI_REQUEST_CONNECT userRequest; + PTDI_REQUEST_KERNEL_CONNECT request; + PTDI_CONNECTION_INFORMATION connInfo; + PCHAR ptr; + + if (Irp->AssociatedIrp.SystemBuffer) { + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_CONNECT; + + userRequest = + (PTDI_REQUEST_CONNECT)Irp->AssociatedIrp.SystemBuffer; + connInfo = userRequest->RequestConnectionInformation; + ptr = (PCHAR)(connInfo + 1); + connInfo->UserData = ptr; + ptr += connInfo->UserDataLength; + connInfo->Options = ptr; + ptr += connInfo->OptionsLength; + connInfo->RemoteAddress = ptr; + + request = (PTDI_REQUEST_KERNEL_CONNECT)&IrpSp->Parameters; + request->RequestConnectionInformation = connInfo; + + // + // BUGBUG: Fill this in too? + // + + request->ReturnConnectionInformation = NULL; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_DISCONNECT: + { + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_DISCONNECT; + + Status = STATUS_SUCCESS; + break; + } + + case IOCTL_TDI_LISTEN: + { + PTDI_REQUEST_LISTEN userRequest; + PTDI_REQUEST_KERNEL_LISTEN request; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_LISTEN)Irp->AssociatedIrp.SystemBuffer; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_LISTEN; + + request = (PTDI_REQUEST_KERNEL_LISTEN)&IrpSp->Parameters; + request->RequestFlags = userRequest->ListenFlags; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_QUERY_INFORMATION: + { + PTDI_REQUEST_QUERY_INFORMATION userRequest; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION request; + PTDI_CONNECTION_INFORMATION connInfo; + PCHAR ptr; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_QUERY_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_QUERY_INFORMATION; + + request = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters; + request->QueryType = userRequest->QueryType; + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength > + sizeof (TDI_REQUEST_QUERY_INFORMATION)) + { + connInfo = (PTDI_CONNECTION_INFORMATION)(userRequest + 1); + ptr = (PCHAR)(connInfo + 1); + connInfo->UserData = ptr; + ptr += connInfo->UserDataLength; + connInfo->Options = ptr; + ptr += connInfo->OptionsLength; + connInfo->RemoteAddress = ptr; + request->RequestConnectionInformation = connInfo; + } + else + request->RequestConnectionInformation = NULL; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_RECEIVE: + { + PTDI_REQUEST_RECEIVE userRequest; + PTDI_REQUEST_KERNEL_RECEIVE request; + ULONG receiveLength; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_RECEIVE)Irp->AssociatedIrp.SystemBuffer; + receiveLength = + IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_RECEIVE; + + request = (PTDI_REQUEST_KERNEL_RECEIVE)&IrpSp->Parameters; + request->ReceiveLength = receiveLength; + request->ReceiveFlags = userRequest->ReceiveFlags; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_RECEIVE_DATAGRAM: + { + PTDI_REQUEST_RECEIVE_DATAGRAM userRequest; + PTDI_REQUEST_KERNEL_RECEIVEDG request; + ULONG receiveLength; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_RECEIVE_DATAGRAM)Irp->AssociatedIrp.SystemBuffer; + receiveLength = + IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_RECEIVE_DATAGRAM; + + request = (PTDI_REQUEST_KERNEL_RECEIVEDG)&IrpSp->Parameters; + request->ReceiveLength = receiveLength; + request->ReceiveFlags = userRequest->ReceiveFlags; + request->ReceiveDatagramInformation = userRequest->ReceiveDatagramInformation; + request->ReturnDatagramInformation = userRequest->ReturnInformation; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_SEND: + { + PTDI_REQUEST_SEND userRequest; + PTDI_REQUEST_KERNEL_SEND request; + ULONG sendLength; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_SEND)Irp->AssociatedIrp.SystemBuffer; + sendLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_SEND; + + request = (PTDI_REQUEST_KERNEL_SEND)&IrpSp->Parameters; + request->SendLength = sendLength; + request->SendFlags = userRequest->SendFlags; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_SEND_DATAGRAM: + { + PTDI_REQUEST_SEND_DATAGRAM userRequest; + PTDI_REQUEST_KERNEL_SENDDG request; + ULONG sendLength; + + if (Irp->AssociatedIrp.SystemBuffer) { + + sendLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_SEND_DATAGRAM; + + request = (PTDI_REQUEST_KERNEL_SENDDG)&IrpSp->Parameters; + request->SendLength = sendLength; + + userRequest = (PTDI_REQUEST_SEND_DATAGRAM)Irp->AssociatedIrp.SystemBuffer; + request->SendDatagramInformation = userRequest->SendDatagramInformation; + Status = STATUS_SUCCESS; + } + break; + } + + case IOCTL_TDI_SET_EVENT_HANDLER: + + // + // Because this request will enable direct callouts from the + // transport provider at DISPATCH_LEVEL to a client-specified + // routine, this request is only valid in kernel mode, denying + // access to this request in user mode. + // + + Status = STATUS_INVALID_PARAMETER; + break; + + case IOCTL_TDI_SET_INFORMATION: + { + PTDI_REQUEST_SET_INFORMATION userRequest; + PTDI_REQUEST_KERNEL_SET_INFORMATION request; + + if (Irp->AssociatedIrp.SystemBuffer) { + + userRequest = + (PTDI_REQUEST_SET_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_SET_INFORMATION; + + request = (PTDI_REQUEST_KERNEL_SET_INFORMATION)&IrpSp->Parameters; + request->SetType = userRequest->SetType; + request->RequestConnectionInformation = NULL; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_ASSOCIATE_ADDRESS: + { + PTDI_REQUEST_ASSOCIATE_ADDRESS userRequest; + PTDI_REQUEST_KERNEL_ASSOCIATE request; + + if (Irp->AssociatedIrp.SystemBuffer) { + + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_ASSOCIATE_ADDRESS; + + userRequest = + (PTDI_REQUEST_ASSOCIATE_ADDRESS)Irp->AssociatedIrp.SystemBuffer; + request = (PTDI_REQUEST_KERNEL_ASSOCIATE)&IrpSp->Parameters; + request->AddressHandle = userRequest->AddressHandle; + + Status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_TDI_DISASSOCIATE_ADDRESS: + { + IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IrpSp->MinorFunction = TDI_DISASSOCIATE_ADDRESS; + + Status = STATUS_SUCCESS; + break; + } + + default: + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + return Status; + +} + + +NTSTATUS +TdiDefaultConnectHandler( + IN PVOID TdiEventContext, + IN LONG RemoteAddressLength, + IN PVOID RemoteAddress, + IN LONG UserDataLength, + IN PVOID UserData, + IN LONG OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext, + OUT PIRP *AcceptIrp + ) + +/*++ + +Routine Description: + + This routine is called when a connect request has completed. The connection + is fully functional when the indication occurs. + +Arguments: + + TdiEventContext - the context value passed in by the user in the Set Event Handler call + + RemoteAddressLength, + + RemoteAddress, + + UserDataLength, + + UserData, + + OptionsLength, + + Options, + + ConnectionId + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (RemoteAddressLength); + UNREFERENCED_PARAMETER (RemoteAddress); + UNREFERENCED_PARAMETER (UserDataLength); + UNREFERENCED_PARAMETER (UserData); + UNREFERENCED_PARAMETER (OptionsLength); + UNREFERENCED_PARAMETER (Options); + UNREFERENCED_PARAMETER (ConnectionContext); + + return STATUS_INSUFFICIENT_RESOURCES; // do nothing +} + + +NTSTATUS +TdiDefaultDisconnectHandler( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN LONG DisconnectDataLength, + IN PVOID DisconnectData, + IN LONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags + ) + +/*++ + +Routine Description: + + This routine is used as the default disconnect event handler + for the transport endpoint. It is pointed to by a field in the + TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TransportEndpoint - Pointer to open file object. + + Context - Typeless pointer specifying connection context. + + DisconnectIndicators - Value indicating reason for disconnection indication. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (DisconnectDataLength); + UNREFERENCED_PARAMETER (DisconnectData); + UNREFERENCED_PARAMETER (DisconnectInformationLength); + UNREFERENCED_PARAMETER (DisconnectInformation); + UNREFERENCED_PARAMETER (DisconnectFlags); + + return STATUS_SUCCESS; // do nothing but return successfully. + +} /* DefaultDisconnectHandler */ + + +NTSTATUS +TdiDefaultErrorHandler( + IN PVOID TdiEventContext, // the endpoint's file object. + IN NTSTATUS Status // status code indicating error type. + ) + +/*++ + +Routine Description: + + This routine is used as the default error event handler for + the transport endpoint. It is pointed to by a field in the + TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TransportEndpoint - Pointer to open file object. + + Status - Status code indicated by this event. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (Status); + + return STATUS_SUCCESS; // do nothing but return successfully. + +} /* DefaultErrorHandler */ + + +NTSTATUS +TdiDefaultReceiveHandler( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ) + +/*++ + +Routine Description: + + This routine is used as the default receive event handler for + the transport endpoint. It is pointed to by a field in the + TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_RECEIVE. + + ConnectionContext - The client-supplied context associated with + the connection on which this connection-oriented TSDU was received. + + ReceiveFlags - Bitflags which indicate the circumstances surrounding + this TSDU's reception. + + BytesIndicated - The number of bytes of this TSDU that are being presented + to the client in this indication.This value is always less than + or equal to BytesAvailable. + + BytesAvailable - The total number of bytes of this TSDU presently + available from the transport. + + BytesTaken - Return value indicating the number of bytes of data that the + client copied from the indication data. + + Tsdu - Pointer to an MDL chain that describes the (first) part of the + (partially) received Transport Service Data Unit, less headers. + + IoRequestPacket - Pointer to a location where the event handler may + chose to return a pointer to an I/O Request Packet (IRP) to satisfy + the incoming data. If returned, this IRP must be formatted as a + valid TdiReceive request, except that the ConnectionId field of + the TdiRequest is ignored and is automatically filled in by the + transport provider. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (ReceiveFlags); + UNREFERENCED_PARAMETER (BytesIndicated); + UNREFERENCED_PARAMETER (BytesAvailable); + UNREFERENCED_PARAMETER (BytesTaken); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (IoRequestPacket); + + return STATUS_DATA_NOT_ACCEPTED; // no handler in place. + +} /* DefaultReceiveHandler */ + + +NTSTATUS +TdiDefaultRcvDatagramHandler( + IN PVOID TdiEventContext, // the event context + IN LONG SourceAddressLength, // length of the originator of the datagram + IN PVOID SourceAddress, // string describing the originator of the datagram + IN LONG OptionsLength, // options for the receive + IN PVOID Options, // + IN ULONG ReceiveDatagramFlags, // + IN ULONG BytesIndicated, // number of bytes this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ) + +/*++ + +Routine Description: + + This routine is used as the default receive datagram event + handler for the transport endpoint. It is pointed to by a + field in the TP_ENDPOINT structure for an endpoint when the + endpoint is created, and also whenever the TdiSetEventHandler + request is submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_RECEIVE_DATAGRAM. + + DestinationAddress - Pointer to the network name of the destination + to which the datagram was directed. + + SourceAddress - Pointer to the network name of the source from which + the datagram originated. + + Tsap - Transport service access point on which this datagram was received. + + ReceiveIndicators - Bitflags which indicate the circumstances surrounding + this TSDU's reception. + + Tsdu - Pointer to an MDL chain that describes the (first) part of the + (partially) received Transport Service Data Unit, less headers. + + IoRequestPacket - Pointer to a location where the event handler may + chose to return a pointer to an I/O Request Packet (IRP) to satisfy + the incoming data. If returned, this IRP must be formatted as a + valid TdiReceiveDatagram request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (SourceAddressLength); + UNREFERENCED_PARAMETER (SourceAddress); + UNREFERENCED_PARAMETER (OptionsLength); + UNREFERENCED_PARAMETER (Options); + UNREFERENCED_PARAMETER (BytesIndicated); + UNREFERENCED_PARAMETER (BytesAvailable); + UNREFERENCED_PARAMETER (BytesTaken); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (IoRequestPacket); + + return STATUS_DATA_NOT_ACCEPTED; // no handler in place. + +} /* DefaultRcvDatagramHandler */ + + +NTSTATUS +TdiDefaultRcvExpeditedHandler( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, // + IN ULONG BytesIndicated, // number of bytes in this indication + IN ULONG BytesAvailable, // number of bytes in complete Tsdu + OUT ULONG *BytesTaken, // number of bytes used by indication routine + IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes + OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. + ) + +/*++ + +Routine Description: + + This routine is used as the default expedited receive event handler + for the transport endpoint. It is pointed to by a field in the + TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_RECEIVE. + + ConnectionContext - The client-supplied context associated with + the connection on which this connection-oriented TSDU was received. + + ReceiveFlags - Bitflags which indicate the circumstances surrounding + this TSDU's reception. + + BytesIndicated - The number of bytes of this TSDU that are being presented + to the client in this indication.This value is always less than + or equal to BytesAvailable. + + BytesAvailable - The total number of bytes of this TSDU presently + available from the transport. + + BytesTaken - Return value indicating the number of bytes of data that the + client copied from the indication data. + + Tsdu - Pointer to an MDL chain that describes the (first) part of the + (partially) received Transport Service Data Unit, less headers. + + IoRequestPacket - Pointer to a location where the event handler may + chose to return a pointer to an I/O Request Packet (IRP) to satisfy + the incoming data. If returned, this IRP must be formatted as a + valid TdiReceive request, except that the ConnectionId field of + the TdiRequest is ignored and is automatically filled in by the + transport provider. + +Return Value: + + NTSTATUS - status of operation. + +--*/ +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (ReceiveFlags); + UNREFERENCED_PARAMETER (BytesIndicated); + UNREFERENCED_PARAMETER (BytesAvailable); + UNREFERENCED_PARAMETER (BytesTaken); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (IoRequestPacket); + + return STATUS_DATA_NOT_ACCEPTED; + +} /* DefaultRcvExpeditedHandler */ + +NTSTATUS +TdiDefaultChainedReceiveHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG ReceiveLength, + IN ULONG StartingOffset, + IN PMDL Tsdu, + IN PVOID TsduDescriptor + ) + +/*++ + +Routine Description: + + This routine is used as the default chanied receive event handler + for the transport endpoint. It is pointed to by a field in the + TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_CHAINED_RECEIVE. + + ConnectionContext - The client-supplied context associated with + the connection on which this connection-oriented TSDU was received. + + ReceiveFlags - Bitflags which indicate the circumstances surrounding + this TSDU's reception. + + ReceiveLength - The length in bytes of client data in the TSDU. + + StartingOffset - The offset, in bytes from the beginning of the TSDU, + at which the client data begins. + + Tsdu - Pointer to an MDL chain that describes the entire received + Transport Service Data Unit. + + TsduDescriptor - A descriptor for the TSDU which must be passed to + TdiReturnChainedReceives in order to return the TSDU for reuse. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (ReceiveFlags); + UNREFERENCED_PARAMETER (ReceiveLength); + UNREFERENCED_PARAMETER (StartingOffset); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (TsduDescriptor); + + return STATUS_DATA_NOT_ACCEPTED; + +} /* DefaultChainedReceiveHandler */ + + +NTSTATUS +TdiDefaultChainedRcvDatagramHandler( + IN PVOID TdiEventContext, + IN LONG SourceAddressLength, + IN PVOID SourceAddress, + IN LONG OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG ReceiveDatagramLength, + IN ULONG StartingOffset, + IN PMDL Tsdu, + IN PVOID TsduDescriptor + ) + +/*++ + +Routine Description: + + This routine is used as the default chained receive datagram + event handler for the transport endpoint. It is pointed to by + a field in the TP_ENDPOINT structure for an endpoint when the + endpoint is created, and also whenever the TdiSetEventHandler + request is submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_CHAINED_RECEIVE_DATAGRAM. + + SourceAddressLength - The length of the source network address. + + SourceAddress - Pointer to the network address of the source from which + the datagram originated. + + OptionsLength - The length of the transport options accompanying this TSDU. + + Options - Pointer to the transport options accompanying this TSDU. + + ReceiveDatagramFlags - Bitflags which indicate the circumstances + surrounding this TSDU's reception. + + ReceiveDatagramLength - The length, in bytes, of the client data in + this TSDU. + + StartingOffset - The offset, in bytes from the start of the TSDU, at + which the client data begins. + + Tsdu - Pointer to an MDL chain that describes the received Transport + Service Data Unit. + + TsduDescriptor - A descriptor for the TSDU which must be passed to + TdiReturnChainedReceives in order to return the TSDU for reuse. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (SourceAddressLength); + UNREFERENCED_PARAMETER (SourceAddress); + UNREFERENCED_PARAMETER (OptionsLength); + UNREFERENCED_PARAMETER (Options); + UNREFERENCED_PARAMETER (ReceiveDatagramLength); + UNREFERENCED_PARAMETER (StartingOffset); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (TsduDescriptor); + + return STATUS_DATA_NOT_ACCEPTED; + +} /* DefaultChainedRcvDatagramHandler */ + + +NTSTATUS +TdiDefaultChainedRcvExpeditedHandler( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG ReceiveLength, + IN ULONG StartingOffset, + IN PMDL Tsdu, + IN PVOID TsduDescriptor + ) + +/*++ + +Routine Description: + + This routine is used as the default chained expedited receive event + handler for the transport endpoint. It is pointed to by a field + in the TP_ENDPOINT structure for an endpoint when the endpoint is + created, and also whenever the TdiSetEventHandler request is + submitted with a NULL EventHandler field. + +Arguments: + + TdiEventContext - Pointer to the client-provided context value specified + in the TdiSetEventHandler call for TDI_EVENT_CHAINED_RECEIVE_EXPEDITED. + + ConnectionContext - The client-supplied context associated with + the connection on which this connection-oriented TSDU was received. + + ReceiveFlags - Bitflags which indicate the circumstances surrounding + this TSDU's reception. + + ReceiveLength - The length in bytes of client data in the TSDU. + + StartingOffset - The offset, in bytes from the beginning of the TSDU, + at which the client data begins. + + Tsdu - Pointer to an MDL chain that describes the entire received + Transport Service Data Unit. + + TsduDescriptor - A descriptor for the TSDU which must be passed to + TdiReturnChainedReceives in order to return the TSDU for reuse. + +Return Value: + + NTSTATUS - status of operation. + +--*/ +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (ReceiveFlags); + UNREFERENCED_PARAMETER (ReceiveLength); + UNREFERENCED_PARAMETER (StartingOffset); + UNREFERENCED_PARAMETER (Tsdu); + UNREFERENCED_PARAMETER (TsduDescriptor); + + return STATUS_DATA_NOT_ACCEPTED; + +} /* DefaultRcvExpeditedHandler */ + + +NTSTATUS +TdiDefaultSendPossibleHandler ( + IN PVOID TdiEventContext, + IN PVOID ConnectionContext, + IN ULONG BytesAvailable) + +/*++ + +Routine Description: + +Arguments: + + TdiEventContext - the context value passed in by the user in the Set Event Handler call + + ConnectionContext - connection context of connection which can be sent on + + BytesAvailable - number of bytes which can now be sent + +Return Value: + + ignored by the transport + +--*/ + +{ + UNREFERENCED_PARAMETER (TdiEventContext); + UNREFERENCED_PARAMETER (ConnectionContext); + UNREFERENCED_PARAMETER (BytesAvailable); + + return STATUS_SUCCESS; +} + +VOID +TdiBuildNetbiosAddress( + IN PUCHAR NetbiosName, + IN BOOLEAN IsGroupName, + IN OUT PTA_NETBIOS_ADDRESS NetworkName + ) + +/*++ + +Routine Description: + + This routine builds a TA_NETBIOS_ADDRESS structure in the locations pointed + to by NetworkName. All fields are filled out. + +Arguments: + + NetbiosName - Pointer to a 16-byte buffer where the a netbios name is + supplied. + + IsGroupName - TRUE if this name is a group name, false otherwise. + + NetworkName - A pointer to a TA_NETBIOS_ADDRESS structure that is to + receive the buid TDI address. + +Return Value: + + none. + +--*/ + +{ + IF_TDIDBG (TDI_DEBUG_NAMES) { + DbgPrint ("TdiBuildNetBIOSAddress: Entered.\n"); + } + + NetworkName->TAAddressCount = 1; + NetworkName->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + NetworkName->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + + if (IsGroupName) { + NetworkName->Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_GROUP; + } else { + NetworkName->Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + } + + RtlCopyMemory ( + NetworkName->Address[0].Address[0].NetbiosName, + NetbiosName, + 16); + +} /* TdiBuildNetbiosAddress */ + + +NTSTATUS +TdiBuildNetbiosAddressEa ( + IN PUCHAR Buffer, + IN BOOLEAN IsGroupName, + IN PUCHAR NetbiosName + ) + +/*++ + +Routine Description: + + Builds an EA describing a Netbios address in the buffer supplied by the + user. + +Arguments: + + Buffer - pointer to a buffer that the ea is to be built in. This buffer + must be at least 40 bytes long. + + IsGroupName - true if the netbios name is a group name, false otherwise. + + NetbiosName - the netbios name to be inserted in the EA to be built. + +Return Value: + + An informative error code if something goes wrong. STATUS_SUCCESS if the + ea is built properly. + +--*/ + +{ + PFILE_FULL_EA_INFORMATION EaBuffer; + PTA_NETBIOS_ADDRESS TAAddress; + ULONG Length; + + IF_TDIDBG (TDI_DEBUG_NAMES) { + DbgPrint ("TdiBuildNetbiosAddressEa: Entered\n "); + } + + try { + Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + + sizeof (TA_NETBIOS_ADDRESS); + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + + if (EaBuffer == NULL) { + return STATUS_UNSUCCESSFUL; + } + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TA_NETBIOS_ADDRESS); + + RtlCopyMemory ( + EaBuffer->EaName, + TdiTransportAddress, + EaBuffer->EaNameLength + 1); + + TAAddress = (PTA_NETBIOS_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + + TdiBuildNetbiosAddress ( + NetbiosName, + IsGroupName, + TAAddress); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + // + // Couldn't touch the passed parameters; just return an error + // status. + // + + return GetExceptionCode(); + } + + return STATUS_SUCCESS; + +} /* TdiBuildNetbiosAddressEa */ + + +VOID +TdiMapBuffer( + IN PMDL MdlChain + ) + +/*++ + +Routine Description: + + This routine ensures that the MappedSystemVa field of every MDL in an + MDL chain is valid; calling MmMapLockedPages where necessary. + + !!! This routine no longer does anything, and should be removed! + +Arguments: + + MdlChain - Pointer to a chain of MDLs describing the data to be mapped. + +Return Value: + + none. + +--*/ + +{ + MdlChain; + + return; + +} /* TdiMapBuffer */ + + +VOID +TdiUnmapBuffer( + IN PMDL MdlChain + ) + +/*++ + +Routine Description: + + This routine restores the mapped status of every MDL in an MDL chain + where the MappedSystemVa field is equal to StartVa+ByteOffset. + + !!! This routine no longer does anything, and should be removed! + +Arguments: + + MdlChain - Pointer to a chain of MDLs describing the data to be unmapped. + +Return Value: + + none. + +--*/ + +{ + MdlChain; + + return; + +} /* TdiUnmapBuffer */ + + +NTSTATUS +TdiCopyMdlToBuffer( + IN PMDL SourceMdlChain, + IN ULONG SourceOffset, + IN PVOID DestinationBuffer, + IN ULONG DestinationOffset, + IN ULONG DestinationBufferSize, + OUT PULONG BytesCopied + ) + +/*++ + +Routine Description: + + This routine copies data described by the source MDL chain starting at + the source offset, into a flat buffer specified by the SVA starting at + the destination offset. A maximum of DestinationBufferSize bytes can + be copied. The actual number of bytes copied is returned in BytesCopied. + +Arguments: + + SourceMdlChain - Pointer to a chain of MDLs describing the source data. + + SourceOffset - Number of bytes to skip in the source data. + + DestinationBuffer - Pointer to a flat buffer to copy the data to. + + DestinationOffset - Number of leading bytes to skip in the destination buffer. + + DestinationBufferSize - Size of the output buffer, including the offset. + + BytesCopied - Pointer to a longword where the actual number of bytes + transferred will be returned. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PUCHAR Dest, Src; + ULONG SrcBytesLeft, DestBytesLeft, BytesSkipped=0; + + IF_TDIDBG (TDI_DEBUG_MAP) { + DbgPrint ("TdiCopyMdlToBuffer: Entered.\n"); + } + + ASSERT( DestinationBufferSize >= DestinationOffset ); + + *BytesCopied = 0; + + // + // Skip source bytes. + // + + Src = MmGetSystemAddressForMdl (SourceMdlChain); + SrcBytesLeft = MmGetMdlByteCount (SourceMdlChain); + while (BytesSkipped < SourceOffset) { + if (SrcBytesLeft > (SourceOffset - BytesSkipped)) { + // PANIC ("TdiCopyMdlToBuffer: Skipping part of this MDL.\n"); + SrcBytesLeft -= (SourceOffset - BytesSkipped); + Src += (SourceOffset - BytesSkipped); + BytesSkipped = SourceOffset; + break; + } else if (SrcBytesLeft == (SourceOffset - BytesSkipped)) { + // PANIC ("TdiCopyMdlToBuffer: Skipping this exact MDL.\n"); + SourceMdlChain = SourceMdlChain->Next; + if (SourceMdlChain == NULL) { + //PANIC ("TdiCopyMdlToBuffer: MDL chain was all header.\n"); + return STATUS_SUCCESS; // no bytes copied. + } + BytesSkipped = SourceOffset; + Src = MmGetSystemAddressForMdl (SourceMdlChain); + SrcBytesLeft = MmGetMdlByteCount (SourceMdlChain); + break; + } else { + // PANIC ("TdiCopyMdlToBuffer: Skipping all of this MDL & more.\n"); + BytesSkipped += SrcBytesLeft; + SourceMdlChain = SourceMdlChain->Next; + if (SourceMdlChain == NULL) { + //PANIC ("TdiCopyMdlToBuffer: Premature end of MDL chain.\n"); + return STATUS_SUCCESS; // no bytes copied. + } + Src = MmGetSystemAddressForMdl (SourceMdlChain); + SrcBytesLeft = MmGetMdlByteCount (SourceMdlChain); + } + } + + // PANIC ("TdiCopyMdlToBuffer: done skipping source bytes.\n"); + + // + // Skip destination bytes. + // + + Dest = (PUCHAR)DestinationBuffer + DestinationOffset; + DestBytesLeft = DestinationBufferSize - DestinationOffset; + + // + // Copy source data into the destination buffer until it's full or + // we run out of data, whichever comes first. + // + + while (DestBytesLeft && SourceMdlChain) { + if (SrcBytesLeft == 0) { + // PANIC ("TdiCopyMdlToBuffer: MDL is empty, skipping to next one.\n"); + SourceMdlChain = SourceMdlChain->Next; + if (SourceMdlChain == NULL) { + // PANIC ("TdiCopyMdlToBuffer: But there are no more MDLs.\n"); + return STATUS_SUCCESS; + } + Src = MmGetSystemAddressForMdl (SourceMdlChain); + SrcBytesLeft = MmGetMdlByteCount (SourceMdlChain); + continue; // skip 0-length MDL's. + } + // PANIC ("TdiCopyMdlToBuffer: Copying a chunk.\n"); + if (DestBytesLeft == SrcBytesLeft) { + // PANIC ("TdiCopyMdlToBuffer: Copying exact amount.\n"); + RtlCopyBytes (Dest, Src, DestBytesLeft); + *BytesCopied += DestBytesLeft; + return STATUS_SUCCESS; + } else if (DestBytesLeft < SrcBytesLeft) { + // PANIC ("TdiCopyMdlToBuffer: Buffer overflow, copying some.\n"); + RtlCopyBytes (Dest, Src, DestBytesLeft); + *BytesCopied += DestBytesLeft; + return STATUS_BUFFER_OVERFLOW; + } else { + // PANIC ("TdiCopyMdlToBuffer: Copying all of this MDL, & more.\n"); + RtlCopyBytes (Dest, Src, SrcBytesLeft); + *BytesCopied += SrcBytesLeft; + DestBytesLeft -= SrcBytesLeft; + Dest += SrcBytesLeft; + SrcBytesLeft = 0; + } + } + + return SourceMdlChain == NULL ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW; +} /* TdiCopyMdlToBuffer */ + + +NTSTATUS +TdiCopyBufferToMdl ( + IN PVOID SourceBuffer, + IN ULONG SourceOffset, + IN ULONG SourceBytesToCopy, + IN PMDL DestinationMdlChain, + IN ULONG DestinationOffset, + IN PULONG BytesCopied + ) + +/*++ + +Routine Description: + + This routine copies data described by the source buffer to the MDL chain + described by the DestinationMdlChain. The + +Arguments: + + SourceBuffer - pointer to the source buffer + + SourceOffset - Number of bytes to skip in the source data. + + SourceBytesToCopy - number of bytes to copy from the source buffer + + DestinationMdlChain - Pointer to a chain of MDLs describing the + destination buffers. + + DestinationOffset - Number of bytes to skip in the destination data. + + BytesCopied - Pointer to a longword where the actual number of bytes + transferred will be returned. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PUCHAR Dest, Src; + ULONG DestBytesLeft, BytesSkipped=0; + + IF_TDIDBG (TDI_DEBUG_MAP) { + DbgPrint ("TdiCopyBufferToMdl: Entered.\n"); + } + + *BytesCopied = 0; + + // + // Skip Destination bytes. + // + + Dest = MmGetSystemAddressForMdl (DestinationMdlChain); + DestBytesLeft = MmGetMdlByteCount (DestinationMdlChain); + while (BytesSkipped < DestinationOffset) { + if (DestBytesLeft > (DestinationOffset - BytesSkipped)) { + // PANIC ("TdiCopyMdlToBuffer: Skipping part of this MDL.\n"); + DestBytesLeft -= (DestinationOffset - BytesSkipped); + Dest += (DestinationOffset - BytesSkipped); + BytesSkipped = DestinationOffset; + break; + } else if (DestBytesLeft == (DestinationOffset - BytesSkipped)) { + // PANIC ("TdiCopyMdlToBuffer: Skipping this exact MDL.\n"); + DestinationMdlChain = DestinationMdlChain->Next; + if (DestinationMdlChain == NULL) { + //PANIC ("TdiCopyMdlToBuffer: MDL chain was all header.\n"); + return STATUS_BUFFER_OVERFLOW; // no bytes copied. + } + BytesSkipped = DestinationOffset; + Dest = MmGetSystemAddressForMdl (DestinationMdlChain); + DestBytesLeft = MmGetMdlByteCount (DestinationMdlChain); + break; + } else { + // PANIC ("TdiCopyMdlToBuffer: Skipping all of this MDL & more.\n"); + BytesSkipped += DestBytesLeft; + DestinationMdlChain = DestinationMdlChain->Next; + if (DestinationMdlChain == NULL) { + //PANIC ("TdiCopyMdlToBuffer: Premature end of MDL chain.\n"); + return STATUS_BUFFER_OVERFLOW; // no bytes copied. + } + Dest = MmGetSystemAddressForMdl (DestinationMdlChain); + DestBytesLeft = MmGetMdlByteCount (DestinationMdlChain); + } + } + + // PANIC ("TdiCopyMdlToBuffer: done skipping source bytes.\n"); + + // + // Skip source bytes. + // + + Src = (PUCHAR)SourceBuffer + SourceOffset; + + // + // Copy source data into the destination buffer until it's full or + // we run out of data, whichever comes first. + // + + while ((SourceBytesToCopy != 0) && (DestinationMdlChain != NULL)) { + if (DestBytesLeft == 0) { + // PANIC ("TdiCopyMdlToBuffer: MDL is empty, skipping to next one.\n"); + DestinationMdlChain = DestinationMdlChain->Next; + if (DestinationMdlChain == NULL) { + // PANIC ("TdiCopyMdlToBuffer: But there are no more MDLs.\n"); + return STATUS_BUFFER_OVERFLOW; + } + Dest = MmGetSystemAddressForMdl (DestinationMdlChain); + DestBytesLeft = MmGetMdlByteCount (DestinationMdlChain); + continue; // skip 0-length MDL's. + } + + // PANIC ("TdiCopyMdlToBuffer: Copying a chunk.\n"); + if (DestBytesLeft >= SourceBytesToCopy) { + // PANIC ("TdiCopyMdlToBuffer: Copying exact amount.\n"); + RtlCopyBytes (Dest, Src, SourceBytesToCopy); + *BytesCopied += SourceBytesToCopy; + return STATUS_SUCCESS; + } else { + // PANIC ("TdiCopyMdlToBuffer: Copying all of this MDL, & more.\n"); + RtlCopyBytes (Dest, Src, DestBytesLeft); + *BytesCopied += DestBytesLeft; + SourceBytesToCopy -= DestBytesLeft; + Src += DestBytesLeft; + DestBytesLeft = 0; + } + } + + return SourceBytesToCopy == 0 ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW; + +} /* TdiCopyBufferToMdl */ + + +NTSTATUS +TdiOpenNetbiosAddress ( + IN OUT PHANDLE FileHandle, + IN PUCHAR Buffer, + IN PVOID DeviceName, + IN PVOID Address) + +/*++ + +Routine Description: + + Opens an address on the given file handle and device. + +Arguments: + + FileHandle - the returned handle to the file object that is opened. + + Buffer - pointer to a buffer that the ea is to be built in. This buffer + must be at least 40 bytes long. + + DeviceName - the Unicode string that points to the device object. + + Name - the address to be registered. If this pointer is NULL, the routine + will attempt to open a "control channel" to the device; that is, it + will attempt to open the file object with a null ea pointer, and if the + transport provider allows for that, will return that handle. + +Return Value: + + An informative error code if something goes wrong. STATUS_SUCCESS if the + returned file handle is valid. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + TA_NETBIOS_ADDRESS NetbiosAddress; + PSZ Name; + ULONG Length; + + IF_TDIDBG (TDI_DEBUG_NAMES) { + DbgPrint ("TdiOpenNetbiosAddress: Opening "); + if (Address == NULL) { + DbgPrint (" Control Channel"); + } else { + DbgPrint (Address); + } + DbgPrint (".\n"); + } + + if (Address != NULL) { + Name = (PSZ)Address; + try { + Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + + sizeof(TA_NETBIOS_ADDRESS); + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + + if (EaBuffer == NULL) { + return STATUS_UNSUCCESSFUL; + } + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TA_NETBIOS_ADDRESS); + + RtlCopyMemory( + EaBuffer->EaName, + TdiTransportAddress, + EaBuffer->EaNameLength + 1 + ); + + // + // Create a copy of the NETBIOS address descriptor in a local + // first, in order to avoid alignment problems. + // + + NetbiosAddress.TAAddressCount = 1; + NetbiosAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + NetbiosAddress.Address[0].AddressLength = + sizeof (TDI_ADDRESS_NETBIOS); + NetbiosAddress.Address[0].Address[0].NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + RtlCopyMemory( + NetbiosAddress.Address[0].Address[0].NetbiosName, + Name, + 16 + ); + + RtlCopyMemory ( + &EaBuffer->EaName[EaBuffer->EaNameLength + 1], + &NetbiosAddress, + sizeof(TA_NETBIOS_ADDRESS) + ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + // + // Couldn't touch the passed parameters; just return an error + // status. + // + + return GetExceptionCode(); + } + } else { + EaBuffer = NULL; + Length = 0; + } + + InitializeObjectAttributes ( + &ObjectAttributes, + DeviceName, + 0, + NULL, + NULL); + + Status = NtCreateFile ( + FileHandle, + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access. + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + 0, // file attributes. + FILE_SHARE_READ | FILE_SHARE_WRITE, // share access. + FILE_CREATE, // create disposition. + 0, // create options. + EaBuffer, // EA buffer. + Length); // EA length. + + if (!NT_SUCCESS( Status )) { + IF_TDIDBG (TDI_DEBUG_NAMES) { + DbgPrint ("TdiOpenNetbiosEndpoint: FAILURE, NtCreateFile returned status %lx.\n", Status); + } + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + IF_TDIDBG (TDI_DEBUG_NAMES) { + DbgPrint ("TdiOpenNetbiosEndpoint: FAILURE, IoStatusBlock.Status contains status code=%lx.\n", Status); + } + } + + return Status; +} /* TdiOpenNetbiosAddress */ + + + +VOID +TdiReturnChainedReceives( + IN PVOID *TsduDescriptors, + IN ULONG NumberOfTsdus + ) + +/*++ + +Routine Description: + + Used by a TDI client to return ownership of a set of chained receive TSDUs + to the NDIS layer. This routine may only be called if the client took + ownership of the TSDUs by returning STATUS_PENDING to one of the + CHAINED_RECEIVE indications. + +Arguments: + + TsduDescriptors - An array of TSDU descriptors. Each descriptor was + provided in one of the CHAINED_RECEIVE indications. The descriptors + are actually pointers to the NDIS_PACKETS containing the TSDUs. + + NumberOfTsdus - The count of TSDU descriptors in the TsduDescriptors array. + +Return Value: + + None. +--*/ + +{ + NdisReturnPackets( + (PNDIS_PACKET *) TsduDescriptors, + (UINT) NumberOfTsdus + ); +} + +VOID +TdiInitialize( + VOID + ) + +/*++ + +Routine Description: + + The TDI initialization routine, since our DriverEntry isn't called. + If we're not already initialized we'll initialize our lists heads. + +Arguments: + + Nothing. + +Return Value: + + None. + +--*/ + +{ + // See if we're already initialized. If we're not, + // we won't have an initalized spinlock so we can't use that + // to serialize this. Instead, we interlocked increment a counter + // that is initially 0. If the counter goes to 1, we're the + // first one through here, so we'll do the initialization. Otherwise + // it's already been done, so we'll decrement the counter back + // to it's original state. + + if (InterlockedIncrement(&TdiInitializationCount) == 1) { + + // We're the first ones. + // Initialize the variables the register/deregister code uses. + + KeInitializeSpinLock(&TDIListLock); + + InitializeListHead(&BindClientList); + + InitializeListHead(&NetAddressClientList); + + InitializeListHead(&BindProviderList); + + InitializeListHead(&NetAddressProviderList); + + InitializeListHead(&BindRequestList); + + InitializeListHead(&NetAddressRequestList); + + NdisRegisterTdiCallBack(TdiRegisterDeviceObject); + + } else { + + // Already been done, just decrement the counter. + InterlockedDecrement(&TdiInitializationCount); + } +} + diff --git a/private/ntos/tdi/wrapper/tdi.def b/private/ntos/tdi/wrapper/tdi.def new file mode 100644 index 000000000..17886834f --- /dev/null +++ b/private/ntos/tdi/wrapper/tdi.def @@ -0,0 +1,44 @@ +NAME TDI.SYS + +DESCRIPTION 'TDI.SYS' + +EXPORTS + TdiBuildNetbiosAddress + TdiBuildNetbiosAddressEa + TdiCopyBufferToMdl + TdiCopyMdlToBuffer + TdiDefaultConnectHandler + TdiDefaultDisconnectHandler + TdiDefaultErrorHandler + TdiDefaultRcvDatagramHandler + TdiDefaultRcvExpeditedHandler + TdiDefaultReceiveHandler + TdiMapBuffer + TdiMapUserRequest + TdiOpenNetbiosAddress + TdiUnmapBuffer + TdiDefaultSendPossibleHandler + CTEInitialize + CTEInitEvent + CTEScheduleEvent + CTEInitTimer + CTEStartTimer + CTESystemUpTime + CTEBlock + CTESignal + CTEInitString + CTEAllocateString + CTELogEvent + TdiDefaultChainedRcvDatagramHandler + TdiDefaultChainedRcvExpeditedHandler + TdiDefaultChainedReceiveHandler + TdiReturnChainedReceives + TdiRegisterNotificationHandler + TdiDeregisterNotificationHandler + TdiRegisterDeviceObject + TdiDeregisterDeviceObject + TdiRegisterAddressChangeHandler + TdiDeregisterAddressChangeHandler + TdiRegisterNetAddress + TdiDeregisterNetAddress + TdiInitialize diff --git a/private/ntos/tdi/wrapper/tdi.pnp b/private/ntos/tdi/wrapper/tdi.pnp new file mode 100644 index 000000000..17886834f --- /dev/null +++ b/private/ntos/tdi/wrapper/tdi.pnp @@ -0,0 +1,44 @@ +NAME TDI.SYS + +DESCRIPTION 'TDI.SYS' + +EXPORTS + TdiBuildNetbiosAddress + TdiBuildNetbiosAddressEa + TdiCopyBufferToMdl + TdiCopyMdlToBuffer + TdiDefaultConnectHandler + TdiDefaultDisconnectHandler + TdiDefaultErrorHandler + TdiDefaultRcvDatagramHandler + TdiDefaultRcvExpeditedHandler + TdiDefaultReceiveHandler + TdiMapBuffer + TdiMapUserRequest + TdiOpenNetbiosAddress + TdiUnmapBuffer + TdiDefaultSendPossibleHandler + CTEInitialize + CTEInitEvent + CTEScheduleEvent + CTEInitTimer + CTEStartTimer + CTESystemUpTime + CTEBlock + CTESignal + CTEInitString + CTEAllocateString + CTELogEvent + TdiDefaultChainedRcvDatagramHandler + TdiDefaultChainedRcvExpeditedHandler + TdiDefaultChainedReceiveHandler + TdiReturnChainedReceives + TdiRegisterNotificationHandler + TdiDeregisterNotificationHandler + TdiRegisterDeviceObject + TdiDeregisterDeviceObject + TdiRegisterAddressChangeHandler + TdiDeregisterAddressChangeHandler + TdiRegisterNetAddress + TdiDeregisterNetAddress + TdiInitialize diff --git a/private/ntos/tdi/wrapper/tdi.rc b/private/ntos/tdi/wrapper/tdi.rc new file mode 100644 index 000000000..6882bb371 --- /dev/null +++ b/private/ntos/tdi/wrapper/tdi.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "TDI Wrapper" +#define VER_INTERNALNAME_STR "tdi.sys" +#define VER_ORIGINALFILENAME_STR "tdi.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/wrapper/tdidebug.h b/private/ntos/tdi/wrapper/tdidebug.h new file mode 100644 index 000000000..e4aba7eed --- /dev/null +++ b/private/ntos/tdi/wrapper/tdidebug.h @@ -0,0 +1,52 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + tdidebug.h + +Abstract: + + This module contains code which assists the process of debugging an NT + TDI client. + +Author: + + David Beaver (dbeaver) 28 June 1991 + +Environment: + + Kernel mode + +Revision History: + + All code moved in from other XNS and NBF locations at creation + + +--*/ + +#if DBG +#include +#include + +VOID +TdiPrintf( + char *Format, + ... + ); + +VOID +TdiFormattedDump( + PCHAR far_p, + ULONG len + ); + +VOID +TdiHexDumpLine( + PCHAR pch, + ULONG len, + PCHAR s, + PCHAR t + ); +#endif diff --git a/private/ntos/tdi/wrapper/tdipnp.c b/private/ntos/tdi/wrapper/tdipnp.c new file mode 100644 index 000000000..615e19715 --- /dev/null +++ b/private/ntos/tdi/wrapper/tdipnp.c @@ -0,0 +1,1029 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + tdipnp.c + +Abstract: + + TDI routines for supporting PnP in transports and transport clients. + +Author: + + Henry Sanders (henrysa) Oct. 10, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + henrysa 10-10-95 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include "tdipnp.h" + +KSPIN_LOCK TDIListLock; + +LIST_ENTRY BindClientList; + +LIST_ENTRY NetAddressClientList; + +LIST_ENTRY BindProviderList; + +LIST_ENTRY NetAddressProviderList; + +LIST_ENTRY BindRequestList; + +LIST_ENTRY NetAddressRequestList; + +BOOLEAN BindRequestInProgress; + +PETHREAD BindRequestThread; + +BOOLEAN AddressRequestInProgress; + +PETHREAD AddressRequestThread; + +VOID +TdiNotifyClientList ( + PLIST_ENTRY ListHead, + PVOID Info, + BOOLEAN Added +) + +/*++ + +Routine Description: + + Called when a new provider is added or deleted. We walk the specified + client list and notify all of the clients of what just occured. + +Arguments: + + ListHead - Head of list to walk. + Info - Information describing the provider that changed. + Added - True if a provider was added, false otherwise + +Return Value: + + + +--*/ + +{ + PLIST_ENTRY Current; + PTDI_PROVIDER_COMMON ProviderCommon; + PTDI_NOTIFY_ELEMENT NotifyElement; + PTDI_PROVIDER_RESOURCE Provider; + + Current = ListHead->Flink; + + + // The Info parameter is actually a pointer to a PROVIDER_COMMON + // structure, so get back to that so that we can find out what kind of + // provider this is. + + ProviderCommon = (PTDI_PROVIDER_COMMON)Info; + + Provider = CONTAINING_RECORD( + ProviderCommon, + TDI_PROVIDER_RESOURCE, + Common + ); + + // Walk the input client list, and for every element in it + // notifhy the client. + + while (Current != ListHead) { + + NotifyElement = CONTAINING_RECORD( + Current, + TDI_NOTIFY_ELEMENT, + Common.Linkage + ); + + if (Provider->Common.Type == TDI_RESOURCE_DEVICE) { + // This is a device object provider. + + // This must be a notify bind element. + + + // If this is a device coming in, call the bind handler, + // else call the unbind handler. + + if (Added) { + (*(NotifyElement->Specific.BindElement.BindHandler))( + &Provider->Specific.Device.DeviceName + ); + } else { + (*(NotifyElement->Specific.BindElement.UnbindHandler))( + &Provider->Specific.Device.DeviceName + ); + } + } else { + + // This is a notify net address element. If this is + // an address coming in, call the add address handler, + // otherwise call delete address handler. + + if (Added) { + (*(NotifyElement->Specific.AddressElement.AddHandler))( + &Provider->Specific.NetAddress.Address + ); + } else { + (*(NotifyElement->Specific.AddressElement.DeleteHandler))( + &Provider->Specific.NetAddress.Address + ); + } + } + + // Get the next one. + + Current = Current->Flink; + + } +} + +VOID +TdiNotifyNewClient ( + PLIST_ENTRY ListHead, + PVOID Info +) + +/*++ + +Routine Description: + + Called when a new client is added and we want to notify it of existing + providers. The client can be for either binds or net addresses. We + walk the specified input list, and notify the client about each entry in + it. + +Arguments: + + ListHead - Head of list to walk. + Info - Information describing the new client to be notified. + +Return Value: + + + +--*/ + +{ + PLIST_ENTRY CurrentEntry; + PTDI_NOTIFY_COMMON NotifyCommon; + PTDI_PROVIDER_RESOURCE Provider; + PTDI_NOTIFY_ELEMENT NotifyElement; + + CurrentEntry = ListHead->Flink; + + // The info is actually a pointer to a client notify element. Cast + // it to the common type. + + NotifyCommon = (PTDI_NOTIFY_COMMON)Info; + + // Walk the input provider list, and for every element in it notify + // the new client. + + while (CurrentEntry != ListHead) { + + // If the new client is for bind notifys, set up to call it's bind + // handler. + + // Put the current provider element into the proper form. + + Provider = CONTAINING_RECORD( + CurrentEntry, + TDI_PROVIDER_RESOURCE, + Common.Linkage + ); + + NotifyElement = CONTAINING_RECORD( + NotifyCommon, + TDI_NOTIFY_ELEMENT, + Common + ); + + if (NotifyCommon->Type == TDI_NOTIFY_DEVICE) { + + // This is a bind notify client. + + + + (*(NotifyElement->Specific.BindElement.BindHandler))( + &Provider->Specific.Device.DeviceName + ); + + } else { + // This is an address notify client. + + (*(NotifyElement->Specific.AddressElement.AddHandler))( + &Provider->Specific.NetAddress.Address + ); + } + + // And do the next one. + + CurrentEntry = CurrentEntry->Flink; + + } +} + +NTSTATUS +TdiHandleSerializedRequest ( + PVOID RequestInfo, + UINT RequestType +) + +/*++ + +Routine Description: + + Called when we want to process a request relating to one of the + lists we manage. We look to see if we are currently processing such + a request - if we are, we queue this for later. Otherwise we'll + remember that we are doing this, and we'll process this request. + When we're done we'll look to see if any more came in while we were + busy. + +Arguments: + + RequestInfo - Reqeust specific information. + RequestType - The type of the request. + +Return Value: + + Request completion status. + + +--*/ + +{ + KIRQL OldIrql; + PLIST_ENTRY List; + PLIST_ENTRY ClientList; + PLIST_ENTRY ProviderList; + PLIST_ENTRY RequestList; + PBOOLEAN SerializeFlag; + PETHREAD *RequestThread; + PTDI_SERIALIZED_REQUEST Request; + PKEVENT BlockedEvent = NULL; + PTDI_NOTIFY_COMMON NotifyElement; + PTDI_PROVIDER_RESOURCE ProviderElement; + + ExAcquireSpinLock( + &TDIListLock, + &OldIrql + ); + + if (RequestType <= TDI_MAX_BIND_REQUEST) { + ClientList = &BindClientList; + ProviderList = &BindProviderList; + RequestList = &BindRequestList; + SerializeFlag = &BindRequestInProgress; + RequestThread = &BindRequestThread; + } else { + ClientList = &NetAddressClientList; + ProviderList = &NetAddressProviderList; + RequestList = &NetAddressRequestList; + SerializeFlag = &AddressRequestInProgress; + RequestThread = &AddressRequestThread; + } + + // If we're not already here, handle it right away. + + if (!(*SerializeFlag)) { + + *SerializeFlag = TRUE; + + // Save the identity of the thread we're doing this in in case someone + // tries to delete a client, which needs to block. In that case we'll + // check to make sure it's not being done in the same thread we're using + // to prevent deadlock. + + *RequestThread = PsGetCurrentThread(); + + for (;;) { + + // We're done with the lock for now, so free it. + + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + + // Figure out the type of request we have here. + + switch (RequestType) { + case TDI_REGISTER_BIND_NOTIFY: + case TDI_REGISTER_ADDRESS_NOTIFY: + // This is a client register bind or address handler request. + + // Insert this one into the registered client list. + NotifyElement = (PTDI_NOTIFY_COMMON)RequestInfo; + InsertTailList( + ClientList, + &NotifyElement->Linkage, + ); + + // Call TdiNotifyNewClient to notify this new client of all + // all existing providers. + + TdiNotifyNewClient( + ProviderList, + RequestInfo + ); + + break; + + case TDI_DEREGISTER_BIND_NOTIFY: + case TDI_DEREGISTER_ADDRESS_NOTIFY: + + // This is a client deregister request. Pull him from the + // client list, free it, and we're done. + + NotifyElement = (PTDI_NOTIFY_COMMON)RequestInfo; + RemoveEntryList(&NotifyElement->Linkage); + + ExFreePool(NotifyElement); + + break; + + case TDI_REGISTER_DEVICE: + case TDI_REGISTER_ADDRESS: + + // A provider is registering a device or address. Add him to + // the appropriate provider list, and then notify all + // existing clients of the new device. + + ProviderElement = (PTDI_PROVIDER_RESOURCE)RequestInfo; + + InsertTailList( + ProviderList, + &ProviderElement->Common.Linkage + ); + + // Call TdiNotifyClientList to do the hard work. + + TdiNotifyClientList( + ClientList, + RequestInfo, + TRUE + ); + break; + + case TDI_DEREGISTER_DEVICE: + case TDI_DEREGISTER_ADDRESS: + + // A provider device or address is deregistering. Pull the + // resource from the provider list, and notify clients that + // he's gone. + + ProviderElement = (PTDI_PROVIDER_RESOURCE)RequestInfo; + RemoveEntryList(&ProviderElement->Common.Linkage); + + TdiNotifyClientList( + ClientList, + RequestInfo, + FALSE + ); + + // Free the tracking structure we had. + + if (RequestType == TDI_DEREGISTER_DEVICE) { + ExFreePool(ProviderElement->Specific.Device.DeviceName.Buffer); + } + ExFreePool(ProviderElement); + + break; + default: + break; + } + + // If there was an event specified with this request, signal + // it now. This should only be a client deregister request, which + // needs to block until it's completed. + + if (BlockedEvent != NULL) { + KeSetEvent(BlockedEvent, 0, FALSE); + } + + // Get the lock, and see if more requests have come in while + // we've been busy. If they have, we'll service them now, otherwise + // we'll clear the in progress flag and exit. + + ExAcquireSpinLock( + &TDIListLock, + &OldIrql + ); + + if (!IsListEmpty(RequestList)) { + + // The request list isn't empty. Pull the next one from + // the list and process it. + + List = RemoveHeadList(RequestList); + + Request = CONTAINING_RECORD(List, TDI_SERIALIZED_REQUEST, Linkage); + + RequestInfo = Request->Element; + RequestType = Request->Type; + BlockedEvent = Request->Event; + + ExFreePool(Request); + + } else { + + // The request list is empty. Clear the flag and we're done. + + *SerializeFlag = FALSE; + + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + break; + } + } + + return STATUS_SUCCESS; + } else { + + // We're already running, so we'll have to queue. If this is a + // deregister bind or address notify call, we'll see if the issueing + // thread is the same one that is currently busy. If so, we'll fail + // to avoid deadlock. Otherwise for deregister calls we'll block until + // it's done. + + Request = (PTDI_SERIALIZED_REQUEST)ExAllocatePool( + NonPagedPool, + sizeof(TDI_SERIALIZED_REQUEST) + ); + + if (Request == NULL) { + + // Couldn't get a request. + + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Got the request. + Request->Element = RequestInfo; + Request->Type = RequestType; + Request->Event = NULL; + + if ( + RequestType == TDI_DEREGISTER_BIND_NOTIFY || + RequestType == TDI_REGISTER_BIND_NOTIFY || + RequestType == TDI_DEREGISTER_ADDRESS_NOTIFY + ) { + + // This is a deregister request. See if it's the same thread + // that's busy. If not, block for it to complete. + + if (*RequestThread == PsGetCurrentThread()) { + + // It's the same one, so give up now. + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + + ExFreePool(Request); + + return STATUS_NETWORK_BUSY; + } else { + // He's not currently busy, go ahead and block. + + KEVENT Event; + NTSTATUS Status; + + KeInitializeEvent( + &Event, + SynchronizationEvent, + FALSE + ); + + Request->Event = &Event; + + // Put this guy on the end of the request list. + + InsertTailList(RequestList, &Request->Linkage); + + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + + Status = KeWaitForSingleObject( + &Event, + UserRequest, + KernelMode, + FALSE, + NULL + ); + + // I don't know what we'd do is the wait failed.... + + return STATUS_SUCCESS; + } + } else { + + // This isn't a deregister request, so there's no special handling + // necessary. Just put the request on the end of the list. + + InsertTailList(RequestList, &Request->Linkage); + + ExReleaseSpinLock( + &TDIListLock, + OldIrql + ); + + return STATUS_SUCCESS; + } + } + +} + +NTSTATUS +TdiRegisterNotificationHandler( + IN TDI_BIND_HANDLER BindHandler, + IN TDI_UNBIND_HANDLER UnbindHandler, + OUT HANDLE *BindingHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI client wants to register for + notification of the arrival of TDI providers. We allocate a + TDI_NOTIFY_ELEMENT for the provider and then call the serialized + worker routine to do the real work. + +Arguments: + + BindHandler - A pointer to the routine to be called when + a new provider arrives. + UnbindHandler - A pointer to the routine to be called when a + provider leaves. + BindingHandle - A handle we pass back that identifies this + client to us. + +Return Value: + + The status of the attempt to register the client. + +--*/ + + +{ + PTDI_NOTIFY_ELEMENT NewElement; + NTSTATUS Status; + + // + // Make sure Tdi is intialized. If there are no pnp transports, then this is + // called by the tdi client and if tdi is not initialized, it is toast + // Multiple calls to TdiIntialize are safe since only the first one does + // the real work + // + TdiInitialize(); + + + // First, try and allocate the needed resource. + + NewElement = (PTDI_NOTIFY_ELEMENT)ExAllocatePool( + NonPagedPool, + sizeof(TDI_NOTIFY_ELEMENT) + ); + + // If we couldn't get it, fail the request. + if (NewElement == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Fill in the basic stuff. + NewElement->Common.Type = TDI_NOTIFY_DEVICE; + NewElement->Specific.BindElement.BindHandler = BindHandler; + NewElement->Specific.BindElement.UnbindHandler = UnbindHandler; + + *BindingHandle = (HANDLE)NewElement; + + + // Now call HandleBindRequest to handle this one. + + Status = TdiHandleSerializedRequest( + NewElement, + TDI_REGISTER_BIND_NOTIFY + ); + + if (Status != STATUS_SUCCESS) { + ExFreePool(NewElement); + } + + return Status; + + +} + +NTSTATUS +TdiDeregisterNotificationHandler( + IN HANDLE BindingHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI client wants to deregister a + previously registered bind notification handler. All we really + do is call TdiHandleSerializedRequest, which does the hard work. + +Arguments: + + BindingHandle - A handle we passed back to the client + on the register call. This is really + a pointer to the notify element. + +Return Value: + + The status of the attempt to deregister the client. + +--*/ + +{ + NTSTATUS Status; + + Status = TdiHandleSerializedRequest( + BindingHandle, + TDI_DEREGISTER_BIND_NOTIFY + ); + + return Status; + +} + +NTSTATUS +TdiRegisterDeviceObject( + IN PUNICODE_STRING DeviceName, + OUT HANDLE *RegistrationHandle +) + +/*++ + +Routine Description: + + Called when a TDI provider wants to register a device object. + +Arguments: + + DeviceName - Name of the device to be registered. + + RegistrationHandle - A handle we pass back to the provider, + identifying this registration. + +Return Value: + + The status of the attempt to register the provider. + +--*/ + + +{ + PTDI_PROVIDER_RESOURCE NewResource; + NTSTATUS Status; + PWCHAR Buffer; + + TdiInitialize(); + + // First, try and allocate the needed resource. + + NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePool( + NonPagedPool, + sizeof(TDI_PROVIDER_RESOURCE) + ); + + // If we couldn't get it, fail the request. + if (NewResource == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // Try and get a buffer to hold the name. + + Buffer = (PWCHAR)ExAllocatePool( + NonPagedPool, + DeviceName->MaximumLength + ); + + if (Buffer == NULL) { + ExFreePool(NewResource); + return STATUS_INSUFFICIENT_RESOURCES; + } + // Fill in the basic stuff. + NewResource->Common.Type = TDI_RESOURCE_DEVICE; + NewResource->Specific.Device.DeviceName.MaximumLength = + DeviceName->MaximumLength; + + NewResource->Specific.Device.DeviceName.Buffer = Buffer; + + RtlCopyUnicodeString( + &NewResource->Specific.Device.DeviceName, + DeviceName + ); + + *RegistrationHandle = (HANDLE)NewResource; + + + // Now call HandleBindRequest to handle this one. + + Status = TdiHandleSerializedRequest( + NewResource, + TDI_REGISTER_DEVICE + ); + + if (Status != STATUS_SUCCESS) { + ExFreePool(Buffer); + ExFreePool(NewResource); + } + + return Status; + + +} + +NTSTATUS +TdiDeregisterDeviceObject( + IN HANDLE RegistrationHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI provider want's to deregister + a device object. + +Arguments: + + RegistrationHandle - A handle we passed back to the provider + on the register call. This is really + a pointer to the resource element. + +Return Value: + + The status of the attempt to deregister the provider. + +--*/ + +{ + NTSTATUS Status; + + Status = TdiHandleSerializedRequest( + RegistrationHandle, + TDI_DEREGISTER_DEVICE + ); + + return Status; + +} + +NTSTATUS +TdiRegisterAddressChangeHandler( + IN TDI_ADD_ADDRESS_HANDLER AddHandler, + IN TDI_DEL_ADDRESS_HANDLER DeleteHandler, + OUT HANDLE *BindingHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI client wants to register for + notification of the arrival of network addresses. We allocate a + TDI_NOTIFY_ELEMENT for the provider and then call the serialized + worker routine to do the real work. + +Arguments: + + AddHandler - A pointer to the routine to be called when + a new address arrives. + DeleteHandler - A pointer to the routine to be called when an + address leaves. + BindingHandle - A handle we pass back that identifies this + client to us. + +Return Value: + + The status of the attempt to register the client. + +--*/ + + +{ + PTDI_NOTIFY_ELEMENT NewElement; + NTSTATUS Status; + + + // First, try and allocate the needed resource. + + NewElement = (PTDI_NOTIFY_ELEMENT)ExAllocatePool( + NonPagedPool, + sizeof(TDI_NOTIFY_ELEMENT) + ); + + // If we couldn't get it, fail the request. + if (NewElement == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Fill in the basic stuff. + NewElement->Common.Type = TDI_NOTIFY_NET_ADDRESS; + NewElement->Specific.AddressElement.AddHandler = AddHandler; + NewElement->Specific.AddressElement.DeleteHandler = DeleteHandler; + + *BindingHandle = (HANDLE)NewElement; + + + // Now call HandleBindRequest to handle this one. + + Status = TdiHandleSerializedRequest( + NewElement, + TDI_REGISTER_ADDRESS_NOTIFY + ); + + if (Status != STATUS_SUCCESS) { + ExFreePool(NewElement); + } + + return Status; + + +} + +NTSTATUS +TdiDeregisterAddressChangeHandler( + IN HANDLE BindingHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI client wants to deregister a + previously registered address change notification handler. All we + really do is call TdiHandleSerializedRequest, which does the hard work. + +Arguments: + + BindingHandle - A handle we passed back to the client + on the register call. This is really + a pointer to the notify element. + +Return Value: + + The status of the attempt to deregister the client. + +--*/ + +{ + NTSTATUS Status; + + Status = TdiHandleSerializedRequest( + BindingHandle, + TDI_DEREGISTER_ADDRESS_NOTIFY + ); + + return Status; + +} + +NTSTATUS +TdiRegisterNetAddress( + IN PTA_ADDRESS Address, + OUT HANDLE *RegistrationHandle +) + +/*++ + +Routine Description: + + Called when a TDI provider wants to register a new net address. + +Arguments: + + Address - New net address to be registered. + + RegistrationHandle - A handle we pass back to the provider, + identifying this registration. + +Return Value: + + The status of the attempt to register the provider. + +--*/ + + +{ + PTDI_PROVIDER_RESOURCE NewResource; + NTSTATUS Status; + + // First, try and allocate the needed resource. + + NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePool( + NonPagedPool, + FIELD_OFFSET( + TDI_PROVIDER_RESOURCE, + Specific.NetAddress + ) + + FIELD_OFFSET(TA_ADDRESS, Address) + + Address->AddressLength + ); + + // If we couldn't get it, fail the request. + if (NewResource == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Fill in the basic stuff. + NewResource->Common.Type = TDI_RESOURCE_DEVICE; + NewResource->Specific.NetAddress.Address.AddressLength = + Address->AddressLength; + + NewResource->Specific.NetAddress.Address.AddressType = + Address->AddressType; + + RtlCopyMemory( + NewResource->Specific.NetAddress.Address.Address, + Address->Address, + Address->AddressLength + ); + + *RegistrationHandle = (HANDLE)NewResource; + + + // Now call HandleBindRequest to handle this one. + + Status = TdiHandleSerializedRequest( + NewResource, + TDI_REGISTER_ADDRESS + ); + + if (Status != STATUS_SUCCESS) { + ExFreePool(NewResource); + } + + return Status; + + +} + +NTSTATUS +TdiDeregisterNetAddress( + IN HANDLE RegistrationHandle +) + +/*++ + +Routine Description: + + This function is called when a TDI provider wants to deregister + a net addres. + +Arguments: + + RegistrationHandle - A handle we passed back to the provider + on the register call. This is really + a pointer to the resource element. + +Return Value: + + The status of the attempt to deregister the provider. + +--*/ + +{ + NTSTATUS Status; + + Status = TdiHandleSerializedRequest( + RegistrationHandle, + TDI_DEREGISTER_ADDRESS + ); + + return Status; + +} + diff --git a/private/ntos/tdi/wrapper/tdipnp.h b/private/ntos/tdi/wrapper/tdipnp.h new file mode 100644 index 000000000..ae98e7d02 --- /dev/null +++ b/private/ntos/tdi/wrapper/tdipnp.h @@ -0,0 +1,163 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + tdipnp.h + +Abstract: + + This module contains the definitions for the PnP related code + in the TDI driver. + +Author: + + Henry Sanders (henrysa) 11 Oct 1995 + +Environment: + + Kernel mode + +Revision History: + + + +--*/ + +#ifndef _TDIPNP_ +#define _TDIPNP_ + +// Define the types possible for a TDI_NOTIFY_ELEMENT structure. + +#define TDI_NOTIFY_DEVICE 0 +#define TDI_NOTIFY_NET_ADDRESS 1 + + +// And the types possible for a TDI_PROVIDER_RESOURCE structure. + +#define TDI_RESOURCE_DEVICE 0 +#define TDI_RESOURCE_NET_ADDRESS 1 + +// +// Define the types of bind requests possible. + +#define TDI_REGISTER_BIND_NOTIFY 0 +#define TDI_DEREGISTER_BIND_NOTIFY 1 +#define TDI_REGISTER_DEVICE 2 +#define TDI_DEREGISTER_DEVICE 3 +#define TDI_REGISTER_ADDRESS_NOTIFY 4 +#define TDI_DEREGISTER_ADDRESS_NOTIFY 5 +#define TDI_REGISTER_ADDRESS 6 +#define TDI_DEREGISTER_ADDRESS 7 + +#define TDI_MAX_BIND_REQUEST TDI_DEREGISTER_DEVICE + +// +// This is the definition of the common part of a TDI_NOTIFY_ELEMENT structure. +// + +typedef struct _TDI_NOTIFY_COMMON { + LIST_ENTRY Linkage; + UCHAR Type; +} TDI_NOTIFY_COMMON, *PTDI_NOTIFY_COMMON; + +// +// The definition of the TDI_NOTIFY_BIND structure. +// + +typedef struct _TDI_NOTIFY_BIND { + TDI_BIND_HANDLER BindHandler; + TDI_UNBIND_HANDLER UnbindHandler; +} TDI_NOTIFY_BIND, *PTDI_NOTIFY_BIND; + +// +// The definition of a TDI_NOTIFY_ADDRESS structure, +// +typedef struct _TDI_NOTIFY_ADDRESS { + TDI_ADD_ADDRESS_HANDLER AddHandler; + TDI_DEL_ADDRESS_HANDLER DeleteHandler; +} TDI_NOTIFY_ADDRESS, *PTDI_NOTIFY_ADDRESS; + + +// +// This is the definition of a TDI_NOTIFY_ELEMENT stucture. +// + +typedef struct _TDI_NOTIFY_ELEMENT { + TDI_NOTIFY_COMMON Common; + union { + TDI_NOTIFY_BIND BindElement; + TDI_NOTIFY_ADDRESS AddressElement; + } Specific; +} TDI_NOTIFY_ELEMENT, *PTDI_NOTIFY_ELEMENT; + + +// +// This is the definition of the common part of a TDI_PROVIDER_RESOURCE structure. +// + +typedef struct _TDI_PROVIDER_COMMON { + LIST_ENTRY Linkage; + UCHAR Type; +} TDI_PROVIDER_COMMON, *PTDI_PROVIDER_COMMON; + +// +// The definition of the TDI_PROVIDER_DEVICE structure. +// + +typedef struct _TDI_PROVIDER_DEVICE { + UNICODE_STRING DeviceName; +} TDI_PROVIDER_DEVICE, *PTDI_PROVIDER_DEVICE; + +// +// The definition of the TDI_PROVIDER_NET_ADDRESS structure. +// + +typedef struct _TDI_PROVIDER_NET_ADDRESS { + TA_ADDRESS Address; +} TDI_PROVIDER_NET_ADDRESS, *PTDI_PROVIDER_NET_ADDRESS; + +// +// This is the definition of a TDI_PROVIDER_RESOURCE stucture. +// + +typedef struct _TDI_PROVIDER_RESOURCE { + TDI_PROVIDER_COMMON Common; + union { + TDI_PROVIDER_DEVICE Device; + TDI_PROVIDER_NET_ADDRESS NetAddress; + } Specific; +} TDI_PROVIDER_RESOURCE, *PTDI_PROVIDER_RESOURCE; + +// +// Structure of a bind list request. +// + +typedef struct _TDI_SERIALIZED_REQUEST { + LIST_ENTRY Linkage; + PVOID Element; + UINT Type; + PKEVENT Event; + +} TDI_SERIALIZED_REQUEST, *PTDI_SERIALIZED_REQUEST; + + +// External defintions for global variables. + +extern KSPIN_LOCK TDIListLock; + +extern LIST_ENTRY BindClientList; + +extern LIST_ENTRY NetAddressClientList; + +extern LIST_ENTRY BindProviderList; + +extern LIST_ENTRY NetAddressProviderList; + +extern LIST_ENTRY BindRequestList; + +extern LIST_ENTRY NetAddressRequestList; + + +#endif // _TDIPNP -- cgit v1.2.3